import Roark from '@roarkanalytics/sdk';
const client = new Roark({
bearerToken: process.env['ROARK_API_BEARER_TOKEN'], // This is the default and can be omitted
});
const response = await client.customerFlow.duplicate('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');
console.log(response.data);
import os
from roark_analytics import Roark
client = Roark(
bearer_token=os.environ.get("ROARK_API_BEARER_TOKEN"), # This is the default and can be omitted
)
response = client.customer_flow.duplicate(
flow_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
print(response.data)
{
"type": "internal",
"code": "internal_error",
"message": "Internal server error"
}
Customer Flow
Duplicate a customer flow
Deep-copies a flow into a new project-owned flow. The copy carries the source’s description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.
POST
/
v1
/
customer-flow
/
{flowId}
/
duplicate
JavaScript
import Roark from '@roarkanalytics/sdk';
const client = new Roark({
bearerToken: process.env['ROARK_API_BEARER_TOKEN'], // This is the default and can be omitted
});
const response = await client.customerFlow.duplicate('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');
console.log(response.data);
import os
from roark_analytics import Roark
client = Roark(
bearer_token=os.environ.get("ROARK_API_BEARER_TOKEN"), # This is the default and can be omitted
)
response = client.customer_flow.duplicate(
flow_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
print(response.data)
The conversation a simulated customer has with the agent under test.
Scripted
Improv
Voicemail
Show child attributes
{"docsConfig":{"theme":"aspen","$schema":"https://mintlify.com/docs.json","name":"Roark Docs","description":"Roark is the quality platform for voice and chat AI. Simulate every scenario before launch, monitor every production call, and score them with audio-native metrics.","colors":{"primary":"#006838","light":"#4faf62","dark":"#005530"},"logo":{"light":"https://mintcdn.com/roark/roh5qorg6REL9dkn/images/logo/light.svg?fit=max\u0026auto=format\u0026n=roh5qorg6REL9dkn\u0026q=85\u0026s=040c5cac74f0bd94ad25a3ccfdb45dbc","dark":"https://mintcdn.com/roark/85D-0i58gn7fPi1w/images/logo/dark.svg?fit=max\u0026auto=format\u0026n=85D-0i58gn7fPi1w\u0026q=85\u0026s=d666f2dc1f3dc1b8320c0bef62dca06b","href":"https://roark.ai"},"favicon":"https://mintcdn.com/roark/roh5qorg6REL9dkn/favicon.svg?fit=max\u0026auto=format\u0026n=roh5qorg6REL9dkn\u0026q=85\u0026s=95d524da66f21b9f4532ec4f6162efc5","appearance":{"default":"system"},"background":{"color":{"light":"#f0eeeb","dark":"#0e0d0c"}},"navbar":{"primary":{"type":"button","label":"Get Started","href":"https://roark.ai/signup"}},"footer":{"socials":{"x":"https://x.com/roarkhq","github":"https://github.com/roarkhq","linkedin":"https://www.linkedin.com/company/roark-innovations"}},"fonts":{"heading":{"family":"Bricolage Grotesque","weight":600},"body":{"family":"Hanken Grotesk"}},"icons":{"library":"lucide"},"styling":{"eyebrows":"section","codeblocks":{"theme":{"light":"everforest-light","dark":"everforest-dark"}},"latex":false},"contextual":{"options":["copy","view","chatgpt","claude","cursor","mcp"],"display":"header"},"navigation":{"global":{},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"roark","hasAudioNarration":false,"actualSubdomain":"roark","customDomains":["docs.roark.ai"],"gitSource":{"type":"github","owner":"roarkhq","repo":"docs-roark-analytics","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6adb2ee87ebcfcb7ce778643","collectionId":"6858a2bb-aa4d-4fe8-84f0-85db8fc0b3ce","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6adb2ee87ebcfcb7ce778643"},"entitlements":{},"buildId":"6adb2ee87ebcfcb7ce778643","clientVersion":"0.0.3964","searchFilterCounts":[{"language":"default","tag":"API Reference","count":182},{"language":"default","tag":"CLI Reference","count":147},{"language":"default","tag":"Documentation","count":95},{"language":"default","tag":"simulation","count":42},{"language":"default","tag":"Simulations","count":33},{"language":"default","tag":"metric","count":19},{"language":"default","tag":"job","count":18},{"language":"default","tag":"Templates","count":18},{"language":"default","tag":"Simulation","count":17},{"language":"default","tag":"agent","count":12}]},"assistantConfig":{},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":true},"customCss":[{"filePath":"style.css","content":"/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 500;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n/* cyrillic-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrodmgPn.woff2) format('woff2');\n unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n/* cyrillic */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrMdmgPn.woff2) format('woff2');\n unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;\n}\n/* symbols2 */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFg08vz7ehw.woff2) format('woff2');\n unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;\n}\n/* vietnamese */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrgdmgPn.woff2) format('woff2');\n unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;\n}\n/* latin-ext */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrkdmgPn.woff2) format('woff2');\n unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;\n}\n/* latin */\n@font-face {\n font-family: 'Geist Mono';\n font-style: normal;\n font-weight: 600;\n font-display: swap;\n src: url(/_mintlify/assets/fonts/google/s/geistmono/v6/or3nQ6H-1_WfwkMZI_qYFrcdmg.woff2) format('woff2');\n unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n:root{--roark-accent:#006838;--roark-accent-hover:#005530;--roark-accent-subtle:rgba(0, 104, 56, 0.08);--roark-shell:#f0eeeb;--roark-panel:#ffffff;--roark-border:#e4e0dc;--roark-radius-card:14px;--roark-ease:cubic-bezier(0.25, 0.1, 0.25, 1)}.dark{--roark-accent:#4faf62;--roark-accent-hover:#5ec070;--roark-accent-subtle:rgba(79, 175, 98, 0.1);--roark-shell:#0e0d0c;--roark-panel:#1a1918;--roark-border:#2e2c2a}code,kbd,pre,pre *,samp{font-family:'Geist Mono',ui-monospace,'SF Mono',SFMono-Regular,Menlo,monospace}h1,h2,h3,h4{font-feature-settings:'ss01' on;letter-spacing:-.02em}#content-area h1{font-size:clamp(2.1rem,3.4vw,2.6rem);line-height:1.12}body{font-feature-settings:'ss01' on,'cv01' on;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}::selection{background:#006838;color:#f0eeeb}.dark ::selection{background:#4faf62;color:#0e0d0c}#content-area a.link{color:var(--roark-accent);text-decoration-color:var(--roark-accent-subtle);text-underline-offset:3px;transition:color .15s var(--roark-ease)}#content-area a.link:hover{color:var(--roark-accent-hover);text-decoration-color:currentColor}.sidebar-group-header,[class*=eyebrow]{font-family:'Geist Mono',ui-monospace,monospace;font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.08em}.sidebar-group-header{font-size:.6875rem;color:#716f6c}.dark .sidebar-group-header{color:#8a8785}#content-area iframe,#content-area img,#content-area video,.card{border-radius:var(--roark-radius-card)}.dark .card{background-color:#1a1918;border-color:rgba(255,255,255,.07)}.card [data-component-part=card-icon]{width:2.75rem;height:2.75rem;display:flex;align-items:center;justify-content:center;background:var(--roark-accent-subtle);border-radius:12px}[data-component-part=step-number]>div{font-family:'Geist Mono',ui-monospace,monospace;background:var(--roark-accent-subtle);color:var(--roark-accent)}#content-area hr{margin-top:2.5rem;margin-bottom:2.5rem;border-color:var(--roark-border)}.prose h2{margin-top:2.75rem}#sidebar{border-right:1px solid var(--roark-border)}img.nav-logo{height:1.75rem}#content-area img{border:1px solid rgba(0,0,0,.06);box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(0,0,0,.03)}.dark #content-area img{border-color:rgba(255,255,255,.06);box-shadow:0 1px 3px rgba(0,0,0,.3),0 8px 24px rgba(0,0,0,.2)}#content-area img.nav-logo{border:0;box-shadow:none;border-radius:0}::-webkit-scrollbar{width:10px;height:10px}.dark ::-webkit-scrollbar-thumb,::-webkit-scrollbar-thumb{border-radius:5px;border:2px solid transparent;background-clip:content-box}::-webkit-scrollbar-thumb{background:#d6d2cc}.dark ::-webkit-scrollbar-thumb{background:#3a3835}::-webkit-scrollbar-track{background:0 0}a,button{transition-timing-function:var(--roark-ease)}"}],"page":{"pagePath":"api-reference/customer-flow/duplicate-a-customer-flow","slug":"/api-reference/customer-flow/duplicate-a-customer-flow","pageMetadata":{"title":"Duplicate a customer flow","description":"Deep-copies a flow into a new project-owned flow. The copy carries the source's description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow/{flowId}/duplicate","href":"/api-reference/customer-flow/duplicate-a-customer-flow","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Deep-copies a flow into a new project-owned flow. The copy carries the source’s description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"codeStyling":{"theme":{"light":"everforest-light","dark":"everforest-dark"}},"config":{},"pageMetadata":{"title":"Duplicate a customer flow","description":"Deep-copies a flow into a new project-owned flow. The copy carries the source's description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow/{flowId}/duplicate","href":"/api-reference/customer-flow/duplicate-a-customer-flow","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"responses":{"201":"89acf326-934b-4fab-9bac-b6e818c27689","400":"b9926c15-c68b-45e5-b103-de1ba285fdc9","401":"8935c32e-ea4f-4be7-89d5-1996b12800a3","403":"16d6e91b-2709-4a0c-9cf8-78b8ed658ed2","404":"3d08fc75-2e05-4f74-9ffd-d46d01ac9c5e","429":"30d2ed7c-1c98-421d-a5d5-b5a8eed3cb40","500":"2d7045d2-17f0-42dc-b802-c9282ff0a972"},"operationId":"postV1Customer-flowByFlowIdDuplicate","tags":["Customer Flow"],"summary":"Duplicate a customer flow","description":"Deep-copies a flow into a new project-owned flow. The copy carries the source's description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.","parameters":["328c72dc-2bc7-4c72-a502-c76a4f8ded4d"],"requestBody":"096b3c10-3449-404b-8160-a8cea74446d7","x-codeSamples":[{"lang":"JavaScript","source":"import Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark({\n bearerToken: process.env['ROARK_API_BEARER_TOKEN'], // This is the default and can be omitted\n});\n\nconst response = await client.customerFlow.duplicate('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');\n\nconsole.log(response.data);"},{"lang":"Python","source":"import os\nfrom roark_analytics import Roark\n\nclient = Roark(\n bearer_token=os.environ.get(\"ROARK_API_BEARER_TOKEN\"), # This is the default and can be omitted\n)\nresponse = client.customer_flow.duplicate(\n flow_id=\"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e\",\n)\nprint(response.data)"}],"method":"post","type":"path","servers":["50012bb9-e427-4f27-bd86-c0f8b4396d0e"],"security":["54c4a1d9-f26a-47d1-b5af-f38e3f3566a6"],"path":"/v1/customer-flow/{flowId}/duplicate","baseUrlOptions":["https://api.roark.ai"],"title":"Duplicate a customer flow","codeSamples":[{"lang":"JavaScript","source":"import Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark({\n bearerToken: process.env['ROARK_API_BEARER_TOKEN'], // This is the default and can be omitted\n});\n\nconst response = await client.customerFlow.duplicate('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');\n\nconsole.log(response.data);"},{"lang":"Python","source":"import os\nfrom roark_analytics import Roark\n\nclient = Roark(\n bearer_token=os.environ.get(\"ROARK_API_BEARER_TOKEN\"), # This is the default and can be omitted\n)\nresponse = client.customer_flow.duplicate(\n flow_id=\"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e\",\n)\nprint(response.data)"}],"requiredOnlyExamples":false,"requestExampleType":"codeSamples","responseExampleType":"generated"},"dependencies":{"parameters":{"328c72dc-2bc7-4c72-a502-c76a4f8ded4d":{"in":"path","name":"flowId","schema":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"","typeLabel":"string\u003cuuid>"},"required":true}},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"description":"Title for the copy. Defaults to \"Copy of\" the source flow.","example":"Reschedule an appointment (v2)","uniqueKey":"title","typeLabel":"string"},"agentIds":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"agentIds.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Agents to link on the copy. Omit to carry the source flow's agents over. Required when the source is a Roark-managed flow with no agents of its own.","uniqueKey":"agentIds","typeLabel":"string\u003cuuid>[]"}},"description":"Options for duplicating a customer flow. Every field is optional.","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"title":"Reschedule an appointment (v2)","agentIds":["3c90c3cc-0d44-4b50-8888-8dd25736052a"]}}},"exampleType":"generated"}},"exampleType":"generated"},"responses":{"89acf326-934b-4fab-9bac-b6e818c27689":{"description":"The duplicated customer flow","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","title":"Scripted","description":"The conversation a simulated customer has with the agent under test.","required":["id","title","source","createdAt","updatedAt","agents","agentExpectations","type","scriptAdherence","offScriptPolicy","branchingMode","happyPath","edgeCases"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.0.title","typeLabel":"string"},"source":{"type":"string","enum":["SYSTEM","CUSTOM"],"default":"CUSTOM","isRequired":true,"uniqueKey":"data.oneOf.0.source","typeLabel":"enum\u003cstring>"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.updatedAt","typeLabel":"string"},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"Name of the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].name","typeLabel":"string"},"description":{"oneOf":[{"type":"string","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].description","typeLabel":"string | null"},"customId":{"oneOf":[{"type":"string","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].customId.oneOf.0","typeLabel":"string"},{"type":"null","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].customId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].customId","typeLabel":"string | null"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.agents.items.[INDEX].updatedAt","typeLabel":"string"}},"required":["id","name","description","customId","createdAt","updatedAt"],"uniqueKey":"data.oneOf.0.agents.items.[INDEX]","typeLabel":"object"},"description":"The agents this flow is run against.","isRequired":true,"uniqueKey":"data.oneOf.0.agents","typeLabel":"object[]"},"agentExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.agentExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.0.agentExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.0.agentExpectations.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"data.oneOf.0.agentExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"SCRIPTED","isRequired":true,"uniqueKey":"data.oneOf.0.type","typeLabel":"string"},"scriptAdherence":{"type":"string","enum":["LOOSE","STRICT"],"description":"How closely a run follows the script. LOOSE (default) hands the whole script to the simulated customer as one prompt; it keeps the call moving whatever your agent says. STRICT runs the script as a state machine on the agent service: at every agent step the simulated customer waits, silent, until your agent has said the expected line, and only then moves on. Scripted flows only; STRICT needs the agent-service transport and is not available on realtime models.","isRequired":true,"uniqueKey":"data.oneOf.0.scriptAdherence","typeLabel":"enum\u003cstring>"},"offScriptPolicy":{"oneOf":[{"type":"object","description":"STRICT only. What the simulated customer does when your agent does not say the expected line. Each unmatched agent utterance is an attempt: `reaction` runs per attempt (STAY_SILENT, REPEAT its last scripted line, RESPOND once in character without moving on, or SAY `sayLine`), and `then` runs when attempts reach `maxAttempts` or your agent stays silent for `waitSeconds` (HANG_UP ends the call with ended reason SCRIPT_DIVERGED, HANG_UP_INVALIDATE ends it the same way and invalidates the run so it is scored by nothing and counted nowhere, MOVE_ON advances anyway, ADAPT hands the rest of the call to loose behaviour). Null: stay silent, 3 attempts, hang up. The default for every agent step; an AGENT_TURN step can carry its own.","required":["reaction","maxAttempts","then"],"properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT","isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.reaction","typeLabel":"enum\u003cstring>"},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3,"isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.maxAttempts","typeLabel":"integer"},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP","isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.then","typeLabel":"enum\u003cstring>"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.0","typeLabel":"string"},{"type":"null","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.sayLine","typeLabel":"string | null"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0.waitSeconds","typeLabel":"integer | null"}},"isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.0","typeLabel":"object"},{"type":"null","isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.offScriptPolicy","typeLabel":"object | null"},"branchingMode":{"type":"string","enum":["DETERMINISTIC","ADAPTIVE"],"description":"How a run walks the graph. DETERMINISTIC (\"Simulate every path\" in the app) places one call per variant, each following its path exactly whatever the agent says. ADAPTIVE (\"Adapt to your agent\") collapses the paths into one call PER PERSONA, on which the simulated customer picks a branch from what the agent actually said. Both modes speak the exact authored lines, and neither changes how metrics or expectations grade.","isRequired":true,"uniqueKey":"data.oneOf.0.branchingMode","typeLabel":"enum\u003cstring>"},"happyPath":{"oneOf":[{"type":"object","title":"Scripted","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type","steps"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"SCRIPTED","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.type","typeLabel":"string"},"steps":{"type":"array","items":{"oneOf":[{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.content","typeLabel":"string | null"},"offScriptPolicy":{"oneOf":[{"type":"object","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.reaction","typeLabel":"enum\u003cstring>"},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.maxAttempts","typeLabel":"integer"},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.then","typeLabel":"enum\u003cstring>"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.0","typeLabel":"string"},{"type":"null","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine","typeLabel":"string | null"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds","typeLabel":"integer | null"}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000},{"type":"null","minLength":1,"maxLength":2000}]},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600},{"type":"null","minimum":1,"maximum":600}]}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0.offScriptPolicy","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.1","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_FIRST_MESSAGE","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.2","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_VERBATIM_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.3","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_SILENCE","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.stage","typeLabel":"object | null"},"silenceDurationSeconds":{"oneOf":[{"type":"integer","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4.silenceDurationSeconds","typeLabel":"integer | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.4","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.5","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.6","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7.stage","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.7","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_HANDOFF","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.stage","typeLabel":"object | null"},"handoffPersonaId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8.handoffPersonaId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.8","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"SCENARIO_LINK","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.stage","typeLabel":"object | null"},"linkedCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"linkedCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX].oneOf.9","typeLabel":"object"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps.items.[INDEX]","typeLabel":"object"},"description":"The one path through the graph this variant runs, in order. Linear by construction, so these steps never nest.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.steps","typeLabel":"object[]"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.happyPath.oneOf.0.systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.happyPath.oneOf.0.systemKey","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.0","typeLabel":"Scripted · object"},{"type":"null","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","isRequired":true,"uniqueKey":"data.oneOf.0.happyPath.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.happyPath","typeLabel":"Scripted · object | null"},"edgeCases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"SCRIPTED","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].type","typeLabel":"string"},"steps":{"type":"array","items":{"oneOf":[{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.content","typeLabel":"string | null"},"offScriptPolicy":{"oneOf":[{"type":"object","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.reaction","typeLabel":"enum\u003cstring>"},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.maxAttempts","typeLabel":"integer"},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.then","typeLabel":"enum\u003cstring>"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.0","typeLabel":"string"},{"type":"null","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine","typeLabel":"string | null"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds","typeLabel":"integer | null"}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000},{"type":"null","minLength":1,"maxLength":2000}]},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600},{"type":"null","minimum":1,"maximum":600}]}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0.offScriptPolicy","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.1","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_FIRST_MESSAGE","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.2","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_VERBATIM_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.3","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_SILENCE","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.stage","typeLabel":"object | null"},"silenceDurationSeconds":{"oneOf":[{"type":"integer","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4.silenceDurationSeconds","typeLabel":"integer | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.4","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.5","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.6","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7.stage","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.7","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_HANDOFF","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.stage","typeLabel":"object | null"},"handoffPersonaId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8.handoffPersonaId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.8","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"SCENARIO_LINK","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.stage","typeLabel":"object | null"},"linkedCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"linkedCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX].oneOf.9","typeLabel":"object"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps.items.[INDEX]","typeLabel":"object"},"description":"The one path through the graph this variant runs, in order. Linear by construction, so these steps never nest.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].steps","typeLabel":"object[]"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX].systemKey","typeLabel":"string | null"}},"required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type","steps"],"title":"Scripted","description":"One path through a scripted flow. The path engine owns which paths exist, so editing the graph is what creates and removes these.","uniqueKey":"data.oneOf.0.edgeCases.items.[INDEX]","typeLabel":"Scripted · object"},"description":"Every other way of running this flow.","isRequired":true,"uniqueKey":"data.oneOf.0.edgeCases","typeLabel":"Scripted · object[]"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.description","typeLabel":"string | null"},"graph":{"type":"array","items":{"oneOf":[{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.content","typeLabel":"string | null"},"offScriptPolicy":{"oneOf":[{"type":"object","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.reaction","typeLabel":"enum\u003cstring>"},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.maxAttempts","typeLabel":"integer"},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.then","typeLabel":"enum\u003cstring>"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.0","typeLabel":"string"},{"type":"null","minLength":1,"maxLength":2000,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.sayLine","typeLabel":"string | null"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","minimum":1,"maximum":600,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0.waitSeconds","typeLabel":"integer | null"}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT"},"sayLine":{"oneOf":[{"type":"string","minLength":1,"maxLength":2000},{"type":"null","minLength":1,"maxLength":2000}]},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP"},"waitSeconds":{"oneOf":[{"type":"integer","minimum":1,"maximum":600},{"type":"null","minimum":1,"maximum":600}]}},"required":["reaction","maxAttempts","then"],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0.offScriptPolicy","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.1","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_FIRST_MESSAGE","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.2","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_VERBATIM_TURN","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.stage","typeLabel":"object | null"},"content":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.content.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.content.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3.content","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.3","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_SILENCE","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.stage","typeLabel":"object | null"},"silenceDurationSeconds":{"oneOf":[{"type":"integer","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.0","typeLabel":"integer"},{"type":"null","exclusiveMinimum":0,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.silenceDurationSeconds.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4.silenceDurationSeconds","typeLabel":"integer | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.4","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.5","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_DTMF","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.stage","typeLabel":"object | null"},"dtmfDigits":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.dtmfDigits.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.dtmfDigits.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6.dtmfDigits","typeLabel":"string | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.6","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7.stage","typeLabel":"object | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.7","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_HANDOFF","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.stage","typeLabel":"object | null"},"handoffPersonaId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.handoffPersonaId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8.handoffPersonaId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.8","typeLabel":"object"},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"SCENARIO_LINK","isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.type","typeLabel":"string"},"nodeId":{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.nodeId","typeLabel":"string\u003cuuid>"},"ref":{"type":"string","minLength":1,"maxLength":64,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.ref","typeLabel":"string"},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.steps","typeLabel":"array"},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.mergeIntoNodeIds.items.[INDEX]","typeLabel":"string"},"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.mergeIntoNodeIds","typeLabel":"string[]"},"stage":{"oneOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage.oneOf.0.name","typeLabel":"string"},"required":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage.oneOf.0.required","typeLabel":"boolean"},"analyze":{"type":"boolean","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage.oneOf.0.analyze","typeLabel":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.stage","typeLabel":"object | null"},"linkedCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"linkedCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9.linkedCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"}},"additionalProperties":false,"uniqueKey":"data.oneOf.0.graph.items.[INDEX].oneOf.9","typeLabel":"object"}],"uniqueKey":"data.oneOf.0.graph.items.[INDEX]","typeLabel":"object"},"description":"The conversation, as a graph of steps. Present on a single flow; omitted from the list, where reading it would mean walking the project step graph once per row.","uniqueKey":"data.oneOf.0.graph","typeLabel":"object[]"}},"isRequired":true,"uniqueKey":"data.oneOf.0","typeLabel":"Scripted · object"},{"type":"object","title":"Improv","description":"The conversation a simulated customer has with the agent under test.","required":["id","title","source","createdAt","updatedAt","agents","agentExpectations","type","happyPath","edgeCases"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.1.title","typeLabel":"string"},"source":{"type":"string","enum":["SYSTEM","CUSTOM"],"default":"CUSTOM","isRequired":true,"uniqueKey":"data.oneOf.1.source","typeLabel":"enum\u003cstring>"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.updatedAt","typeLabel":"string"},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"Name of the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].name","typeLabel":"string"},"description":{"oneOf":[{"type":"string","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].description","typeLabel":"string | null"},"customId":{"oneOf":[{"type":"string","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].customId.oneOf.0","typeLabel":"string"},{"type":"null","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].customId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].customId","typeLabel":"string | null"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.agents.items.[INDEX].updatedAt","typeLabel":"string"}},"required":["id","name","description","customId","createdAt","updatedAt"],"uniqueKey":"data.oneOf.1.agents.items.[INDEX]","typeLabel":"object"},"description":"The agents this flow is run against.","isRequired":true,"uniqueKey":"data.oneOf.1.agents","typeLabel":"object[]"},"agentExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.agentExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.1.agentExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.1.agentExpectations.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"data.oneOf.1.agentExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"IMPROV","isRequired":true,"uniqueKey":"data.oneOf.1.type","typeLabel":"string"},"happyPath":{"oneOf":[{"type":"object","title":"Improv","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type","handoffPersonas"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"IMPROV","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.type","typeLabel":"string"},"handoffPersonas":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX].phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas.items.[INDEX]","typeLabel":"object"},"description":"The personas this brief hands the phone to, in order of first mention. Empty when the brief inherits the happy path's: read the happy path's list.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.handoffPersonas","typeLabel":"object[]"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.systemKey","typeLabel":"string | null"},"prompt":{"oneOf":[{"type":"string","description":"The brief the simulated customer improvises from. A `@{persona:\u003cid>}` mention hands the phone to that persona mid-call; the sentences around the mention say when.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.prompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"The brief the simulated customer improvises from. A `@{persona:\u003cid>}` mention hands the phone to that persona mid-call; the sentences around the mention say when.","uniqueKey":"data.oneOf.1.happyPath.oneOf.0.prompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.happyPath.oneOf.0.prompt","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.0","typeLabel":"Improv · object"},{"type":"null","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","isRequired":true,"uniqueKey":"data.oneOf.1.happyPath.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.happyPath","typeLabel":"Improv · object | null"},"edgeCases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"IMPROV","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].type","typeLabel":"string"},"handoffPersonas":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX].phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas.items.[INDEX]","typeLabel":"object"},"description":"The personas this brief hands the phone to, in order of first mention. Empty when the brief inherits the happy path's: read the happy path's list.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].handoffPersonas","typeLabel":"object[]"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].systemKey","typeLabel":"string | null"},"prompt":{"oneOf":[{"type":"string","description":"The brief the simulated customer improvises from. A `@{persona:\u003cid>}` mention hands the phone to that persona mid-call; the sentences around the mention say when.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].prompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"The brief the simulated customer improvises from. A `@{persona:\u003cid>}` mention hands the phone to that persona mid-call; the sentences around the mention say when.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].prompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX].prompt","typeLabel":"string | null"}},"required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type","handoffPersonas"],"title":"Improv","description":"One brief to run an improv flow with.","uniqueKey":"data.oneOf.1.edgeCases.items.[INDEX]","typeLabel":"Improv · object"},"description":"Every other way of running this flow.","isRequired":true,"uniqueKey":"data.oneOf.1.edgeCases","typeLabel":"Improv · object[]"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.1.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.1.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.1.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.1","typeLabel":"Improv · object"},{"type":"object","title":"Voicemail","description":"The conversation a simulated customer has with the agent under test.","required":["id","title","source","createdAt","updatedAt","agents","agentExpectations","type","happyPath","edgeCases"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.2.title","typeLabel":"string"},"source":{"type":"string","enum":["SYSTEM","CUSTOM"],"default":"CUSTOM","isRequired":true,"uniqueKey":"data.oneOf.2.source","typeLabel":"enum\u003cstring>"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.updatedAt","typeLabel":"string"},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"Name of the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].name","typeLabel":"string"},"description":{"oneOf":[{"type":"string","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Description of the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].description","typeLabel":"string | null"},"customId":{"oneOf":[{"type":"string","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].customId.oneOf.0","typeLabel":"string"},{"type":"null","description":"Custom identifier for the agent","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].customId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].customId","typeLabel":"string | null"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.agents.items.[INDEX].updatedAt","typeLabel":"string"}},"required":["id","name","description","customId","createdAt","updatedAt"],"uniqueKey":"data.oneOf.2.agents.items.[INDEX]","typeLabel":"object"},"description":"The agents this flow is run against.","isRequired":true,"uniqueKey":"data.oneOf.2.agents","typeLabel":"object[]"},"agentExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.agentExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.2.agentExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.2.agentExpectations.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"data.oneOf.2.agentExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.2.type","typeLabel":"string"},"happyPath":{"oneOf":[{"type":"object","title":"Voicemail","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.type","typeLabel":"string"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.2.happyPath.oneOf.0.systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.happyPath.oneOf.0.systemKey","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.0","typeLabel":"Voicemail · object"},{"type":"null","description":"The way this flow is meant to go. Null when the flow has none, and then every way is an edge case.","isRequired":true,"uniqueKey":"data.oneOf.2.happyPath.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.happyPath","typeLabel":"Voicemail · object | null"},"edgeCases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"title":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].title","typeLabel":"string"},"precededByCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowId","typeLabel":"string\u003cuuid> | null"},"precededByCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowVariantId.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].precededByCustomerFlowVariantId","typeLabel":"string\u003cuuid> | null"},"isGenerated":{"type":"boolean","default":false,"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].isGenerated","typeLabel":"boolean"},"includeFlowExpectations":{"type":"boolean","description":"Whether this variant is graded on the flow's agentExpectations as well as its additionalExpectations. False grades it on its additionalExpectations only. Always true on the happy path.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].includeFlowExpectations","typeLabel":"boolean"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].updatedAt","typeLabel":"string"},"personaOverride":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","description":"The name the agent will identify as during conversations","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.name","typeLabel":"string"},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.language","typeLabel":"enum\u003cstring>"},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages.items.[INDEX]","typeLabel":"enum\u003cstring>"},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.understoodLanguages","typeLabel":"enum\u003cstring>[]"},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.accent","typeLabel":"enum\u003cstring>"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.age","typeLabel":"enum\u003cstring>"},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.gender","typeLabel":"enum\u003cstring>"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechPace","typeLabel":"enum\u003cstring>"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.speechClarity","typeLabel":"enum\u003cstring>"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\"","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.hasDisfluencies","typeLabel":"boolean"},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.baseEmotion","typeLabel":"enum\u003cstring>"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.intentClarity","typeLabel":"enum\u003cstring>"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.confirmationStyle","typeLabel":"enum\u003cstring>"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.memoryReliability","typeLabel":"enum\u003cstring>"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.responseTiming","typeLabel":"enum\u003cstring>"},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.interruption","typeLabel":"enum\u003cstring>"},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0.items.[INDEX]","typeLabel":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.0","typeLabel":"string[]"},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessages","typeLabel":"string[] | null"},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleTimeoutSeconds","typeLabel":"integer"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageMaxSpokenCount","typeLabel":"integer"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.idleMessageResetCountOnUserSpeechEnabled","typeLabel":"boolean"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties.additionalProperties","typeLabel":"any"},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.properties","typeLabel":"object"},"createdAt":{"type":"string","description":"Creation timestamp","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.updatedAt","typeLabel":"string"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.0","typeLabel":"string"},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself.","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.displayName","typeLabel":"string | null"},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","description":"Human-readable description of the persona","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.description","typeLabel":"string | null"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.0","typeLabel":"enum\u003cstring>"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.secondaryLanguage","typeLabel":"enum\u003cstring> | null"},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.0","typeLabel":"string"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.backstoryPrompt","typeLabel":"string | null"},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0.phoneNumber","typeLabel":"string"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.0","typeLabel":"object"},{"type":"null","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the persona"},"name":{"type":"string","description":"The name the agent will identify as during conversations"},"displayName":{"oneOf":[{"type":"string","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."},{"type":"null","description":"Label shown in place of the name across the dashboard (e.g. a short descriptor like \"Irate Escalator\"). The persona still identifies as `name` on calls. Omit or set null to display the name itself."}]},"description":{"oneOf":[{"type":"string","description":"Human-readable description of the persona"},{"type":"null","description":"Human-readable description of the persona"}]},"language":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"],"description":"Primary language ISO 639-1 code for the persona"},"secondaryLanguage":{"oneOf":[{"type":"string","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"},{"type":"null","enum":["EN"],"description":"Secondary language ISO 639-1 code for code-switching (e.g., Hinglish, Spanglish)"}]},"understoodLanguages":{"type":"array","items":{"type":"string","enum":["EN","ES","DE","HI","FR","NL","AR","EL","IT","ID","TH","JA","TL","MS","ZH","TR","PT","HE"]},"minItems":1,"description":"Languages the persona can understand. Multilingual combinations are limited by multilingual speech recognition support."},"accent":{"type":"string","enum":["US","US_X_SOUTH","GB","ES","DE","IN","FR","NL","SA","GR","AU","IT","ID","TH","JP","NZ","PH","SG","MY","HK","TR","PT","IL"],"description":"Accent of the persona, defined using ISO 3166-1 alpha-2 country codes with optional variants"},"age":{"type":"string","enum":["CHILD","TEENAGER","ADULT","ELDERLY"],"default":"ADULT","description":"How old the caller sounds and behaves. Only ages the persona's accent has a voice for are accepted; defaults to ADULT, which every accent supports."},"gender":{"type":"string","enum":["MALE","FEMALE"],"description":"Gender of the persona"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","description":"Background noise setting"},"speechPace":{"type":"string","enum":["SUPER_SLOW","SLOW","NORMAL","FAST","SUPER_FAST"],"default":"NORMAL","description":"Speech pace of the persona"},"speechClarity":{"type":"string","enum":["CLEAR","VAGUE","RAMBLING"],"default":"CLEAR","description":"Speech clarity of the persona"},"hasDisfluencies":{"type":"boolean","default":false,"description":"Whether the persona uses filler words like \"um\" and \"uh\""},"baseEmotion":{"type":"string","enum":["NEUTRAL","CHEERFUL","CONFUSED","FRUSTRATED","SKEPTICAL","RUSHED","DISTRACTED","ANGRY","ANXIOUS","SAD"],"default":"NEUTRAL","description":"Base emotional state of the persona"},"intentClarity":{"type":"string","enum":["CLEAR","INDIRECT","VAGUE"],"default":"CLEAR","description":"How clearly the persona expresses their intentions"},"confirmationStyle":{"type":"string","enum":["EXPLICIT","VAGUE"],"default":"EXPLICIT","description":"How the persona confirms information"},"memoryReliability":{"type":"string","enum":["HIGH","LOW"],"default":"HIGH","description":"How reliable the persona's memory is"},"responseTiming":{"type":"string","enum":["RELAXED","NORMAL","QUICK","BARGE_IN"],"default":"NORMAL","description":"Deprecated and inert: it no longer affects the call. It set how long the persona waited once the agent stopped talking, and measured across production simulations it moved the reply gap by less than the noise floor, because model and speech latency dominate it. Every persona now uses one voice-activity profile. Use `interruption` for a caller who talks over the agent. Still accepted and stored so existing clients keep working. BARGE_IN is stored as `responseTiming: QUICK` with `interruption: OCCASIONAL`."},"interruption":{"type":"string","enum":["OFF","BACKCHANNEL","OCCASIONAL","HEAVY"],"default":"OFF","description":"How much the persona talks over the agent while it is still speaking. OFF waits its turn. BACKCHANNEL makes listening noises (\"mm-hm\") over the agent without taking the floor, which tests whether the agent wrongly stops for them. OCCASIONAL adds cutting in on some long agent turns, HEAVY on most of them. Timing is randomised per turn, so two runs of the same persona do not interrupt at identical moments."},"backstoryPrompt":{"oneOf":[{"type":"string","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"},{"type":"null","description":"Background story and behavioral patterns for the persona","example":"A busy professional calling during lunch break"}]},"idleMessages":{"oneOf":[{"type":"array","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."},{"type":"null","items":{"type":"string"},"description":"Messages the persona will say when the agent goes silent during a call. null = \"Automatic\": language-appropriate defaults are used at call time."}]},"idleTimeoutSeconds":{"type":"integer","minimum":5,"maximum":60,"default":10,"description":"Seconds of silence before the persona sends an idle message"},"idleMessageMaxSpokenCount":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Maximum number of idle messages the persona will send before giving up"},"idleMessageResetCountOnUserSpeechEnabled":{"type":"boolean","default":true,"description":"Whether the idle message counter resets when the agent speaks"},"properties":{"type":"object","additionalProperties":{"oneOf":[{}]},"default":{},"description":"Additional custom properties about the persona","example":{"age":35,"zipCode":"94105","occupation":"Software Engineer"}},"phoneNumber":{"type":"string","description":"The E.164 number every call with this persona uses, when Roark has pinned one for your project. Present only when set; read-only.","example":"+19312830919"},"createdAt":{"type":"string","description":"Creation timestamp"},"updatedAt":{"type":"string","description":"Last update timestamp"}},"required":["id","name","language","understoodLanguages","accent","age","gender","backgroundNoise","speechPace","speechClarity","hasDisfluencies","baseEmotion","intentClarity","confirmationStyle","memoryReliability","responseTiming","interruption","idleMessages","idleTimeoutSeconds","idleMessageMaxSpokenCount","idleMessageResetCountOnUserSpeechEnabled","properties","createdAt","updatedAt"],"description":"The persona this runs as instead of the happy path's. Null means it inherits.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].personaOverride","typeLabel":"object | null"},"environment":{"oneOf":[{"type":"object","description":"The conditions this runs under. Null means it inherits the happy path's.","required":["id","name","backgroundNoise","backgroundNoiseVolume","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","minLength":1,"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.name","typeLabel":"string"},"backgroundNoise":{"type":"string","enum":["NONE","AIRPORT","CHILDREN_PLAYING","CITY","COFFEE_SHOP","CONSTRUCTION","CRYING_BABY","DRIVING","LIBRARY","OFFICE","THUNDERSTORM","TRAIN"],"default":"NONE","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoise","typeLabel":"enum\u003cstring>"},"backgroundNoiseVolume":{"type":"number","minimum":0,"maximum":1,"default":0.1,"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.backgroundNoiseVolume","typeLabel":"number"},"createdAt":{"type":"string","description":"Creation timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.createdAt","typeLabel":"string"},"updatedAt":{"type":"string","description":"Last update timestamp in ISO 8601 format","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.updatedAt","typeLabel":"string"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.0","typeLabel":"object"},{"type":"null","description":"The conditions this runs under. Null means it inherits the happy path's.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].environment","typeLabel":"object | null"},"additionalExpectations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"prompt":{"type":"string","description":"What the agent under test is graded against.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX].prompt","typeLabel":"string"}},"required":["id","prompt"],"description":"One thing the agent under test is graded against.","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].additionalExpectations.items.[INDEX]","typeLabel":"object"},"description":"Graded on top of the flow's own expectations, for this variant only.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].additionalExpectations","typeLabel":"object[]"},"type":{"type":"string","const":"VOICEMAIL","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].type","typeLabel":"string"},"systemKey":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].systemKey.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].systemKey.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX].systemKey","typeLabel":"string | null"}},"required":["id","title","precededByCustomerFlowId","precededByCustomerFlowVariantId","isGenerated","includeFlowExpectations","createdAt","updatedAt","personaOverride","environment","additionalExpectations","type"],"title":"Voicemail","description":"One voicemail greeting.","uniqueKey":"data.oneOf.2.edgeCases.items.[INDEX]","typeLabel":"Voicemail · object"},"description":"Every other way of running this flow.","isRequired":true,"uniqueKey":"data.oneOf.2.edgeCases","typeLabel":"Voicemail · object[]"},"description":{"oneOf":[{"type":"string","uniqueKey":"data.oneOf.2.description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"data.oneOf.2.description.oneOf.1","typeLabel":"null"}],"uniqueKey":"data.oneOf.2.description","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"data.oneOf.2","typeLabel":"Voicemail · object"}],"isRequired":true,"uniqueKey":"data","typeLabel":"Scripted · object | Improv · object | Voicemail · object"}},"required":["data"],"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"data":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","title":"\u003cstring>","source":"CUSTOM","createdAt":"\u003cstring>","updatedAt":"\u003cstring>","agents":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","description":"\u003cstring>","customId":"\u003cstring>","createdAt":"\u003cstring>","updatedAt":"\u003cstring>"}],"agentExpectations":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","prompt":"\u003cstring>"}],"type":"SCRIPTED","scriptAdherence":"LOOSE","offScriptPolicy":{"reaction":"STAY_SILENT","maxAttempts":3,"then":"HANG_UP","sayLine":"\u003cstring>","waitSeconds":300},"branchingMode":"DETERMINISTIC","happyPath":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","title":"\u003cstring>","precededByCustomerFlowId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","precededByCustomerFlowVariantId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","isGenerated":false,"includeFlowExpectations":true,"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","personaOverride":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","language":"EN","understoodLanguages":["EN"],"accent":"US","age":"ADULT","gender":"MALE","backgroundNoise":"NONE","speechPace":"NORMAL","speechClarity":"CLEAR","hasDisfluencies":false,"baseEmotion":"NEUTRAL","intentClarity":"CLEAR","confirmationStyle":"EXPLICIT","memoryReliability":"HIGH","responseTiming":"NORMAL","interruption":"OFF","idleMessages":["\u003cstring>"],"idleTimeoutSeconds":10,"idleMessageMaxSpokenCount":3,"idleMessageResetCountOnUserSpeechEnabled":true,"properties":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","displayName":"\u003cstring>","description":"\u003cstring>","secondaryLanguage":"EN","backstoryPrompt":"A busy professional calling during lunch break","phoneNumber":"+19312830919"},"environment":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","backgroundNoise":"NONE","backgroundNoiseVolume":0.1,"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","description":"\u003cstring>"},"additionalExpectations":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","prompt":"\u003cstring>"}],"type":"SCRIPTED","steps":[{"type":"AGENT_TURN","nodeId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","ref":"\u003cstring>","steps":"\u003carray>","mergeIntoNodeIds":["\u003cstring>"],"stage":{"name":"\u003cstring>","required":true,"analyze":true},"content":"\u003cstring>","offScriptPolicy":{"reaction":"STAY_SILENT","maxAttempts":3,"then":"HANG_UP","sayLine":"\u003cstring>","waitSeconds":300}}],"systemKey":"\u003cstring>"},"edgeCases":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","title":"\u003cstring>","precededByCustomerFlowId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","precededByCustomerFlowVariantId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","isGenerated":false,"includeFlowExpectations":true,"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","personaOverride":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","language":"EN","understoodLanguages":["EN"],"accent":"US","age":"ADULT","gender":"MALE","backgroundNoise":"NONE","speechPace":"NORMAL","speechClarity":"CLEAR","hasDisfluencies":false,"baseEmotion":"NEUTRAL","intentClarity":"CLEAR","confirmationStyle":"EXPLICIT","memoryReliability":"HIGH","responseTiming":"NORMAL","interruption":"OFF","idleMessages":["\u003cstring>"],"idleTimeoutSeconds":10,"idleMessageMaxSpokenCount":3,"idleMessageResetCountOnUserSpeechEnabled":true,"properties":{"age":35,"zipCode":"94105","occupation":"Software Engineer"},"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","displayName":"\u003cstring>","description":"\u003cstring>","secondaryLanguage":"EN","backstoryPrompt":"A busy professional calling during lunch break","phoneNumber":"+19312830919"},"environment":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","backgroundNoise":"NONE","backgroundNoiseVolume":0.1,"createdAt":"\u003cstring>","updatedAt":"\u003cstring>","description":"\u003cstring>"},"additionalExpectations":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","prompt":"\u003cstring>"}],"type":"SCRIPTED","steps":[{"type":"AGENT_TURN","nodeId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","ref":"\u003cstring>","steps":"\u003carray>","mergeIntoNodeIds":["\u003cstring>"],"stage":{"name":"\u003cstring>","required":true,"analyze":true},"content":"\u003cstring>","offScriptPolicy":{"reaction":"STAY_SILENT","maxAttempts":3,"then":"HANG_UP","sayLine":"\u003cstring>","waitSeconds":300}}],"systemKey":"\u003cstring>"}],"description":"\u003cstring>","graph":[{"type":"AGENT_TURN","nodeId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","ref":"\u003cstring>","steps":"\u003carray>","mergeIntoNodeIds":["\u003cstring>"],"stage":{"name":"\u003cstring>","required":true,"analyze":true},"content":"\u003cstring>","offScriptPolicy":{"reaction":"STAY_SILENT","maxAttempts":3,"then":"HANG_UP","sayLine":"\u003cstring>","waitSeconds":300}}]}}}},"exampleType":"generated"}},"exampleType":"generated"},"b9926c15-c68b-45e5-b103-de1ba285fdc9":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Validation error","uniqueKey":"","typeLabel":"object"},"example":{"type":"validation","code":"invalid_parameter","message":"The request was invalid","param":"email"},"examples":{"Example":{"title":"Example","value":{"type":"validation","code":"invalid_parameter","message":"The request was invalid","param":"email"}}},"exampleType":"examples"}},"description":"Bad Request","exampleType":"examples"},"8935c32e-ea4f-4be7-89d5-1996b12800a3":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Authentication error","uniqueKey":"","typeLabel":"object"},"example":{"type":"authentication","code":"unauthorized","message":"Authentication required"},"examples":{"Example":{"title":"Example","value":{"type":"authentication","code":"unauthorized","message":"Authentication required"}}},"exampleType":"examples"}},"description":"Unauthorized","exampleType":"examples"},"16d6e91b-2709-4a0c-9cf8-78b8ed658ed2":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Permission error","uniqueKey":"","typeLabel":"object"},"example":{"type":"forbidden","code":"permission_denied","message":"You do not have permission to access this resource"},"examples":{"Example":{"title":"Example","value":{"type":"forbidden","code":"permission_denied","message":"You do not have permission to access this resource"}}},"exampleType":"examples"}},"description":"Forbidden","exampleType":"examples"},"3d08fc75-2e05-4f74-9ffd-d46d01ac9c5e":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Not found error","uniqueKey":"","typeLabel":"object"},"example":{"type":"not_found","code":"resource_not_found","message":"The requested resource could not be found"},"examples":{"Example":{"title":"Example","value":{"type":"not_found","code":"resource_not_found","message":"The requested resource could not be found"}}},"exampleType":"examples"}},"description":"Not Found","exampleType":"examples"},"30d2ed7c-1c98-421d-a5d5-b5a8eed3cb40":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Rate limit error","uniqueKey":"","typeLabel":"object"},"example":{"type":"rate_limit","code":"too_many_requests","message":"Rate limit exceeded"},"examples":{"Example":{"title":"Example","value":{"type":"rate_limit","code":"too_many_requests","message":"Rate limit exceeded"}}},"exampleType":"examples"}},"description":"Too Many Requests","exampleType":"examples"},"2d7045d2-17f0-42dc-b802-c9282ff0a972":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["validation","authentication","forbidden","not_found","conflict","payment_required","rate_limit","internal"],"description":"The error type category","examples":["validation","authentication"],"isRequired":true,"uniqueKey":"type","typeLabel":"enum\u003cstring>"},"code":{"type":"string","description":"Machine-readable error code identifier","examples":["invalid_parameter","missing_required_field","unauthorized"],"isRequired":true,"uniqueKey":"code","typeLabel":"string"},"message":{"type":"string","description":"Human-readable error message","examples":["The request was invalid","Authentication required"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"param":{"type":"string","description":"The parameter that caused the error (if applicable)","examples":["email","user_id"],"uniqueKey":"param","typeLabel":"string"},"details":{"description":"Additional error context information","uniqueKey":"details","typeLabel":"any"}},"required":["type","code","message"],"description":"Server error","uniqueKey":"","typeLabel":"object"},"example":{"type":"internal","code":"internal_error","message":"Internal server error"},"examples":{"Example":{"title":"Example","value":{"type":"internal","code":"internal_error","message":"Internal server error"}}},"exampleType":"examples"}},"description":"Internal Server Error","exampleType":"examples"}},"security":{"54c4a1d9-f26a-47d1-b5af-f38e3f3566a6":{"Bearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","scopes":[],"schemeName":"Bearer"}}},"servers":{"50012bb9-e427-4f27-bd86-c0f8b4396d0e":{"description":"Production","url":"https://api.roark.ai"}},"schemas":{"11ac02ac-2709-40fa-b0c8-3f3609a51d0a":{"oneOf":[{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_TURN","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"content":{"oneOf":[{"type":"string"},{"type":"null"}]},"offScriptPolicy":{"oneOf":[{"type":"object","required":["reaction","maxAttempts","then"],"properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT","isRequired":true},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3,"isRequired":true},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP","isRequired":true},"sayLine":{"oneOf":[{"type":"string","maxLength":2000,"minLength":1},{"type":"null","maxLength":2000,"minLength":1}]},"waitSeconds":{"oneOf":[{"type":"integer","maximum":600,"minimum":1},{"type":"null","maximum":600,"minimum":1}]}}},{"type":"null","required":["reaction","maxAttempts","then"],"properties":{"reaction":{"type":"string","enum":["STAY_SILENT","REPEAT","RESPOND","SAY"],"default":"STAY_SILENT"},"sayLine":{"oneOf":[{"type":"string","maxLength":2000,"minLength":1},{"type":"null","maxLength":2000,"minLength":1}]},"maxAttempts":{"type":"integer","minimum":1,"maximum":20,"default":3},"then":{"type":"string","enum":["HANG_UP","MOVE_ON","ADAPT","HANG_UP_INVALIDATE"],"default":"HANG_UP"},"waitSeconds":{"oneOf":[{"type":"integer","maximum":600,"minimum":1},{"type":"null","maximum":600,"minimum":1}]}}}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_TURN","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"content":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_FIRST_MESSAGE","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"content":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_VERBATIM_TURN","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"content":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_SILENCE","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"silenceDurationSeconds":{"oneOf":[{"type":"integer","minimum":0,"exclusiveMinimum":true},{"type":"null","minimum":0,"exclusiveMinimum":true}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_DTMF","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"dtmfDigits":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"AGENT_DTMF","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"dtmfDigits":{"oneOf":[{"type":"string"},{"type":"null"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"VOICEMAIL","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"CUSTOMER_HANDOFF","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"handoffPersonaId":{"oneOf":[{"type":"string","format":"uuid"},{"type":"null","format":"uuid"}]}},"additionalProperties":false},{"type":"object","description":"One step in a scripted flow's conversation.\n\n`nodeId` is the identity contract: include it to update the existing step, omit it to create a new one.\nA step continues into `steps` (more than one child is a branch point) and/or `mergeIntoNodeIds`, which\nnames steps elsewhere in the same request that this step rejoins. Branches that come back together are\nrepresented that way rather than by repeating the shared step, so reading a flow, editing it and writing\nit back preserves it exactly.\n\nA merge target is named by its `nodeId` when it already exists, or by `ref` when it is being created in\nthe same request. `ref` is a label you choose, it is request-local, and it is never stored or returned.\nPut the shared step inline under the first branch that reaches it and point the others at it: a top-level\nstep is a root wired straight from the start of the flow, so a merge target parked there would also be\nreachable directly.\n\nA `CUSTOMER_TURN` describes what the simulated customer says and the persona phrases it; a\n`CUSTOMER_VERBATIM_TURN` is said word for word, and one placed as a top-level step opens the call\nthe moment it connects, before the agent speaks. `CUSTOMER_FIRST_MESSAGE` is the retired name for\nthat opening case: still accepted, stored and returned as `CUSTOMER_VERBATIM_TURN`.\n\nThe two DTMF types are mirror images and both require `dtmfDigits`. `CUSTOMER_DTMF` is keys the\nsimulated caller presses while navigating your agent. `AGENT_DTMF` is keys your agent under test is\nexpected to press while navigating a menu the simulation is playing, so its digits are an assertion the\nrun is graded against rather than an instruction, and it counts as an agent turn for role alternation.\n\nA `CUSTOMER_HANDOFF` hands the phone to another persona (`handoffPersonaId`), or back to the flow's\nown persona when it is null. The steps after it are spoken by whoever holds the phone. It sits between\nturns like a silence, so it may follow either side.\n\nIn a STRICT flow an `AGENT_TURN` may carry its own `offScriptPolicy`, which replaces the flow-level one at\nthat step. Omit it (or send null) to follow the flow's policy. Use it where one missed step makes the rest\nof the call meaningless: an authentication menu, say, with `then: HANG_UP_INVALIDATE`.\n\nAny step may set `stage`, the named phase of the flow it belongs to: the IVR, the login, the scenario\nitself. Steps in one stage are drawn as a group in the editor, and the name is yours: the reports say it\nback to you. The stage does not have to exist first, it is created the first time a step names it, and a\nname already in the project is matched ignoring case.\n\nA stage with `required: true` is a gate. A run that ends without getting PAST it never reached what was\nbeing measured, so it stays in the report, labelled with the stage it never completed and the one it\nstopped in, and is left out of the run score and the success-criteria verdict: an agent that never clears\nthe IVR does not fail every check in a scenario it never started. `required` belongs to the stage rather\nthan to the step, so a read returns it on every step in that stage, and a write that sets it on any one\nof them marks the whole stage. A flow that requires no stage is scored exactly as before.\n\nA stage with `analyze: false` is not measured. Its turns are kept out of the core call metrics (talk\ntime, speech pace, turn and response timings), which is what an IVR usually wants alongside\n`required: true`: the run has to get through the menu, but how the agent sounded reading it is not what\nis being tested. Coverage is being extended package by package, so the judged metrics (quality,\ncompliance, repetition, escalation, tone) still read the whole call for now. Nothing is cut and nothing\nis re-timed, so a turn at 4:10 is still at 4:10 with an earlier stage excluded, and rate metrics divide\nby the part of the call that was measured rather than by the whole of it. Defaults to true, belongs to\nthe stage the same way `required` does, and turning it off on any one step turns it off for the stage.","required":["type"],"properties":{"type":{"type":"string","const":"SCENARIO_LINK","isRequired":true},"nodeId":{"type":"string","format":"uuid"},"ref":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"array","items":{"$circularRef":"11ac02ac-2709-40fa-b0c8-3f3609a51d0a"}},"mergeIntoNodeIds":{"type":"array","items":{"type":"string","minLength":1}},"stage":{"oneOf":[{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"isRequired":true},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false},{"type":"null","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"required":{"type":"boolean"},"analyze":{"type":"boolean"}},"additionalProperties":false}]},"linkedCustomerFlowId":{"oneOf":[{"type":"string","format":"uuid"},{"type":"null","format":"uuid"}]},"linkedCustomerFlowVariantId":{"oneOf":[{"type":"string","format":"uuid"},{"type":"null","format":"uuid"}]}},"additionalProperties":false}]}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"JWT","scopes":[],"schemeName":"Bearer"},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Bearer authentication header of the form `Bearer \u003ctoken>`, where `\u003ctoken>` is your auth token.","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"Bearer","name":"Authorization"}],"title":"Bearer"}]},"originalFileLocation":"api-reference/openapi.documented.json"},"subdomain":"roark","siteName":"Roark Docs","title":"Duplicate a customer flow - Roark Docs","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-23T04:00:25.402Z","originalFileLocation":"api-reference/customer-flow/duplicate-a-customer-flow.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Deep-copies a flow into a new project-owned flow. The copy carries the source\u0026#x27;s description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Roark Docs\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Duplicate a customer flow - Roark Docs\">\u003cmeta data-mint-head property=\"og:description\" content=\"Deep-copies a flow into a new project-owned flow. The copy carries the source\u0026#x27;s description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Roark Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://roark.mintlify.app/_mintlify/api/og/v2?division=Customer+Flow\u0026amp;title=Duplicate+a+customer+flow\u0026amp;description=Deep-copies+a+flow+into+a+new+project-owned+flow.+The+copy+carries+the+source%27s+description%2C+branching+mode%2C+linked+agents%2C+flow-level+expectations+and+flow-own\u0026amp;theme=0214e8eaec612c77add8c6d0\u0026amp;signature=105b84f0deaabbe5690adbf3c1eddd9494eb8c33e2e9373e615d92ca101a4b74\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Duplicate a customer flow - Roark Docs\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Deep-copies a flow into a new project-owned flow. The copy carries the source\u0026#x27;s description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://roark.mintlify.app/_mintlify/api/og/v2?division=Customer+Flow\u0026amp;title=Duplicate+a+customer+flow\u0026amp;description=Deep-copies+a+flow+into+a+new+project-owned+flow.+The+copy+carries+the+source%27s+description%2C+branching+mode%2C+linked+agents%2C+flow-level+expectations+and+flow-own\u0026amp;theme=0214e8eaec612c77add8c6d0\u0026amp;signature=105b84f0deaabbe5690adbf3c1eddd9494eb8c33e2e9373e615d92ca101a4b74\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/roark/E3SiV_DMp7O0imMO/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Roark Docs\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#006838\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/customer-flow/duplicate-a-customer-flow.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.roark.ai/#organization\",\"name\":\"Roark Docs\",\"url\":\"https://docs.roark.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/roark/roh5qorg6REL9dkn/images/logo/light.svg?fit=max\u0026auto=format\u0026n=roh5qorg6REL9dkn\u0026q=85\u0026s=040c5cac74f0bd94ad25a3ccfdb45dbc\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.roark.ai#website\",\"name\":\"Roark Docs\",\"url\":\"https://docs.roark.ai\",\"publisher\":{\"@id\":\"https://docs.roark.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow#webpage\",\"url\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow\",\"name\":\"Duplicate a customer flow\",\"description\":\"Deep-copies a flow into a new project-owned flow. The copy carries the source's description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\",\"dateModified\":\"2026-10-23T04:00:25.402Z\",\"isPartOf\":{\"@id\":\"https://docs.roark.ai#website\"},\"breadcrumb\":{\"@id\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Customer Flow\",\"item\":\"https://docs.roark.ai/api-reference/customer-flow/list-customer-flows\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Duplicate a customer flow\",\"item\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow#apireference\",\"headline\":\"Duplicate a customer flow\",\"name\":\"Duplicate a customer flow\",\"description\":\"Deep-copies a flow into a new project-owned flow. The copy carries the source's description, branching mode, linked agents, flow-level expectations and flow-owned metrics. A scripted flow copies its whole step graph; an improv flow copies its variants (personas, briefs, expectations). Duplicating a Roark-managed flow is how you customise it. Voicemail flows are Roark-managed and cannot be duplicated.\",\"url\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow\",\"mainEntityOfPage\":{\"@id\":\"https://docs.roark.ai/api-reference/customer-flow/duplicate-a-customer-flow#webpage\"},\"dateModified\":\"2026-10-23T04:00:25.402Z\",\"publisher\":{\"@id\":\"https://docs.roark.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.roark.ai#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Roark Docs\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Roark Docs","nav":{"global":{},"tabs":[{"tab":"Documentation","icon":"book","pages":[{"title":"Documentation","href":"/documentation/getting-started/introduction"}]},{"tab":"API Reference","icon":"terminal","groups":[{"group":"API Reference","pages":[{"title":"Introduction","href":"/api-reference/introduction"},{"title":"Authorization","href":"/api-reference/authorization"}]},{"group":"Health","pages":[{"title":"Get API health status","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /health","href":"/api-reference/health/get-api-health-status"}]},{"group":"CLI Auth","pages":[{"title":"Exchange a CLI authorization code for an API key","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/cli/auth/token","href":"/api-reference/cli-auth/exchange-a-cli-authorization-code-for-an-api-key"}]},{"group":"Prospect Simulation","pages":[{"title":"Run a free simulation against a prospect agent","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/public/prospect-sim/run","href":"/api-reference/prospect-simulation/run-a-free-simulation-against-a-prospect-agent"},{"title":"Read a prospect simulation teaser report","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/public/prospect-sim/report/{token}","href":"/api-reference/prospect-simulation/read-a-prospect-simulation-teaser-report"},{"title":"Mint a live observer WebSocket URL for a prospect call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/public/prospect-sim/report/{token}/observer-url","href":"/api-reference/prospect-simulation/mint-a-live-observer-websocket-url-for-a-prospect-call"}]},{"group":"Me","pages":[{"title":"Describe the current credential","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/me","href":"/api-reference/me/describe-the-current-credential"},{"title":"List your personal credentials","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/me/api-keys","href":"/api-reference/me/list-your-personal-credentials"},{"title":"Create a personal credential","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/me/api-keys","href":"/api-reference/me/create-a-personal-credential"},{"title":"Revoke one of your personal credentials","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/me/api-keys/{id}","href":"/api-reference/me/revoke-one-of-your-personal-credentials"}]},{"group":"Project","pages":[{"title":"List projects available to this credential","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/projects","href":"/api-reference/project/list-projects-available-to-this-credential"}]},{"group":"Agent Endpoint","pages":[{"title":"List agent endpoints","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent/endpoint","href":"/api-reference/agent-endpoint/list-agent-endpoints"},{"title":"Create a new agent endpoint","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/agent/endpoint","href":"/api-reference/agent-endpoint/create-a-new-agent-endpoint"},{"title":"Get agent endpoint by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent/endpoint/{endpointId}","href":"/api-reference/agent-endpoint/get-agent-endpoint-by-id"},{"title":"Update an agent endpoint","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/agent/endpoint/{endpointId}","href":"/api-reference/agent-endpoint/update-an-agent-endpoint"}]},{"group":"Agent","pages":[{"title":"List an agent's prompts","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent/{agentId}/prompts","href":"/api-reference/agent/list-an-agents-prompts"},{"title":"Set an agent's prompt","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/agent/{agentId}/prompts","href":"/api-reference/agent/set-an-agents-prompt"},{"title":"List a prompt's versions","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent/{agentId}/prompts/{promptId}/versions","href":"/api-reference/agent/list-a-prompts-versions"},{"title":"List agents","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent","href":"/api-reference/agent/list-agents"},{"title":"Create a new agent","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/agent","href":"/api-reference/agent/create-a-new-agent"},{"title":"Get agent by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent/{agentId}","href":"/api-reference/agent/get-agent-by-id"},{"title":"Update an agent","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/agent/{agentId}","href":"/api-reference/agent/update-an-agent"},{"title":"Delete an agent","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/agent/{agentId}","href":"/api-reference/agent/delete-an-agent"},{"title":"Build a Roark-hosted agent","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/agent/build","href":"/api-reference/agent/build-a-roark-hosted-agent"}]},{"group":"Call","pages":[{"title":"List calls","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call","href":"/api-reference/call/list-calls"},{"title":"Create a call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/call","href":"/api-reference/call/create-a-call"},{"title":"Get a call by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call/{callId}","href":"/api-reference/call/get-a-call-by-id"},{"title":"Delete a call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/call/{callId}","href":"/api-reference/call/delete-a-call"},{"title":"Delete calls in batch","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/call/batch-delete","href":"/api-reference/call/delete-calls-in-batch"},{"title":"List call sentiment runs","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call/{callId}/sentiment-run","href":"/api-reference/call/list-call-sentiment-runs"},{"title":"List call metrics","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call/{callId}/metrics","href":"/api-reference/call/list-call-metrics"},{"title":"Get call transcript","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call/{callId}/transcript","href":"/api-reference/call/get-call-transcript"},{"title":"Append tool invocations to a call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/call/{callId}/tool-invocations","href":"/api-reference/call/append-tool-invocations-to-a-call"},{"title":"Update the properties of a call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json patch /v1/call/{callId}/properties","href":"/api-reference/call/update-the-properties-of-a-call"},{"title":"Get call cost","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/call/{callId}/cost","href":"/api-reference/call/get-call-cost"}]},{"group":"Chat","pages":[{"title":"List chats","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/chat","href":"/api-reference/chat/list-chats"},{"title":"Create a chat","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/chat","href":"/api-reference/chat/create-a-chat"},{"title":"Get a chat by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/chat/{id}","href":"/api-reference/chat/get-a-chat-by-id"},{"title":"Get chat transcript","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/chat/{id}/transcript","href":"/api-reference/chat/get-chat-transcript"},{"title":"List chat metrics","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/chat/{id}/metrics","href":"/api-reference/chat/list-chat-metrics"}]},{"group":"Metric","pages":[{"title":"List metric definitions","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/definitions","href":"/api-reference/metric/list-metric-definitions"},{"title":"Create custom metric definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/definitions","href":"/api-reference/metric/create-custom-metric-definition"},{"title":"Get a metric definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/definitions/{idOrSlug}","href":"/api-reference/metric/get-a-metric-definition"},{"title":"Update a metric definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/metric/definitions/{idOrSlug}","href":"/api-reference/metric/update-a-metric-definition"},{"title":"Delete a metric definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/metric/definitions/{idOrSlug}","href":"/api-reference/metric/delete-a-metric-definition"},{"title":"Create a threshold on a metric","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/definitions/{idOrSlug}/thresholds","href":"/api-reference/metric/create-a-threshold-on-a-metric"},{"title":"List a metric’s variants","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/definitions/{idOrSlug}/variants","href":"/api-reference/metric/list-a-metric’s-variants"},{"title":"Create a metric variant","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/definitions/{idOrSlug}/variants","href":"/api-reference/metric/create-a-metric-variant"},{"title":"Get a metric variant","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/definitions/{idOrSlug}/variants/{variantId}","href":"/api-reference/metric/get-a-metric-variant"},{"title":"Update a metric variant","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/metric/definitions/{idOrSlug}/variants/{variantId}","href":"/api-reference/metric/update-a-metric-variant"},{"title":"Delete a metric variant","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/metric/definitions/{idOrSlug}/variants/{variantId}","href":"/api-reference/metric/delete-a-metric-variant"}]},{"group":"Metric Policy","pages":[{"title":"List metric policies","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/policies","href":"/api-reference/metric-policy/list-metric-policies"},{"title":"Create a metric policy","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/policies","href":"/api-reference/metric-policy/create-a-metric-policy"},{"title":"Get metric policy by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/policies/{policyId}","href":"/api-reference/metric-policy/get-metric-policy-by-id"},{"title":"Update a metric policy","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/metric/policies/{policyId}","href":"/api-reference/metric-policy/update-a-metric-policy"},{"title":"Delete a metric policy","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/metric/policies/{policyId}","href":"/api-reference/metric-policy/delete-a-metric-policy"}]},{"group":"Metric Collection Job","pages":[{"title":"List metric collection jobs","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/collection-jobs","href":"/api-reference/metric-collection-job/list-metric-collection-jobs"},{"title":"Create and run a metric collection job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/collection-jobs","href":"/api-reference/metric-collection-job/create-and-run-a-metric-collection-job"},{"title":"Get metric collection job by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/collection-jobs/{jobId}","href":"/api-reference/metric-collection-job/get-metric-collection-job-by-id"},{"title":"Retry a metric collection job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/collection-jobs/{jobId}/retry","href":"/api-reference/metric-collection-job/retry-a-metric-collection-job"},{"title":"Cancel a metric collection job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/metric/collection-jobs/{jobId}/cancel","href":"/api-reference/metric-collection-job/cancel-a-metric-collection-job"},{"title":"Get metric values produced by a metric collection job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/metric/collection-jobs/{jobId}/metric-values","href":"/api-reference/metric-collection-job/get-metric-values-produced-by-a-metric-collection-job"}]},{"group":"Simulation Persona","pages":[{"title":"List personas","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/persona","href":"/api-reference/simulation-persona/list-personas"},{"title":"Create a new persona","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/persona","href":"/api-reference/simulation-persona/create-a-new-persona"},{"title":"Get persona by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/persona/{personaId}","href":"/api-reference/simulation-persona/get-persona-by-id"},{"title":"Update a persona","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/persona/{personaId}","href":"/api-reference/simulation-persona/update-a-persona"},{"title":"Delete a persona","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/persona/{personaId}","href":"/api-reference/simulation-persona/delete-a-persona"}]},{"group":"Simulation","pages":[{"title":"Run a simulation","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/run","href":"/api-reference/simulation/run-a-simulation"},{"title":"List run groups","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/run-group","href":"/api-reference/simulation/list-run-groups"},{"title":"Create a run group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/run-group","href":"/api-reference/simulation/create-a-run-group"},{"title":"Get a run group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/run-group/{groupId}","href":"/api-reference/simulation/get-a-run-group"},{"title":"Delete a run group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/simulation/run-group/{groupId}","href":"/api-reference/simulation/delete-a-run-group"},{"title":"Regenerate a run group report","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/run-group/{groupId}/report","href":"/api-reference/simulation/regenerate-a-run-group-report"},{"title":"List simulation groups","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/group","href":"/api-reference/simulation/list-simulation-groups"},{"title":"Create a simulation group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/group","href":"/api-reference/simulation/create-a-simulation-group"},{"title":"Get a simulation group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/group/{groupId}","href":"/api-reference/simulation/get-a-simulation-group"},{"title":"Update a simulation group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/simulation/group/{groupId}","href":"/api-reference/simulation/update-a-simulation-group"},{"title":"Delete a simulation group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/simulation/group/{groupId}","href":"/api-reference/simulation/delete-a-simulation-group"},{"title":"Run a simulation group","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/group/{groupId}/run","href":"/api-reference/simulation/run-a-simulation-group"},{"title":"Mock a tool call during a test call","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/tool-mock","href":"/api-reference/simulation/mock-a-tool-call-during-a-test-call"},{"title":"List tool fixtures","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/tool-fixture","href":"/api-reference/simulation/list-tool-fixtures"},{"title":"Set a tool fixture","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/tool-fixture","href":"/api-reference/simulation/set-a-tool-fixture"},{"title":"Update a tool fixture","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/simulation/tool-fixture/{fixtureId}","href":"/api-reference/simulation/update-a-tool-fixture"},{"title":"Delete a tool fixture","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/simulation/tool-fixture/{fixtureId}","href":"/api-reference/simulation/delete-a-tool-fixture"}]},{"group":"Simulation Environment","pages":[{"title":"List environments","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/environment","href":"/api-reference/simulation-environment/list-environments"},{"title":"Create an environment","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/environment","href":"/api-reference/simulation-environment/create-an-environment"},{"title":"Get environment by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/environment/{environmentId}","href":"/api-reference/simulation-environment/get-environment-by-id"},{"title":"Update an environment","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/simulation/environment/{environmentId}","href":"/api-reference/simulation-environment/update-an-environment"},{"title":"Delete an environment","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/simulation/environment/{environmentId}","href":"/api-reference/simulation-environment/delete-an-environment"}]},{"group":"Simulation Run Plan","pages":[{"title":"List run plans","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan","href":"/api-reference/simulation-run-plan/list-run-plans"},{"title":"Create a run plan","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/plan","href":"/api-reference/simulation-run-plan/create-a-run-plan"},{"title":"Get run plan by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan/{planId}","href":"/api-reference/simulation-run-plan/get-run-plan-by-id"},{"title":"Update a run plan","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/simulation/plan/{planId}","href":"/api-reference/simulation-run-plan/update-a-run-plan"},{"title":"Delete a run plan","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/simulation/plan/{planId}","href":"/api-reference/simulation-run-plan/delete-a-run-plan"}]},{"group":"Simulation Run Plan Job","pages":[{"title":"List simulation plan jobs","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan/jobs","href":"/api-reference/simulation-run-plan-job/list-simulation-plan-jobs"},{"title":"Get simulation plan job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan/job/{jobId}","href":"/api-reference/simulation-run-plan-job/get-simulation-plan-job"},{"title":"Cancel a simulation plan job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/plan/job/{jobId}/cancel","href":"/api-reference/simulation-run-plan-job/cancel-a-simulation-plan-job"},{"title":"Run a simulation plan","deprecated":true,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/plan/{planId}/job","href":"/api-reference/simulation-run-plan-job/run-a-simulation-plan"},{"title":"Get simulation run results","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan/job/{jobId}/results","href":"/api-reference/simulation-run-plan-job/get-simulation-run-results"},{"title":"Generate simulation run debrief","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/simulation/plan/job/{jobId}/debrief","href":"/api-reference/simulation-run-plan-job/generate-simulation-run-debrief"},{"title":"Get simulation run cost","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/plan/job/{jobId}/cost","href":"/api-reference/simulation-run-plan-job/get-simulation-run-cost"}]},{"group":"Simulation Template","pages":[{"title":"List simulation templates","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/template","href":"/api-reference/simulation-template/list-simulation-templates"}]},{"group":"Simulation Job","pages":[{"title":"Lookup by phone number","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/job/lookup","href":"/api-reference/simulation-job/lookup-by-phone-number"},{"title":"Get simulation by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/job/{jobId}","href":"/api-reference/simulation-job/get-simulation-by-id"},{"title":"List mocked tool invocations","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/simulation/job/{jobId}/tool-mock","href":"/api-reference/simulation-job/list-mocked-tool-invocations"}]},{"group":"Customer Flow","pages":[{"title":"List customer flows","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/customer-flow","href":"/api-reference/customer-flow/list-customer-flows"},{"title":"Create a customer flow","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow","href":"/api-reference/customer-flow/create-a-customer-flow"},{"title":"Get customer flow by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/customer-flow/{flowId}","href":"/api-reference/customer-flow/get-customer-flow-by-id"},{"title":"Update a customer flow","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/customer-flow/{flowId}","href":"/api-reference/customer-flow/update-a-customer-flow"},{"title":"Delete a customer flow","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/customer-flow/{flowId}","href":"/api-reference/customer-flow/delete-a-customer-flow"},{"title":"Replace a scripted flow's steps","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/customer-flow/{flowId}/graph","href":"/api-reference/customer-flow/replace-a-scripted-flows-steps"},{"title":"Update a flow's happy path","description":"Updates the happy path's title, persona, environment, brief or expectations. Omitted fields are left\nalone; `additionalExpectations` replaces the set wholesale rather than appending.\n\nIts persona and environment are what the edge cases inherit, so changing them here changes every edge\ncase that does not name its own.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/customer-flow/{flowId}/happy-path","href":"/api-reference/customer-flow/update-a-flows-happy-path"},{"title":"Duplicate a customer flow","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow/{flowId}/duplicate","href":"/api-reference/customer-flow/duplicate-a-customer-flow"}]},{"group":"Customer Flow Edge Case","pages":[{"title":"Add an edge case","description":"Adds a variant to an IMPROV flow.\n\nA scripted flow's variants are owned by the path engine, one per path through the graph, so they are\ncreated by editing the graph through PUT /v1/customer-flow/{flowId}/graph rather than here.\n\nLeave personaOverrideId or environmentId unset to inherit the happy path's.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow/{flowId}/edge-case","href":"/api-reference/customer-flow-edge-case/add-an-edge-case"},{"title":"Update an edge case","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/customer-flow/{flowId}/edge-case/{edgeCaseId}","href":"/api-reference/customer-flow-edge-case/update-an-edge-case"},{"title":"Remove an edge case","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/customer-flow/{flowId}/edge-case/{edgeCaseId}","href":"/api-reference/customer-flow-edge-case/remove-an-edge-case"},{"title":"Promote an edge case to the happy path","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/customer-flow/{flowId}/edge-case/{edgeCaseId}/promote","href":"/api-reference/customer-flow-edge-case/promote-an-edge-case-to-the-happy-path"}]},{"group":"HTTP Request Definition","pages":[{"title":"List HTTP request definitions","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/http-request-definition","href":"/api-reference/http-request-definition/list-http-request-definitions"},{"title":"Create HTTP request definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/http-request-definition","href":"/api-reference/http-request-definition/create-http-request-definition"},{"title":"Get HTTP request definition by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/http-request-definition/{definitionId}","href":"/api-reference/http-request-definition/get-http-request-definition-by-id"},{"title":"Update HTTP request definition","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/http-request-definition/{definitionId}","href":"/api-reference/http-request-definition/update-http-request-definition"}]},{"group":"Webhook","pages":[{"title":"List webhooks","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/webhook","href":"/api-reference/webhook/list-webhooks"},{"title":"Create webhook","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/webhook","href":"/api-reference/webhook/create-webhook"},{"title":"Get webhook by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/webhook/{webhookId}","href":"/api-reference/webhook/get-webhook-by-id"},{"title":"Delete webhook","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/webhook/{webhookId}","href":"/api-reference/webhook/delete-webhook"},{"title":"Event","version":null,"deprecated":null,"openapi":"/api-reference/openapi.documented.json webhook Webhook","href":"/api-reference/webhook/event"}]},{"group":"Issue","pages":[{"title":"List issues","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/issue","href":"/api-reference/issue/list-issues"},{"title":"Create issue","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/issue","href":"/api-reference/issue/create-issue"},{"title":"Get issue by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/issue/{issueId}","href":"/api-reference/issue/get-issue-by-id"}]},{"group":"Autoimprove","pages":[{"title":"List Autoimprove jobs","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/autoimprove/job","href":"/api-reference/autoimprove/list-autoimprove-jobs"},{"title":"Start an Autoimprove job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job","href":"/api-reference/autoimprove/start-an-autoimprove-job"},{"title":"Get an Autoimprove job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/autoimprove/job/{jobId}","href":"/api-reference/autoimprove/get-an-autoimprove-job"},{"title":"Send guidance to a running job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job/{jobId}/guidance","href":"/api-reference/autoimprove/send-guidance-to-a-running-job"},{"title":"Answer Roark's question","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job/{jobId}/answer","href":"/api-reference/autoimprove/answer-roarks-question"},{"title":"Cancel a job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job/{jobId}/cancel","href":"/api-reference/autoimprove/cancel-a-job"},{"title":"Promote a verified job to production","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job/{jobId}/promote","href":"/api-reference/autoimprove/promote-a-verified-job-to-production"},{"title":"Dismiss a verified job","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/autoimprove/job/{jobId}/dismiss","href":"/api-reference/autoimprove/dismiss-a-verified-job"}]},{"group":"Agent Config","pages":[{"title":"Resolve config for a session","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/agent-config/{key}/resolve","href":"/api-reference/agent-config/resolve-config-for-a-session"},{"title":"List managed configs","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent-config","href":"/api-reference/agent-config/list-managed-configs"},{"title":"Get a managed config","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/agent-config/{key}","href":"/api-reference/agent-config/get-a-managed-config"},{"title":"Write a config revision","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/agent-config/{key}","href":"/api-reference/agent-config/write-a-config-revision"},{"title":"Promote staging to production","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/agent-config/{key}/promote","href":"/api-reference/agent-config/promote-staging-to-production"},{"title":"Discard the staging revision","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/agent-config/{key}/staging","href":"/api-reference/agent-config/discard-the-staging-revision"}]},{"group":"Knowledge Base","pages":[{"title":"List knowledge bases","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/knowledge-base","href":"/api-reference/knowledge-base/list-knowledge-bases"},{"title":"Create a knowledge base","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/knowledge-base","href":"/api-reference/knowledge-base/create-a-knowledge-base"},{"title":"Get a knowledge base by ID","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/knowledge-base/{id}","href":"/api-reference/knowledge-base/get-a-knowledge-base-by-id"}]},{"group":"Analytics","pages":[{"title":"Get the analytics schema","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/analytics/schema","href":"/api-reference/analytics/get-the-analytics-schema"},{"title":"Run an analytics query","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/analytics/query","href":"/api-reference/analytics/run-an-analytics-query"}]},{"group":"Dashboard","pages":[{"title":"List dashboards","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/dashboard","href":"/api-reference/dashboard/list-dashboards"},{"title":"Create a dashboard","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/dashboard","href":"/api-reference/dashboard/create-a-dashboard"},{"title":"Get a dashboard","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/dashboard/{dashboardId}","href":"/api-reference/dashboard/get-a-dashboard"},{"title":"Update a dashboard","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/dashboard/{dashboardId}","href":"/api-reference/dashboard/update-a-dashboard"},{"title":"Delete a dashboard","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/dashboard/{dashboardId}","href":"/api-reference/dashboard/delete-a-dashboard"},{"title":"Get dashboard data","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/dashboard/{dashboardId}/data","href":"/api-reference/dashboard/get-dashboard-data"},{"title":"Add a widget","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/dashboard/{dashboardId}/widget","href":"/api-reference/dashboard/add-a-widget"},{"title":"Replace a widget","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/dashboard/{dashboardId}/widget/{widgetId}","href":"/api-reference/dashboard/replace-a-widget"},{"title":"Remove a widget","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/dashboard/{dashboardId}/widget/{widgetId}","href":"/api-reference/dashboard/remove-a-widget"},{"title":"Get widget data","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/dashboard/{dashboardId}/widget/{widgetId}/data","href":"/api-reference/dashboard/get-widget-data"}]},{"group":"Config","pages":[{"title":"Diff a config bundle","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/config/diff","href":"/api-reference/config/diff-a-config-bundle"},{"title":"Apply a config bundle","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/config/apply","href":"/api-reference/config/apply-a-config-bundle"}]},{"group":"Organization Project","pages":[{"title":"List projects","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/org/project","href":"/api-reference/organization-project/list-projects"},{"title":"Create a project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/org/project","href":"/api-reference/organization-project/create-a-project"},{"title":"Get a project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/org/project/{projectId}","href":"/api-reference/organization-project/get-a-project"},{"title":"Update a project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/org/project/{projectId}","href":"/api-reference/organization-project/update-a-project"},{"title":"Delete a project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/org/project/{projectId}","href":"/api-reference/organization-project/delete-a-project"}]},{"group":"Organization Project Member","pages":[{"title":"List project members","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/org/project/{projectId}/member","href":"/api-reference/organization-project-member/list-project-members"},{"title":"Invite a member","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/org/project/{projectId}/member","href":"/api-reference/organization-project-member/invite-a-member"},{"title":"Update a member role","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json put /v1/org/project/{projectId}/member/{memberId}","href":"/api-reference/organization-project-member/update-a-member-role"},{"title":"Remove a member","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/org/project/{projectId}/member/{memberId}","href":"/api-reference/organization-project-member/remove-a-member"}]},{"group":"Organization Project API Key","pages":[{"title":"List project API keys","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/org/project/{projectId}/api-key","href":"/api-reference/organization-project-api-key/list-project-api-keys"},{"title":"Generate a project API key","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json post /v1/org/project/{projectId}/api-key","href":"/api-reference/organization-project-api-key/generate-a-project-api-key"},{"title":"Revoke a project API key","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json delete /v1/org/project/{projectId}/api-key/{keyId}","href":"/api-reference/organization-project-api-key/revoke-a-project-api-key"}]},{"group":"Usage","pages":[{"title":"Get usage \u0026 cost","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/usage","href":"/api-reference/usage/get-usage-\u0026-cost"}]},{"group":"Benchmark","pages":[{"title":"List benchmark suites","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/suite","href":"/api-reference/benchmark/list-benchmark-suites"},{"title":"List benchmark metrics","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/metric","href":"/api-reference/benchmark/list-benchmark-metrics"},{"title":"Get the benchmark leaderboard","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/leaderboard","href":"/api-reference/benchmark/get-the-benchmark-leaderboard"},{"title":"List score samples for a target","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/target/{targetKey}/score-sample","href":"/api-reference/benchmark/list-score-samples-for-a-target"},{"title":"List a target’s published generations","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/target/{targetKey}/history","href":"/api-reference/benchmark/list-a-target’s-published-generations"},{"title":"Get a benchmark target","deprecated":null,"version":null,"openapi":"/api-reference/openapi.documented.json get /v1/benchmark/target/{targetKey}","href":"/api-reference/benchmark/get-a-benchmark-target"}]}]},{"tab":"CLI Reference","icon":"square-terminal","pages":[{"title":"CLI Reference","href":"/cli-reference/overview"}]},{"tab":"Changelog","icon":"history","href":"https://changelog.roark.ai"}]},"title":"Duplicate a customer flow - Roark Docs","buildId":"dpl_HcHmUS6i3UwswnTHJUEdcTtwfdTQ","deploymentId":"dpl_HcHmUS6i3UwswnTHJUEdcTtwfdTQ","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-BRm_l__d.js","utils-f1l1Oc-G.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-BlhqDhJO.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-DHCB7rsC.js","snippetPresets-CYXLx6Hv.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-B78P2dEq.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-C4YLPVAh.js","shallow-vlLnTDkQ.js","getApiPlaygroundDisplay-D_98hhvf.js","CodeSnippet-DtnZHGqq.js","CodeSnippets-CLFb8KJt.js","Page-kQh739Uz.js","getPastedJson-Cq-zlbnU.js","icon-sN3t983t.js","MethodPill-P_YQ2s09.js","DuplicateIcon-BVzyNsGJ.js","createIcon-B3jqgqC6.js","DynamicMarkdown-Crwq6WG_.js","SectionHeading-C7pGS2Bb.js","axios-vUo_bRtp.js","Markdown-BuPH8yuW.js","clsx-CCOYKgaU.js","lib--scc4DXi.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-BnkC_6oA.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-BM4S31-f.js"]}