TVH Voice docs

#Integration guide for AI coding agents

You are integrating calling into a CRM using the Voice Infrastructure Platform. Work through this checklist in order. Each step names the endpoint(s), the recipe with tested code, and how to verify the step before moving on. Everything here also works in the sandbox (mock carrier + simulated phone network), so build and test there first.

Machine-readable references:

#0. Decisions to ask the human about first

Do not guess these; ask, then record the answers in your project notes.

  1. Base URL and API key. Production and sandbox base URLs. Which scopes the key needs (default: calls:*, agents:*, numbers:read, routing:*, webhooks:*, events:read, recordings:read). The key lives in server-side config (env var), never in browser code.
  2. Agent mapping. Which CRM entity is an agent (user id?) → used as agent_ref.
  3. Who answers inbound calls. One agent, a team (ring group), voicemail, or "the CRM decides per call" (webhook-decides, 2-second answer window with a fallback rule).
  4. Caller ID. Which number each user calls from (per user, per team, or one company number).
  5. Recording. Off / all / inbound / outbound; consent announcement on or off; retention days. This is a legal decision (GDPR, local consent law) — the human must decide.
  6. Softphone location. Embedded browser softphone (recommended), desk phones/apps (long-lived SIP password), or both.
  7. Which carrier (production only) and who creates its account and API key. The platform never needs the human's carrier password, only an API key/credentials for a dedicated key.
  8. Destinations and limits. Allowed destination countries, max concurrent calls, calls per hour/day, spend-guard minutes (toll-fraud protection — the customer pays the carrier).

#1. Authenticate

#2. Connect a carrier (usually done once by the human in the dashboard)

#3. Create agents and embed the softphone

#4. Numbers and regulatory documents

#5. Click-to-call

#6. Webhooks (store call activity in the CRM)

#7. Inbound routing

#8. Call logs and recordings

#9. Go-live test (run it, report the results to the human)

  1. Sandbox: steps 1–8 pass against the sandbox (docker compose up in the platform repo, see /docs/sandbox.md).
  2. Production carrier connected and healthy; at least one active number.
  3. Agent registers from the CRM in a real browser (microphone allowed).
  4. Click-to-call to a real mobile phone: both hear each other; call.ended stored in the CRM.
  5. Call the number from a mobile phone: the configured agent/group rings; answer; hang up.
  6. Webhook signature verification rejects a modified body (send one manually).
  7. If recording is on: recording.ready received, playback works, consent message heard.
  8. Limits: a call to a non-allowed country is refused with DESTINATION_NOT_ALLOWED.

#10. Switching carriers later

Configuration only — no CRM change:

  1. Connect the new carrier (POST /v1/carriers); both can be active at the same time.
  2. Move numbers (port them outside the platform, or order new numbers at the new carrier). Outbound calls always use the carrier of the chosen caller ID, so you migrate per number.
  3. Optionally set failover_carrier_id on the old carrier during the migration.
  4. Revoke the old carrier (POST /v1/carriers/{id}/revoke). Never reference carrier/provider names in CRM code: use carrier_id and capabilities only. Recipe: /docs/recipes/09-switch-carriers.md.