{"openapi":"3.1.0","info":{"title":"Word Is Bond — Voice Agent Testing API","version":"0.1.0","summary":"Metered, self-serve testing for voice agents you run.","description":"Point a synthetic caller at a voice agent you run — on any SIP / WebRTC or PSTN endpoint — and get back a scored transcript, pass/fail verdict, and per-turn latency / word-error-rate / barge-in metrics. Score a captured transcript offline, run a live synthetic call, put a suite on a schedule to catch behaviour drift, or read regression trends. You only ever test systems you control — there is no outbound-to-strangers surface. Plans and quotas are served live by GET /api/billing/plans (the single source of truth for pricing — never hard-coded). Agent-readable overview: https://wordis-bond.com/llms.txt","contact":{"name":"Word Is Bond","url":"https://wordis-bond.com"},"license":{"name":"Proprietary","url":"https://wordis-bond.com/terms"}},"externalDocs":{"description":"Agent-readable overview (llms.txt) and quickstart","url":"https://wordis-bond.com/llms.txt"},"servers":[{"url":"https://api.wordis-bond.com","description":"Production"}],"security":[{"apiKey":[]}],"tags":[{"name":"Testing","description":"Run and score voice-agent tests."},{"name":"Suites","description":"Reusable scenarios/personas against a target agent."},{"name":"Trends","description":"Pass-rate, average score, and regression analytics over runs."},{"name":"Numbers","description":"Prove control of a phone number before testing over PSTN."},{"name":"Outcome","description":"Post-call assertions: after a scored run, verify the business result actually landed."},{"name":"Recording","description":"BYOS call recording: tee a scored call to your OWN storage. wordis-bond keeps only a pointer."},{"name":"Flows","description":"Version-control + a regression gate for phone-system flows: import a platform config to diffable code, version it, and block a change that regressed behavior."},{"name":"Monitoring","description":"Voice-agent observability: score LIVE production calls with the same versioned judge that scores tests, track rolling health, and alert on drift. The raw transcript is scored in-flight and never stored."},{"name":"Reports","description":"Shareable, tokenized run reports."},{"name":"API keys","description":"Issue and revoke wb_live_ API keys."},{"name":"Billing","description":"Plans and the authed tenant subscription (source of truth for pricing)."},{"name":"Meta","description":"Health and discovery."}],"paths":{"/health":{"get":{"operationId":"getHealth","tags":["Meta"],"summary":"Service health (no auth)","security":[],"responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"ok"},"worker":{"type":"string"},"env":{"type":"string"}},"required":["status"]}}}}}}},"/api/billing/plans":{"get":{"operationId":"listPlans","tags":["Billing"],"summary":"Plan + entitlements table (no auth) — the single source of truth for pricing","description":"The complete plan/entitlement table as cacheable JSON. This is the ONLY authoritative source of prices and quotas — clients, docs, and agents must read prices here rather than hard-coding them.","security":[],"responses":{"200":{"description":"Plans","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/PlanView"}},"partnerProgram":{"$ref":"#/components/schemas/PartnerProgram"},"currency":{"type":"string","examples":["usd"]}},"required":["plans","partnerProgram","currency"]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}}}}},"/api/billing/subscription":{"get":{"operationId":"getSubscription","tags":["Billing"],"summary":"The authed tenant's current plan, usage, and entitlements","responses":{"200":{"description":"Subscription snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/Plan"},"hasBillingAccount":{"type":"boolean"},"usage":{"type":"object","properties":{"callMinutes":{"type":"number"},"ttsChars":{"type":"number"}}},"entitlements":{"$ref":"#/components/schemas/PlanView"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/api-keys":{"get":{"operationId":"listApiKeys","tags":["API keys"],"summary":"List active (non-revoked) API keys","description":"Never returns the secret — only the id, name, and 12-char prefix.","responses":{"200":{"description":"Active keys","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createApiKey","tags":["API keys"],"summary":"Create an API key (the raw token is returned exactly once)","description":"The `token` (`wb_live_…`, 80 chars) is shown ONCE and never stored in plaintext — only a SHA-256 hash is persisted. Store it securely; a lost token must be revoked and reissued.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"A human label for the key."}},"required":["name"]}}}},"responses":{"201":{"description":"Created (token shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"token":{"type":"string","examples":["wb_live_…"]}},"required":["token"]}]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","tags":["API keys"],"summary":"Revoke an API key","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"revoked":{"type":"boolean","const":true}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/tests/demo":{"post":{"operationId":"runDemo","tags":["Testing"],"summary":"Run the hosted demo agent and get a scored result (~60s, zero carrier cost)","description":"Runs the canonical demo scenario against the hosted demo agent (a dental front desk) and returns a completed, fully-scored run — so you get a real scorecard without owning an agent yet. Direct transport only; not metered against call minutes. The response also includes a shareable public report URL.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"persona":{"type":"object","properties":{"description":{"type":"string"}},"description":"Override the synthetic caller persona."},"goal":{"type":"string","description":"Override what the caller is trying to accomplish."},"language":{"type":"string","default":"en"},"expected":{"type":"array","items":{"type":"string"},"description":"Expected agent lines to pin for word-error-rate scoring."},"bargeIn":{"type":"boolean","description":"Inject one caller-initiated barge-in."}}}}}},"responses":{"201":{"description":"Completed, scored demo run","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/TestRun"},{"type":"object","properties":{"peerId":{"type":"string","examples":["wb-demo-dental"]},"reportUrl":{"type":"string","format":"uri"}}}]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/tests/run":{"post":{"operationId":"runTest","tags":["Testing"],"summary":"Run a test: score a captured transcript (offline) or run a live synthetic call","description":"Two modes, selected by the body:\n\n• **Offline scoring** — supply `transcript` (or `turns`) plus a `scenarioGoal`; the judge scores it synchronously and the completed run is returned. Works on every plan; not metered against call minutes.\n\n• **Live synthetic call** — supply `targetAgent` and `goal`. For the hosted demo target (`{transport:\"direct\", peerId:\"wb-demo-dental\"}`) the run completes synchronously. For a `direct` (SIP/WebRTC) target the response returns a tokenized `mediaWebSocketUrl` per run for your agent-side harness to dial. For a `pstn` target a real carrier call is originated (pro+ plans only — starter returns 402). Poll `GET /api/tests/{id}` for the terminal scored state.\n\n**PSTN destinations must be verified.** A `pstn` run is only placed to a number the account has proven it controls (or a Word Is Bond DID). An unproven destination returns `403 NUMBER_NOT_VERIFIED` and no call is placed. Prove control once with `POST /api/numbers/verify/start` → `POST /api/numbers/verify/confirm`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ScoreTranscriptRequest"},{"$ref":"#/components/schemas/LiveRunRequest"}]}}}},"responses":{"201":{"description":"Offline/demo → a single completed run in `data`. Live (direct/pstn) → `data` is `{ runs, concurrency, transport? }` where each run may carry `mediaWebSocketUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/TestRun"},{"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/TestRun"}},"concurrency":{"type":"integer"},"transport":{"type":"string"}},"required":["runs"]}]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/NumberNotVerified"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/api/outcome/secret":{"get":{"operationId":"getOutcomeSecret","tags":["Outcome"],"summary":"Read the per-tenant assertion signing secret (minted on first read)","description":"Word Is Bond signs every outbound assertion request with this secret. Verify it on your endpoint: recompute hex(HMAC-SHA256(secret, `${wb-timestamp}.${rawBody}`)), compare in constant time, and reject a timestamp more than 5 minutes old. Rotate with POST /api/outcome/secret/rotate.","responses":{"200":{"description":"The signing secret + verification recipe","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"secret":{"type":"string","examples":["wbas_…"]},"signatureHeader":{"type":"string","const":"wb-signature"},"timestampHeader":{"type":"string","const":"wb-timestamp"},"verify":{"type":"string"}},"required":["secret"]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/outcome/assertions":{"get":{"operationId":"listOutcomeAssertions","tags":["Outcome"],"summary":"List the outcome assertions this account has registered","responses":{"200":{"description":"Assertions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OutcomeAssertion"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createOutcomeAssertion","tags":["Outcome"],"summary":"Register a post-call assertion against a suite","description":"After a scored run for `suiteId` (optionally narrowed to one scenario by `scenarioName`), Word Is Bond POSTs a signed request to `endpoint` and evaluates the matchers. The endpoint MUST be a public https URL; internal, loopback, link-local, and cloud-metadata hosts are rejected at registration. `expected.json_path` maps a JSONPath to an expected value (or the sentinels `$exists` / `$absent`); values may interpolate the scenario, e.g. `{{scenario.requested_date}}`. `priceCheck` compares the price the agent quoted against an uploaded price list. Method is POST and trigger is post_call in v0.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutcomeAssertionInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OutcomeAssertion"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/outcome/assertions/{id}":{"parameters":[{"$ref":"#/components/parameters/IdPath"}],"get":{"operationId":"getOutcomeAssertion","tags":["Outcome"],"summary":"Get one assertion","responses":{"200":{"description":"Assertion","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OutcomeAssertion"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"put":{"operationId":"updateOutcomeAssertion","tags":["Outcome"],"summary":"Update one assertion (partial)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutcomeAssertionInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OutcomeAssertion"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"operationId":"deleteOutcomeAssertion","tags":["Outcome"],"summary":"Delete one assertion","responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/outcome/price-lists":{"get":{"operationId":"listPriceLists","tags":["Outcome"],"summary":"List uploaded price lists","responses":{"200":{"description":"Price lists","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PriceList"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createPriceList","tags":["Outcome"],"summary":"Upload a price list the price matcher compares an agent quote against","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"currency":{"type":"string","examples":["USD"]},"entries":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"price":{"type":"number"}},"required":["key","price"]}}},"required":["name","entries"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PriceList"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/outcome/runs/{runId}":{"get":{"operationId":"getRunOutcomes","tags":["Outcome"],"summary":"The evaluated outcomes (pass / fail / inconclusive) for one of your runs","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Outcome results for the run","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"results":{"type":"array","items":{"$ref":"#/components/schemas/OutcomeResult"}},"totals":{"type":"object","properties":{"total":{"type":"integer"},"pass":{"type":"integer"},"fail":{"type":"integer"},"inconclusive":{"type":"integer"}}}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/recording/secret":{"get":{"operationId":"getRecordingSecret","tags":["Recording"],"summary":"Read the BYOS signing secret (minted on first read)","description":"wordis-bond signs every presigned-URL callback with this secret; verify our `wb-signature` before minting an upload URL. Requires the byosRecording capability (pro/enterprise). Rotate with POST /api/recording/secret/rotate.","responses":{"200":{"description":"The signing secret + verify recipe","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"secret":{"type":"string"},"signatureHeader":{"type":"string"},"timestampHeader":{"type":"string"},"verify":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"}}}},"/api/recording/targets":{"get":{"operationId":"listRecordingTargets","tags":["Recording"],"summary":"List the storage targets this account has registered","responses":{"200":{"description":"Recording targets","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RecordingTarget"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"}}},"post":{"operationId":"createRecordingTarget","tags":["Recording"],"summary":"Register where recordings go (a presigned-URL callback endpoint)","description":"The `callbackUrl` must be a public https URL and is SSRF-validated at registration. A run records to the account’s first enabled target when it opts in with `record: true`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingTargetInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RecordingTarget"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/recording/targets/{id}":{"get":{"operationId":"getRecordingTarget","tags":["Recording"],"summary":"Get one recording target","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Recording target","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RecordingTarget"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}},"put":{"operationId":"updateRecordingTarget","tags":["Recording"],"summary":"Update a recording target (enable/disable, change callback or track)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingTargetInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RecordingTarget"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"operationId":"deleteRecordingTarget","tags":["Recording"],"summary":"Delete a recording target","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/numbers":{"get":{"operationId":"listVerifiedNumbers","tags":["Numbers"],"summary":"List the phone numbers this account has proven it controls","description":"Every number here may be used as a `pstn` target for a test run. Word Is Bond DIDs are always dialable and are not listed.","responses":{"200":{"description":"Verified numbers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VerifiedNumber"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/numbers/verify/start":{"post":{"operationId":"startNumberVerification","tags":["Numbers"],"summary":"Prove you control a phone number: Word Is Bond calls it and speaks a one-time code","description":"Normalizes `number` to E.164, then places ONE short outbound call to it. The call speaks a 6-digit code twice and hangs up. Confirm the code within 10 minutes with `POST /api/numbers/verify/confirm`.\n\nOnly a salted SHA-256 digest of the code is stored; the code is single-use, expires in 10 minutes, and locks after 5 incorrect confirmations.\n\nThis is the only endpoint that dials a number you have not yet proven you control, so it is tightly bounded: 5 starts per account per 10 minutes, 3 calls per number per 24 hours, 5 distinct numbers per account per 24 hours, and the call is charged against the account monthly spend ceiling. Calling an already-verified number returns `200` and places no call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"number":{"type":"string","description":"The number to verify. E.164, or a NANP number in any spelling.","examples":["+15045204977"]}},"required":["number"]}}}},"responses":{"200":{"description":"Already verified — no call was placed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"number":{"type":"string"},"verified":{"type":"boolean","const":true},"alreadyVerified":{"type":"boolean","const":true}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"202":{"description":"The verification call is being placed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"number":{"type":"string","examples":["+15045204977"]},"status":{"type":"string","const":"calling"},"verificationId":{"type":"string","format":"uuid"},"expiresInSeconds":{"type":"integer","examples":[600]},"attemptsAllowed":{"type":"integer","examples":[5]},"next":{"type":"string"}},"required":["number","status","expiresInSeconds"]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"description":"A verification limit was reached: `RATE_LIMITED` (burst), `NUMBER_ATTEMPT_LIMIT` (3 calls to this number in 24h), or `ACCOUNT_NUMBER_LIMIT` (5 distinct numbers in 24h).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/api/numbers/verify/confirm":{"post":{"operationId":"confirmNumberVerification","tags":["Numbers"],"summary":"Confirm the code spoken on the verification call","description":"On success the number becomes a permitted `pstn` target for this account. The code is compared in constant time against the stored digest and is burned on use. Wrong codes decrement `attemptsRemaining`; the challenge locks at zero and a new verification must be started.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"number":{"type":"string","examples":["+15045204977"]},"code":{"type":"string","pattern":"^\\d{6}$","examples":["135790"]},"label":{"type":"string","description":"Optional label stored with the verified number."}},"required":["number","code"]}}}},"responses":{"201":{"description":"The number is verified","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/VerifiedNumber"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Validation failed, or the challenge cannot be used: `NO_PENDING_VERIFICATION`, `VERIFICATION_EXPIRED`, or `INVALID_CODE` (with `attemptsRemaining`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`VERIFICATION_LOCKED` — too many incorrect codes. Start a new verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/numbers/{id}":{"delete":{"operationId":"revokeVerifiedNumber","tags":["Numbers"],"summary":"Revoke a verified number","description":"After revocation, PSTN runs against that number return 403 NUMBER_NOT_VERIFIED.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"revoked":{"type":"boolean","const":true},"id":{"type":"string","format":"uuid"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/tests/{id}/score":{"post":{"operationId":"rescoreRun","tags":["Testing"],"summary":"(Re)score a run's transcript with the judge","parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"scenarioGoal":{"type":"string"},"assertions":{"type":"array","items":{"type":"string"}},"transcript":{"type":"string","description":"Overrides the run's stored transcript when supplied."}}}}}},"responses":{"200":{"description":"Rescored run","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TestRun"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/tests/canary":{"post":{"operationId":"runCanary","tags":["Testing"],"summary":"Trigger the hosted self-test now and publish a public report","description":"Runs the demo suite against the hosted demo agent under the fixed canary account and publishes a dated, tokenized public report — the reproducible proof the landing page links.","responses":{"201":{"description":"Canary run summary","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"score":{"type":["integer","null"]},"passed":{"type":["boolean","null"]},"reportUrl":{"type":"string","format":"uri"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/tests":{"get":{"operationId":"listRuns","tags":["Testing"],"summary":"List the most recent runs for the tenant (up to 100)","responses":{"200":{"description":"Runs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TestRun"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/tests/{id}":{"get":{"operationId":"getRun","tags":["Testing"],"summary":"Fetch one run (poll here for the terminal scored state)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Run","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TestRun"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/tests/suites":{"get":{"operationId":"listSuites","tags":["Suites"],"summary":"List the tenant's test suites","responses":{"200":{"description":"Suites","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TestSuite"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createSuite","tags":["Suites"],"summary":"Create a reusable test suite","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"targetAgent":{"$ref":"#/components/schemas/TargetAgent"},"language":{"type":"string","default":"en"},"scenarios":{"type":"array","items":{"$ref":"#/components/schemas/Scenario"}},"schedule":{"type":"string","description":"Cadence for scheduled runs (plan-gated), e.g. \"weekly\" | \"daily\" | \"hourly\"."}},"required":["name","targetAgent"]}}}},"responses":{"201":{"description":"Created suite","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TestSuite"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/tests/suites/{id}":{"get":{"operationId":"getSuite","tags":["Suites"],"summary":"Fetch one suite","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Suite","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TestSuite"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/tests/suites/{id}/trends":{"get":{"operationId":"getSuiteTrends","tags":["Trends"],"summary":"Per-suite time series + run-to-run regression flags","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Suite trend series","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SuiteTrendSeries"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/tests/trends":{"get":{"operationId":"getAccountTrends","tags":["Trends"],"summary":"Account-wide pass-rate, average score, and regressions over a window","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":365,"default":90}}],"responses":{"200":{"description":"Account trends","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AccountTrends"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/reports/{id}/link":{"get":{"operationId":"getReportLink","tags":["Reports"],"summary":"Get the shareable public report URL for one of your runs","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Tokenized share URL","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"revokeReportLink","tags":["Reports"],"summary":"Revoke sharing for one of your runs (invalidates every outstanding share link)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Sharing revoked","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"revoked":{"type":"boolean"}},"required":["revoked"]},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/r/{id}/{token}":{"get":{"operationId":"getPublicReport","tags":["Reports"],"summary":"Public, tokenized HTML report for one run (no auth; not enumerable)","security":[],"parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Report HTML","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Not found or bad token (existence is never revealed)"}}}},"/api/flows":{"get":{"operationId":"listFlows","tags":["Flows"],"summary":"List your version-controlled phone-system flows","responses":{"200":{"description":"Flows","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createFlow","tags":["Flows"],"summary":"Create a flow and its first immutable version","description":"Compile a phone-system flow to the canonical Flow IR and save it as version 1. Each save is immutable and content-hashed — this is the version control.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"language":{"type":"string"},"description":{"type":"string"},"suiteId":{"type":"string","description":"Test suite the regression gate runs against this flow."},"ir":{"$ref":"#/components/schemas/FlowIR"},"note":{"type":"string"}},"required":["name","ir"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/import":{"post":{"operationId":"importFlow","tags":["Flows"],"summary":"Import a platform config (e.g. Vapi) into Flow IR","description":"Convert a real platform flow/agent config into the canonical, diffable Flow IR. Reports the fields the IR abstracts away. Pass save:true to persist it as a new flow (version 1). The same IR is the migration surface — export it to another platform. Vapi ships in v0; Twilio Studio and Amazon Connect are the next adapters onto this IR.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["vapi"]},"config":{"type":"object","description":"The platform's native flow/agent object."},"save":{"type":"boolean"},"name":{"type":"string"}},"required":["platform","config"]}}}},"responses":{"200":{"description":"Imported IR","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"201":{"description":"Imported + saved as a flow","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/credentials":{"get":{"operationId":"listPlatformCredentials","tags":["Flows"],"summary":"List stored platform API keys (masked)","responses":{"200":{"description":"Credentials (masked hint only)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"storePlatformCredential","tags":["Flows"],"summary":"Store a platform API key for live imports (sealed at rest)","description":"Store a customer platform API key for a live import. The key is sealed with AES-256-GCM at rest and never returned or logged — only a masked hint comes back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["vapi"]},"apiKey":{"type":"string"}},"required":["platform","apiKey"]}}}},"responses":{"201":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/{id}":{"get":{"operationId":"getFlow","tags":["Flows"],"summary":"A flow and its current IR version","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Flow","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteFlow","tags":["Flows"],"summary":"Delete a flow and all its versions","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/flows/{id}/versions":{"get":{"operationId":"listFlowVersions","tags":["Flows"],"summary":"List a flow's immutable versions (metadata + content hashes)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Versions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"operationId":"saveFlowVersion","tags":["Flows"],"summary":"Save a new immutable version and get the diff vs the previous","parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ir":{"$ref":"#/components/schemas/FlowIR"},"note":{"type":"string"}},"required":["ir"]}}}},"responses":{"201":{"description":"Version saved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/{id}/versions/{version}":{"get":{"operationId":"getFlowVersion","tags":["Flows"],"summary":"One version's IR","parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"version","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Version","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/flows/{id}/diff":{"get":{"operationId":"diffFlowVersions","tags":["Flows"],"summary":"Structural diff between two versions","description":"Human-readable added / removed / changed nodes and edges between two versions of the flow.","parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"from","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"to","in":"query","required":false,"schema":{"type":"integer","minimum":1},"description":"Defaults to the current version."}],"responses":{"200":{"description":"Diff","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/flows/{id}/export":{"post":{"operationId":"exportFlow","tags":["Flows"],"summary":"Export the current IR to a platform config","parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"platform","in":"query","required":false,"schema":{"type":"string","enum":["vapi"]}},{"name":"version","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Exported config","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/{id}/test":{"post":{"operationId":"runFlowGate","tags":["Flows"],"summary":"Run the regression gate now","description":"Compile the current flow version into a synthetic-caller scenario, run it against the flow's target, and compare the result to the previous version's baseline. A pass→fail or a score drop past the threshold is a regression that GATES the change (gate.gated=false). Reuses the same judge-version-aware regression logic as the trends endpoint.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"201":{"description":"Gate verdict","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/flows/{id}/gate":{"get":{"operationId":"getFlowGate","tags":["Flows"],"summary":"The gate verdict for the flow's latest run","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Gate verdict (or null when no run exists)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor":{"get":{"operationId":"listMonitors","tags":["Monitoring"],"summary":"List your production monitors and their cached health","description":"Each monitor watches one production line/agent. Requires the productionMonitoring capability (pro/enterprise). The ingest secret is never returned here — read it from GET /api/monitor/{id}/secret.","responses":{"200":{"description":"Monitors","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Monitor"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"}}},"post":{"operationId":"createMonitor","tags":["Monitoring"],"summary":"Create a monitor (mints an ingest signing secret)","description":"Define what \"good\" looks like via `scenarioGoal` (+ optional `assertions` / `rubric`) — the same judge inputs a test suite uses. Then stream completed calls to POST /api/monitor/{id}/events.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Monitor"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/monitor/{id}":{"get":{"operationId":"getMonitor","tags":["Monitoring"],"summary":"Get one monitor (without its ingest secret)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Monitor","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Monitor"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"updateMonitor","tags":["Monitoring"],"summary":"Update a monitor (goal, assertions, sampling, alerts, enable/disable)","description":"A supplied `alertWebhookUrl` must be a public https URL and is SSRF-validated at set time.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Monitor"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"operationId":"deleteMonitor","tags":["Monitoring"],"summary":"Delete a monitor and its events","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/{id}/secret":{"get":{"operationId":"getMonitorSecret","tags":["Monitoring"],"summary":"Read the ingest signing secret (minted on create)","description":"Use this `wbms_` secret to sign a webhook ingest: wb-signature = hex(HMAC-SHA256(secret, `${wb-timestamp}.${rawBody}`)). Rotate with POST /api/monitor/{id}/secret/rotate.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Secret + signing recipe","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"secret":{"type":"string"},"signatureHeader":{"type":"string"},"timestampHeader":{"type":"string"},"sign":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/{id}/secret/rotate":{"post":{"operationId":"rotateMonitorSecret","tags":["Monitoring"],"summary":"Rotate the ingest signing secret (old signatures stop verifying)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Rotated secret","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"secret":{"type":"string"},"rotated":{"type":"boolean"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/{id}/events":{"post":{"operationId":"ingestMonitorEvent","tags":["Monitoring"],"summary":"Ingest a completed production call (API-key auth)","description":"Send a finished call to be scored. Returns 202 immediately; scoring runs in the background and the monitor health updates. Sampling and a monthly cap bound cost — an over-cap or sampled-out call is recorded as skipped, never billed. The transcript is scored in-flight and never stored.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorIngestInput"}}}},"responses":{"202":{"description":"Accepted for scoring","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string"},"eventId":{"type":"string"},"reason":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"get":{"operationId":"listMonitorEvents","tags":["Monitoring"],"summary":"Recent scored events for a monitor (metadata only; no transcript)","parameters":[{"$ref":"#/components/parameters/IdPath"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MonitorEvent"}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/{id}/health":{"get":{"operationId":"getMonitorHealth","tags":["Monitoring"],"summary":"Live rolling health + drift for a monitor","description":"Health is one of unknown | healthy | degraded | critical, computed over the rolling window. Drift is isolated by judge version — a judge change is reported as `judgeVersionChanged`, never as drift.","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Health","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/{id}/series":{"get":{"operationId":"getMonitorSeries","tags":["Monitoring"],"summary":"Score / pass time series for a monitor (for charting)","parameters":[{"$ref":"#/components/parameters/IdPath"}],"responses":{"200":{"description":"Series points","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"monitorId":{"type":"string"},"points":{"type":"array","items":{"type":"object"}}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/monitor/ingest":{"post":{"operationId":"ingestMonitorWebhook","tags":["Monitoring"],"summary":"Signed-webhook ingest (HMAC; no bearer token)","description":"For platform relays that cannot hold an API key. Authenticate with the monitor ingest secret: set wb-timestamp (unix seconds) and wb-signature = hex(HMAC-SHA256(ingestSecret, `${wb-timestamp}.${rawBody}`)). The account is derived from the monitor. A missing/invalid signature is 401.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/MonitorIngestInput"},{"type":"object","required":["monitorId"],"properties":{"monitorId":{"type":"string","format":"uuid"}}}]}}}},"responses":{"202":{"description":"Accepted for scoring","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string"},"eventId":{"type":"string"}}},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"400":{"description":"Missing monitorId / transcript / bad JSON","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"null"},"error":{"type":["object","string","null"]}},"required":["data","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"wb_live_*","description":"Send your API key as a bearer token on every request: `Authorization: Bearer wb_live_…`. Create one at POST /api/api-keys (the raw token is shown once). Only a SHA-256 hash is stored."}},"parameters":{"IdPath":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}},"responses":{"Unauthorized":{"description":"Missing, invalid, or revoked credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"PaymentRequired":{"description":"The plan does not permit this action, or the monthly quota is spent with no overage (e.g. a starter plan attempting a PSTN run). Body carries an upgrade hint; see GET /api/billing/plans.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"RateLimited":{"description":"Too many requests, or a concurrency/spend ceiling was hit. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"NumberNotVerified":{"description":"`NUMBER_NOT_VERIFIED` — a PSTN test call was requested to a number this account has not proven it controls. No call was placed and nothing was charged. Word Is Bond only tests systems you control. The error body carries a `hint` pointing at POST /api/numbers/verify/start.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"ValidationError":{"description":"The request body failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"NotFound":{"description":"The resource does not exist or is not owned by the tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"ServiceUnavailable":{"description":"A dependency (e.g. the carrier for a PSTN run) is not configured or is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"schemas":{"FlowIR":{"type":"object","description":"The canonical, git-serializable representation of a phone-system flow. A discriminated node graph (prompt / menu / condition / agent / transfer / tool / hangup) plus flow metadata. Deterministic serialization → a stable content hash and a clean diff, so a flow can be version-controlled and regression-tested like source code. Extensible toward the migration surface — the same envelope hosts an imported vendor config and a hand-authored flow.","properties":{"schemaVersion":{"type":"integer","const":1},"kind":{"type":"string","const":"voice_flow"},"name":{"type":"string"},"language":{"type":"string","examples":["en"]},"entry":{"type":"string","description":"Id of the node the call enters at."},"nodes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["prompt","menu","condition","agent","transfer","tool","hangup"]},"label":{"type":"string"}},"required":["id","type"]}},"metadata":{"type":"object","description":"Free-form; reserved keys `goal` and `persona` drive the regression scenario."}},"required":["schemaVersion","kind","name","language","entry","nodes"]},"ErrorEnvelope":{"type":"object","properties":{"data":{"type":"null"},"error":{"oneOf":[{"type":"string"},{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["message"]}]}},"required":["data","error"]},"VerifiedNumber":{"type":"object","description":"A phone number this account has proven it controls; a permitted `pstn` test destination.","properties":{"id":{"type":"string","format":"uuid"},"number":{"type":"string","description":"Canonical E.164.","examples":["+15045204977"]},"method":{"type":"string","enum":["voice_code"],"description":"How control was proven."},"label":{"type":["string","null"]},"verifiedAt":{"type":"string","format":"date-time"}},"required":["id","number"]},"OutcomeAssertionInput":{"type":"object","description":"Register/patch an outcome assertion. `endpoint` must be a public https URL.","properties":{"suiteId":{"type":"string","format":"uuid","description":"The suite this assertion is attached to (required on create)."},"scenarioName":{"type":["string","null"],"description":"Only run for this scenario (persona name). Null = every scenario."},"name":{"type":"string"},"endpoint":{"type":"string","format":"uri","examples":["https://hooks.acme.com/wb-outcome"]},"method":{"type":"string","enum":["POST"]},"expected":{"type":"object","description":"status + JSONPath equality/existence checks. Values may interpolate {{scenario.*}}.","properties":{"status":{"type":"integer"},"json_path":{"type":"object","additionalProperties":true,"examples":[{"$.booking.status":"confirmed","$.booking.date":"{{scenario.requested_date}}"}]}}},"priceCheck":{"type":["object","null"],"description":"Compare the agent quote against a price list.","properties":{"quoted_field":{"type":"string","description":"JSONPath into the response for the recorded amount. Absent ⇒ extract from the transcript."},"source":{"type":"string","examples":["price_list_ref:<priceListId>"]},"service_key":{"type":"string","description":"Which list entry to expect. Default {{scenario.requested_service}}."},"tolerance":{"type":"number"}},"required":["source"]},"timeoutMs":{"type":"integer","description":"Per-request timeout, capped at 10000."},"retries":{"type":"integer","description":"Retry attempts on an unreachable/5xx call, capped at 3."},"enabled":{"type":"boolean"}}},"OutcomeAssertion":{"allOf":[{"$ref":"#/components/schemas/OutcomeAssertionInput"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"trigger":{"type":"string","const":"post_call"},"createdAt":{"type":"string","format":"date-time"}},"required":["id"]}]},"PriceList":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"currency":{"type":"string","examples":["USD"]},"entries":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"price":{"type":"number"}}}}},"required":["id","name"]},"OutcomeResult":{"type":"object","description":"One assertion evaluated against one run. Stores match RESULTS only — never the raw response body.","properties":{"assertionId":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string","enum":["pass","fail","inconclusive","pending"]},"reason":{"type":["string","null"]},"checks":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["assertionId","status"]},"RecordingTargetInput":{"type":"object","description":"Where BYOS recordings go. `callbackUrl` must be a public https URL (SSRF-validated at registration): wordis-bond POSTs it for a presigned PUT URL per recording, so wordis-bond never holds your cloud credentials.","properties":{"name":{"type":"string"},"model":{"type":"string","const":"presigned_callback"},"callbackUrl":{"type":"string","format":"uri","examples":["https://hooks.acme.com/wb-recording"]},"track":{"type":"string","enum":["inbound","outbound","both"],"default":"inbound"},"enabled":{"type":"boolean","default":true}},"required":["name","callbackUrl"]},"RecordingTarget":{"allOf":[{"$ref":"#/components/schemas/RecordingTargetInput"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}},"required":["id"]}]},"MonitorInput":{"type":"object","description":"A production monitor. `scenarioGoal` (+ optional `assertions` / `rubric`) is what the judge scores each call against — the same inputs a test suite uses. `sampleRate` and the plan monthly cap bound cost.","properties":{"name":{"type":"string","examples":["Support line — main agent"]},"scenarioGoal":{"type":"string","examples":["Answer billing questions accurately and book a callback when asked"]},"assertions":{"type":"array","items":{"type":"string"},"description":"Optional natural-language checks the agent must satisfy."},"rubric":{"type":["string","null"],"description":"Optional free-text rubric appended to the judge instructions."},"language":{"type":"string","default":"en"},"enabled":{"type":"boolean","default":true},"sampleRate":{"type":"number","minimum":0,"maximum":1,"default":1,"description":"Fraction of ingested calls to score."},"baselineWindow":{"type":"integer","minimum":2,"maximum":500,"default":20,"description":"Rolling-window size for the health baseline."},"minBaseline":{"type":"integer","minimum":1,"maximum":200,"default":5,"description":"Scored events required before drift can fire."},"alertEmail":{"type":"boolean","default":true},"alertWebhookUrl":{"type":["string","null"],"format":"uri","description":"Optional signed outbound alert webhook. Public https, SSRF-validated at set time."}},"required":["name","scenarioGoal"]},"Monitor":{"allOf":[{"$ref":"#/components/schemas/MonitorInput"},{"type":"object","description":"A monitor as returned — never includes the ingest secret (read it from /secret).","properties":{"id":{"type":"string","format":"uuid"},"health":{"type":"string","enum":["unknown","healthy","degraded","critical"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","health"]}]},"MonitorIngestInput":{"type":"object","description":"A completed production call to score. The transcript is scored in-flight and never stored.","properties":{"transcript":{"type":"string","description":"Formatted \"AGENT: … / CALLER: …\" transcript."},"externalId":{"type":["string","null"],"description":"Your own call id — used to dedupe re-delivery."},"platform":{"type":["string","null"],"examples":["retell","vapi","telnyx"]},"durationSec":{"type":["integer","null"]},"occurredAt":{"type":["string","null"],"format":"date-time"}},"required":["transcript"]},"MonitorEvent":{"type":"object","description":"One scored production call — metadata + verdict only, no transcript.","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","scored","skipped","failed"]},"reason":{"type":["string","null"]},"source":{"type":"string","enum":["api","webhook","wb"]},"platform":{"type":["string","null"]},"score":{"type":["integer","null"]},"passed":{"type":["boolean","null"]},"judgeVersion":{"type":["string","null"]},"drift":{"type":["string","null"],"enum":["none","score_drop","pass_to_fail",null]},"durationSec":{"type":["integer","null"]},"occurredAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","status"]},"Plan":{"type":"string","enum":["starter","pro","enterprise"]},"PlanView":{"type":"object","description":"One plan row from the entitlements source of truth. Prices are always read from here.","properties":{"plan":{"$ref":"#/components/schemas/Plan"},"priceUsdPerMonth":{"type":"number"},"purchasable":{"type":"boolean"},"lookupKey":{"type":["string","null"]},"callMinutesPerMonth":{"type":"integer"},"allowedTransports":{"type":"array","items":{"type":"string","enum":["direct","pstn"]}},"overageUsdPerMinute":{"type":"object","properties":{"pstn":{"type":["number","null"]},"direct":{"type":["number","null"]}}},"ttsCharsPerMonth":{"type":"integer"},"concurrentCalls":{"type":"integer"},"scheduledRuns":{"type":"object","properties":{"cadence":{"type":"string","enum":["weekly","daily","hourly"]},"maxSuites":{"type":["integer","null"]}}},"apiKeys":{"type":["integer","null"]},"requiresCard":{"type":"boolean"}},"required":["plan","priceUsdPerMonth","callMinutesPerMonth","allowedTransports"]},"PartnerProgram":{"type":"object","description":"Operator-first workspace ladder. The operator bills and owns the end customer; Word Is Bond bills one wholesale fee per active workspace. An evaluation slot is one bounded transcript scored once.","properties":{"platformShareCapPercent":{"type":"number","maximum":25},"operatorShareFloorPercent":{"type":"number","minimum":75},"retailMerchant":{"type":"string","enum":["operator"]},"platformBilling":{"type":"string","enum":["per_active_workspace"]},"usageUnit":{"type":"string","enum":["evaluation_slot"]},"offers":{"type":"array","items":{"type":"object","properties":{"tier":{"type":"string","enum":["watch","protect","assure"]},"wholesaleUsdPerMonth":{"type":"number"},"minimumRetailUsdPerMonth":{"type":"number"},"operatorKeepsUsdPerMonthAtMinimum":{"type":"number"},"cogsBudgetUsdPerMonth":{"type":"number"},"voiceMinutesPerMonth":{"type":"integer"},"evaluationSlotsPerMonth":{"type":"integer"}},"required":["tier","wholesaleUsdPerMonth","minimumRetailUsdPerMonth","operatorKeepsUsdPerMonthAtMinimum","cogsBudgetUsdPerMonth","voiceMinutesPerMonth","evaluationSlotsPerMonth"]}}},"required":["platformShareCapPercent","operatorShareFloorPercent","retailMerchant","platformBilling","usageUnit","offers"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"keyPrefix":{"type":"string","description":"First 12 chars, e.g. \"wb_live_ab12\"."},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","keyPrefix"]},"TargetAgent":{"type":"object","description":"The voice agent under test (a system YOU control). `direct` = SIP/WebRTC (near-zero cost, all plans); `pstn` = a real carrier call (pro+). The hosted demo target is `{transport:\"direct\", peerId:\"wb-demo-dental\"}`.","properties":{"transport":{"type":"string","enum":["pstn","webrtc","direct","sip"]},"toNumber":{"type":"string","description":"E.164 number to dial (required for a pstn target)."},"peerId":{"type":"string","description":"Hosted-agent id (e.g. \"wb-demo-dental\")."}},"required":["transport"]},"Scenario":{"type":"object","description":"One persona + goal the suite exercises.","properties":{"persona":{"oneOf":[{"type":"string"},{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}]},"goal":{"type":"string"},"expected":{"type":"array","items":{"type":"string"}}}},"ScoreTranscriptRequest":{"type":"object","title":"Offline scoring","description":"Score a captured transcript with the judge. Provide `transcript` OR `turns`.","properties":{"transcript":{"type":"string"},"turns":{"type":"array","description":"Per-turn capture; formatted into a transcript when `transcript` is omitted.","items":{"type":"object","properties":{"role":{"type":"string","enum":["agent","caller"]},"text":{"type":"string"}}}},"scenarioGoal":{"type":"string","description":"What the conversation was meant to accomplish."},"goal":{"type":"string","description":"Alias for scenarioGoal."},"assertions":{"type":"array","items":{"type":"string"}},"suiteId":{"type":["string","null"],"format":"uuid"},"language":{"type":"string","default":"en"},"transport":{"type":"string","enum":["pstn","webrtc"],"default":"webrtc"}}},"LiveRunRequest":{"type":"object","title":"Live synthetic call","description":"Drive a live synthetic caller against a target agent you run.","properties":{"targetAgent":{"$ref":"#/components/schemas/TargetAgent"},"goal":{"type":"string","description":"What the synthetic caller is trying to accomplish."},"persona":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["description"]},"suiteId":{"type":["string","null"],"format":"uuid"},"transport":{"type":"string","enum":["pstn","webrtc"]},"language":{"type":"string","default":"en"},"concurrency":{"type":"integer","minimum":1,"maximum":25,"default":1},"expected":{"type":"array","items":{"type":"string"}},"bargeIn":{"type":"boolean"},"record":{"type":"boolean","default":false,"description":"Opt this run into BYOS recording to your own storage (default off). Requires the byosRecording capability (pro/enterprise) and an enabled recording target. The customer-owned pointer comes back in the run’s recording_url and the report."},"recordTargetId":{"type":"string","format":"uuid","description":"Record to THIS target (else the most-recent enabled one)."}},"required":["targetAgent"]},"TestSuite":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"name":{"type":"string"},"targetAgent":{"$ref":"#/components/schemas/TargetAgent"},"language":{"type":"string"},"scenarios":{"type":"array","items":{"$ref":"#/components/schemas/Scenario"}},"schedule":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","targetAgent"]},"TestRun":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"suiteId":{"type":["string","null"],"format":"uuid"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"transport":{"type":"string"},"language":{"type":"string"},"persona":{"type":["object","null"]},"targetAgent":{"oneOf":[{"$ref":"#/components/schemas/TargetAgent"},{"type":"null"}]},"score":{"type":["integer","null"],"minimum":0,"maximum":100},"passed":{"type":["boolean","null"]},"scorecard":{"oneOf":[{"$ref":"#/components/schemas/Scorecard"},{"type":"null"}]},"metrics":{"oneOf":[{"$ref":"#/components/schemas/RunMetrics"},{"type":"null"}]},"transcript":{"type":["string","null"]},"recordingUrl":{"type":["string","null"],"description":"BYOS recording pointer — a URL into YOUR OWN storage when the run recorded (never wordis-bond storage). Null unless the run opted in with `record: true`. See metrics.recording for the tee state (stored/truncated/failed)."},"mediaWebSocketUrl":{"type":"string","description":"Only on a live `direct` run — the tokenized WS your agent-side harness dials."},"startedAt":{"type":["string","null"],"format":"date-time"},"endedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","status"]},"Scorecard":{"type":"object","description":"The judged result. `score` is the 0–100 overall; `layers` carry per-dimension detail.","properties":{"score":{"type":"number","minimum":0,"maximum":100},"passed":{"type":"boolean"},"summary":{"type":"string"},"sentiment":{"type":"number","minimum":-1,"maximum":1},"taskCompletion":{"type":"number","minimum":0,"maximum":1},"layers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"score":{"type":"number","minimum":0,"maximum":100},"passed":{"type":"boolean"},"rationale":{"type":"string"},"assertions":{"type":"array","items":{"type":"object"}}}}}},"required":["score","passed"]},"RunMetrics":{"type":"object","properties":{"latencyMs":{"type":"object","properties":{"p50":{"type":"number"},"p95":{"type":"number"},"max":{"type":"number"}}},"wer":{"type":["number","null"],"minimum":0,"maximum":1,"description":"Script-adherence error across pinned scenario wording, or null if nothing was pinned. This is not transcription word-error rate unless a caller supplies an STT reference transcript."},"bargeInCount":{"type":"integer"},"turnCount":{"type":"integer"},"taskCompletion":{"type":"number","minimum":0,"maximum":1},"sentiment":{"type":"number","minimum":-1,"maximum":1}}},"TrendPoint":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"score":{"type":["integer","null"]},"passed":{"type":["boolean","null"]},"at":{"type":["string","null"],"format":"date-time"}}},"RegressionPoint":{"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"at":{"type":["string","null"],"format":"date-time"},"reason":{"type":"string","enum":["pass_to_fail","score_drop"]},"from":{"type":["number","null"]},"to":{"type":["number","null"]}}},"SuiteTrend":{"type":"object","properties":{"suiteId":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"runs":{"type":"integer"},"passed":{"type":"integer"},"failed":{"type":"integer"},"passRate":{"type":["number","null"]},"avgScore":{"type":["number","null"]},"regressions":{"type":"integer"}}},"SuiteTrendSeries":{"type":"object","properties":{"suiteId":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"points":{"type":"array","items":{"$ref":"#/components/schemas/TrendPoint"}},"regressions":{"type":"array","items":{"$ref":"#/components/schemas/RegressionPoint"}},"summary":{"$ref":"#/components/schemas/SuiteTrend"}}},"AccountTrends":{"type":"object","properties":{"window":{"type":"string","examples":["90d"]},"totals":{"type":"object","properties":{"suites":{"type":"integer"},"runs":{"type":"integer"},"passed":{"type":"integer"},"failed":{"type":"integer"},"passRate":{"type":["number","null"]},"avgScore":{"type":["number","null"]},"regressions":{"type":"integer"}}},"suites":{"type":"array","items":{"$ref":"#/components/schemas/SuiteTrend"}}}}}}}