#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
- JSON in and out,
snake_casefields, timestamps in ISO 8601 UTC, phone numbers in E.164 (+31201234567). - Path ids are UUIDs; agents can also be addressed by
agent_ref, numbers by E.164. provider_extrasfields are opaque carrier data — never build logic on them.
#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.