# Word Is Bond > Testing for voice agents you run. Point a synthetic caller at an agent you control > (on Vapi, Retell, ElevenLabs, LiveKit, Pipecat, Bland, or any SIP / WebRTC / PSTN > endpoint) and get back a scored transcript, a pass/fail verdict, and per-turn latency, > word-error-rate, and barge-in metrics. Score a captured transcript, 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. Site: https://wordis-bond.com · API: https://api.wordis-bond.com ## Get a key (self-serve, no human involved) 1. Sign up at the Studio: https://studio.wordis-bond.com — the free Starter plan needs no card. 2. Create an API key: POST https://api.wordis-bond.com/api/api-keys with JSON {"name": "my agent"} while signed in. The raw token (wb_live_…) is returned exactly once and is never stored in plaintext — save it. Manage or revoke keys any time. GET https://api.wordis-bond.com/api/billing/plans is the authoritative plan, quota, and price table — read it rather than scraping the pricing page. https://wordis-bond.com/pricing renders the same ratified tiers as static markup and upgrades itself from that endpoint when it is reachable, so the endpoint wins on any disagreement. Upgrade/downgrade is self-serve in the Studio. ## Auth Send your API key as a bearer token on every /api request: Authorization: Bearer wb_live_... Only a SHA-256 hash of your key is stored. Every response is JSON of the shape {"data": ..., "error": ...} — exactly one side is non-null. Errors use proper status codes: 401 (missing/invalid key), 402 (plan doesn't permit / quota spent), 422 (validation), 429 (rate/spend limit). ## First scored result in ~60s (no target of your own) With a key, run the hosted demo voice agent (a dental front desk) and get a real, fully-scored result — no agent of your own required: curl -X POST https://api.wordis-bond.com/api/tests/demo \ -H "Authorization: Bearer wb_live_..." # → { "data": { "score": 0-100, "passed": bool, "scorecard": {...}, # "metrics": {...}, "transcript": "...", # "reportUrl": "https://api.wordis-bond.com/r/{id}/{token}" } } ## Live endpoints (all under https://api.wordis-bond.com) - GET /health — service health, no auth. - GET /api/billing/plans — plan + entitlement table (no auth). The single source of truth for pricing and quotas. - POST /api/tests/demo — run the hosted demo agent and get a scored run + a shareable report URL. Body optional: {"persona":{"description"},"goal","language"}. - POST /api/tests/run — run a test against a voice agent you run. Two modes: • Score a captured transcript offline: {"transcript" or "turns", "scenarioGoal", "assertions"?, "language"?} → a completed, scored run synchronously. • Run a live synthetic call: {"targetAgent":{"transport","toNumber"?,"peerId"?}, "goal","persona"?,"transport"?,"language"?,"concurrency"?}. A "direct" (SIP/WebRTC) target returns a tokenized media WebSocket URL your agent-side harness dials; a "pstn" target places a real carrier call (pro+ plans) — and only to a number you have verified (see below), else 403 NUMBER_NOT_VERIFIED. Poll GET /api/tests/{id}. - POST /api/tests/{id}/score — (re)score a run's transcript with the judge. - GET /api/tests — your most recent runs (up to 100). - GET /api/tests/{id} — one run; poll here for the terminal scored state. - GET /api/tests/suites — your reusable suites. POST /api/tests/suites — create one: {"name","targetAgent","language"?,"scenarios"?,"schedule"?}. - GET /api/tests/suites/{id} — one suite. - GET /api/tests/suites/{id}/trends — per-suite time series + regression flags. - GET /api/tests/trends?days=90 — account-wide pass-rate, average score, regressions. - POST /api/numbers/verify/start — prove you control a phone number. Body {"number"}. Word Is Bond calls it once, speaks a 6-digit code twice, and hangs up. → 202. - POST /api/numbers/verify/confirm — Body {"number","code"}. → 201, the number is verified. - GET /api/numbers — your verified numbers. DELETE /api/numbers/{id} — revoke one. - POST /api/outcome/assertions — register a post-call assertion on a suite: after a scored run Word Is Bond POSTs a signed request to your endpoint and checks the business result landed. Body {"suiteId","name","endpoint","expected"?,"priceCheck"?,"scenarioName"?}. The endpoint must be a public https URL. GET lists them; PUT/DELETE /api/outcome/assertions/{id}. - GET /api/outcome/secret — your assertion signing secret + how to verify the wb-signature. - GET/POST /api/outcome/price-lists — price lists the price matcher compares an agent quote against. - GET /api/outcome/runs/{runId} — the outcomes (pass / fail / inconclusive) for one run. - POST /api/recording/targets — BYOS recording: register where recordings go, then add "record":true to POST /api/tests/run to tee that call's audio to YOUR OWN storage. Word Is Bond keeps only a pointer (the run's recording_url), never the audio. Body {"name","callbackUrl","track"?}; callbackUrl is a public https endpoint that returns a presigned PUT URL per recording. GET lists them; PUT/DELETE /api/recording/targets/{id}. Pro/enterprise capability; bundled free (you pay your own storage). Starter → 402. - GET /api/recording/secret — your BYOS signing secret + how to verify the wb-signature. - POST /api/flows — put a phone-system flow under version control: {"name","ir",...} saves the Flow IR as an immutable, content-hashed version 1. GET /api/flows lists them. - POST /api/flows/import — import a platform config (e.g. Vapi) into the diffable Flow IR: {"platform":"vapi","config":{...},"save"?}. Export it back with POST /api/flows/{id}/export. - POST /api/flows/{id}/versions — save a new immutable version and get the structural diff. - GET /api/flows/{id}/diff?from=&to= — the added/removed/changed nodes + edges between versions. - POST /api/flows/{id}/test — run the regression gate: a behavior change that regressed vs the previous version's baseline is caught and blocks the change. GET /api/flows/{id}/gate reads it. - POST /api/monitor — create a production monitor: {"name","scenarioGoal","assertions"?,...}. A monitor watches ONE live production line. GET /api/monitor lists them with their health. Pro/enterprise capability; starter → 402. - POST /api/monitor/{id}/events — send one COMPLETED production call to be scored: {"transcript","externalId"?,"platform"?,"durationSec"?}. Returns 202; scoring is deferred. Pass externalId (your own call id) and a re-delivered call is scored exactly once. - GET /api/monitor/{id}/health — live rolling health: unknown | healthy | degraded | critical, plus the drift verdict and the baseline it was compared against. - GET /api/monitor/{id}/series — the score/pass time series, for charting. - POST /api/monitor/ingest — the same ingest, HMAC-signed instead of key-authed, for platform relays that cannot hold an API key. Sign with the monitor's wbms_ secret; see /secret. - GET /api/reports/{id}/link — the shareable public report URL for one of your runs. - GET /api/billing/subscription — your current plan, usage, and entitlements. - POST /api/api-keys · GET /api/api-keys · DELETE /api/api-keys/{id} — manage keys. Public, tokenized report pages (no auth, not enumerable) render at https://api.wordis-bond.com/r/{id}/{token}. ## Testing vs monitoring Testing answers "will this agent behave before I ship it?" — you run a synthetic caller and get a scored verdict. Monitoring answers "is it still behaving in production, right now?" — you stream completed real calls to a monitor and the SAME versioned judge scores each one, tracks a rolling baseline, and alerts when quality drifts. Two properties matter, and they are guarantees, not defaults: 1. The transcript you send is scored in flight and NEVER stored. Only the scorecard and safe metadata are persisted. There is no transcript column. 2. Drift is isolated by judge version. Our judge is versioned and rides a provider fallback chain, so it can move underneath you. A score delta across that boundary says nothing about your agent, so it is reported as judgeVersionChanged and never counted as a regression. A verdict produced by a degraded judge (every provider tier unavailable) is recorded as skipped, never as a failing call. ## Ownership boundary Word Is Bond tests systems you control. On every self-serve plan it never places outbound calls to third parties: the dialer, campaigns, bulk-blast, and A/B-launch endpoints are not reachable and return 403 { "data": null, "error": { "code": "OUTREACH_NOT_AVAILABLE", "hint": "..." } } No upgrade buys that capability — there is no checkout link on that denial. It is enabled only by a human, per account, after review. A PSTN test call is only placed to a number your account has PROVEN it controls, or to a Word Is Bond DID. Any other destination returns: 403 { "data": null, "error": { "code": "NUMBER_NOT_VERIFIED", "hint": "..." } } Prove control once, then test freely: curl -X POST https://api.wordis-bond.com/api/numbers/verify/start \ -H "Authorization: Bearer wb_live_..." -H "content-type: application/json" \ -d '{"number":"+15045204977"}' # → 202; we call that number and speak a 6-digit code twice, then hang up. curl -X POST https://api.wordis-bond.com/api/numbers/verify/confirm \ -H "Authorization: Bearer wb_live_..." -H "content-type: application/json" \ -d '{"number":"+15045204977","code":"135790"}' # → 201 { "data": { "verified": true } } The code is stored only as a salted hash, is single-use, expires in 10 minutes, and locks after 5 wrong guesses. Verification calls are capped at 3 per number and 5 distinct numbers per account per day. Revoke any time with DELETE /api/numbers/{id}. ## Outcome Verification — did the business result land? A scored transcript tells you the agent SOUNDED right. An outcome assertion tells you it actually worked: after a run, Word Is Bond calls an endpoint you register and checks the booking is really in your calendar, or the price the agent quoted matches your price list. curl -X POST https://api.wordis-bond.com/api/outcome/assertions \ -H "Authorization: Bearer wb_live_..." -H "content-type: application/json" \ -d '{"suiteId":"","name":"booking landed","endpoint":"https://hooks.you.com/verify", "expected":{"status":200,"json_path":{"$.booking.status":"confirmed", "$.booking.date":"{{scenario.requested_date}}"}}}' Each result is pass (endpoint reachable, everything matched), fail (a matcher mismatched — the booking is missing or the price is wrong), or inconclusive (your endpoint was unreachable — reported separately so a flaky endpoint never looks like an agent failure). Every request Word Is Bond sends carries wb-signature and wb-timestamp headers: verify hex(HMAC-SHA256(secret, `${wb-timestamp}.${rawBody}`)) against wb-signature (constant time) and reject a timestamp over 5 minutes old. Read your secret at GET /api/outcome/secret. Word Is Bond only calls public https endpoints — internal, loopback, link-local, and cloud-metadata hosts are refused. ## Flow CI — version-control + a regression gate for your phone system Contact-center flows (menu trees, call routing, voice-agent configs) are business-critical logic that usually has no software lifecycle: no git, no diff, no tests. Flow CI adds one. Compile a flow to the canonical Flow IR — a deterministic, git-serializable node graph (prompt / menu / condition / agent / transfer / tool / hangup). Import one from a platform: curl -X POST https://api.wordis-bond.com/api/flows/import \ -H "Authorization: Bearer wb_live_..." -H "content-type: application/json" \ -d '{"platform":"vapi","config":{...your assistant...},"save":true,"name":"Front desk"}' # → the Flow IR + the fields the IR abstracts away, saved as version 1. Every save is an immutable, content-hashed version — that is the version control. Change the flow, save a new version, and read exactly what changed: curl "https://api.wordis-bond.com/api/flows//diff?from=1&to=2" \ -H "Authorization: Bearer wb_live_..." # → added / removed / changed nodes and edges, human-readable. Then gate the change: Flow CI compiles the new version into a synthetic-caller test, runs it against the flow's target, and compares the result to the previous version's baseline. A pass-to-fail, or a score drop past the threshold, is a regression that blocks the change — continuous integration for phone-system logic. Same judge-version-aware logic as the trends endpoint, so a change in our scoring judge never looks like your flow regressing. curl -X POST https://api.wordis-bond.com/api/flows//test \ -H "Authorization: Bearer wb_live_..." # → { "data": { "run": {...}, "gate": { "status": "pass|fail|baseline", # "gated": bool, "reason": "...", "summary": "..." } } } The same IR exports back to a platform, so it is also a migration surface: import from one platform, export to another. Vapi ships first; Twilio Studio and Amazon Connect are next. ## MCP (Model Context Protocol) — agent-native Remote MCP server (Streamable HTTP transport, JSON-RPC 2.0): https://api.wordis-bond.com/mcp Tools: run_demo, run_test, get_trends, list_suites, create_suite, verify_number_start, verify_number_confirm, list_verified_numbers, register_recording_target, import_flow, diff_flow, test_flow, list_monitors, create_monitor, ingest_call, get_monitor_health. `initialize` and `tools/list` are free (discovery); `tools/call` runs through the same authentication and limits as the REST API — send the same Authorization: Bearer wb_live_... header. A keyless tool call returns a readable error telling you how to get a key. Add to Claude Code / Cursor / any MCP client (mcp.json): { "mcpServers": { "wordis-bond": { "type": "http", "url": "https://api.wordis-bond.com/mcp", "headers": { "Authorization": "Bearer wb_live_..." } } } } Claude Code one-liner: claude mcp add --transport http wordis-bond https://api.wordis-bond.com/mcp \ --header "Authorization: Bearer wb_live_..." ## Machine-readable spec OpenAPI 3.1: https://api.wordis-bond.com/openapi.json (alias: https://api.wordis-bond.com/.well-known/openapi.json) MCP server card: https://api.wordis-bond.com/.well-known/mcp/server-card.json MCP manifest: https://api.wordis-bond.com/.well-known/mcp ## What happens to your call data Call audio is not recorded. Media frames stream through to speech-to-text and are discarded — nothing writes them to disk or object storage. Transcripts and scorecards are stored, because trend and regression comparison needs them; transcripts are deleted 365 days after the run. Audio, transcripts, phone numbers, email addresses, and API tokens are never written to our request logs, product analytics, or error reports — a lint rule and a test suite fail the build if that changes. Export everything with GET /api/account/export; delete everything with DELETE /api/account {"confirm":true}. ## Guides Quickstart / docs: https://wordis-bond.com/docs Pricing: https://wordis-bond.com/pricing Terms: https://api.wordis-bond.com/terms Privacy: https://api.wordis-bond.com/privacy ## Example # 1. sign up (free, no card) at https://studio.wordis-bond.com, then create a key: curl -X POST https://api.wordis-bond.com/api/api-keys \ -H "Authorization: Bearer " -H "content-type: application/json" \ -d '{"name":"agent"}' # 2. run the hosted demo and get a scored result: curl -X POST https://api.wordis-bond.com/api/tests/demo \ -H "Authorization: Bearer wb_live_..."