TVH Voice docs

#API conventions

#Base URL and versioning

All endpoints live under /v1. Additive changes (new fields, endpoints, event types) happen without a version change — ignore unknown fields. Breaking changes would ship as /v2 next to /v1. Webhook payloads carry version (currently 1); an endpoint receives the version it was created with.

#Authentication and scopes

Authorization: Bearer vk_<id>_<secret>. Keys are created in the dashboard or with POST /v1/api-keys (shown once, stored hashed), rotated with POST /v1/api-keys/{id}/rotate and revoked with DELETE /v1/api-keys/{id}.

Scope Allows
calls:read / calls:write Call log, workspace & usage / start and control calls, delete recordings
agents:read / agents:write Agents / create, change, delete agents and issue softphone configs
numbers:read / numbers:write Numbers, orders, documents / order, release, upload
carriers:read / carriers:write Carrier configuration
routing:read / routing:write Inbound rules and ring groups
webhooks:read / webhooks:write Webhook endpoints and deliveries
events:read Event list and live stream
recordings:read Signed recording URLs
admin Everything, plus API keys, limits and the audit log

#Requests and responses

#Errors

{ "error": { "code", "message", "details"?, "request_id" } } — see errors.md for all codes. Validation errors list the failing fields in details.issues.

#Pagination

List endpoints that can grow (/v1/calls, /v1/events, /v1/webhook-deliveries, /v1/audit-log) take limit (1–200, default 50) and return next_cursor; pass it as cursor until it is null.

#Idempotency

Send Idempotency-Key: <unique string, 8–200 chars> on any POST. The first response is stored for 24 hours and replayed (header idempotent-replayed: true) for retries with the same key and body. Reusing a key with a different body returns 409 IDEMPOTENCY_KEY_REUSED; a retry while the first request is still running returns 409 CONFLICT. Server errors (5xx) are not stored, so you can retry them with the same key. Always use a key for POST /v1/calls — it prevents double dialing.

#Rate limits

Limit Value
All /v1 endpoints, per API key (or per IP without a key) 600 requests / minute
POST /v1/calls, per API key 120 requests / minute
Dashboard sign-in, per IP 10 attempts / minute

Responses carry x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset; a 429 carries Retry-After. Call volume is additionally capped by the workspace limits (concurrent calls, calls per hour/day, spend guard), which return 429 LIMIT_EXCEEDED / SPEND_GUARD_TRIPPED.

#Capabilities

Carriers differ (a generic SIP trunk has no number API). GET /v1/carriers/{id} returns capabilities; hide features the carrier lacks. Calling them anyway returns 422 CAPABILITY_NOT_SUPPORTED.