Skip to Content
API conventions

API conventions

Every versioned developer endpoint (/v1/*) follows this contract. Module pages only document behavior that differs from these defaults.

Naming and types

Requests and responses deliberately use different casing:

LocationConventionExample
JSON request fieldssnake_casecustomer_phone
Query parameterssnake_casestore_id
JSON response fieldscamelCasecustomerPhone
Status valueslowercase snake_casein_progress
Other enum valuesExact documented stringPIECE, outbound
Event typeslowercase dotted namesvox.call.completed
HTTP headersstandard header casingIdempotency-Key

Common scalar types are described consistently throughout these docs:

TypeWire representation
stringJSON string
booleanJSON true or false; never 0 or 1
integerJSON number without a fractional part
numberJSON number
uuidHyphenated UUID string
datetimeUTC ISO 8601 string, for example 2026-07-14T10:15:30.000Z
dateCalendar date in YYYY-MM-DD format
moneyJSON number in the currency’s major unit unless the field says otherwise
enumOne of the values explicitly listed for the field

Optional fields may be omitted from a request. Nullable response fields are present with null when the value is known to be absent. Do not send an empty string in place of null.

Success responses

Single-resource and action responses use:

{ "message": "Contact created", "data": { "id": "6c68ea57-8a2e-44d1-b42f-1ab3b5241e1d" } }

Collection responses place the array in data. Paginated collections also include meta. An action with no resource to return may omit data.

FieldTypePresenceDescription
messagestringUsuallyHuman-readable operation summary; do not branch application logic on it.
dataobject | arrayWhen the endpoint returns dataResource or collection.
metaobjectPaginated collectionsPagination metadata.

Error responses

All /v1/* errors use the same machine-readable envelope:

{ "error": "validation_error", "message": "The customer_phone field must be defined", "details": [ { "field": "customer_phone", "message": "The customer_phone field must be defined" } ] }
FieldTypePresenceDescription
errorstringAlwaysStable lowercase snake_case code for program logic.
messagestringAlwaysHuman-readable summary for logs or UI.
detailsarrayValidation errors onlyField-level validation failures.
metaobjectWhen relevantExtra recovery information, such as retryAfter.

HTTP statuses

StatusMeaningTypical error code
200 OKRead, update, delete, or action completedNot applicable
201 CreatedResource or asynchronous operation createdNot applicable
400 Bad RequestRequest cannot be processed as submittedrequest_failed, invalid_json
401 UnauthorizedAPI key missing, invalid, or expiredinvalid_api_key, expired_api_key
403 ForbiddenKey is valid but access is deniedapi_access_not_included, missing_scope, ip_not_allowed, origin_not_allowed
404 Not FoundResource is absent or belongs to another businessnot_found
409 ConflictCurrent state conflicts with the operationidempotency_key_reused, request_in_progress
422 Unprocessable EntityOne or more fields failed validationvalidation_error
429 Too Many RequestsRate limit exceededrate_limit_exceeded
500 Internal Server ErrorUnexpected Cloove failureinternal_error

Resource lookups are business-scoped. A resource belonging to another business returns 404, not 403, so its existence is not disclosed.

Pagination

Paginated endpoints accept page and limit. Defaults and maximum values are listed on the endpoint when they differ by module.

GET /v1/vox/calls?page=2&limit=20
{ "message": "Vox calls retrieved", "data": [], "meta": { "total": 137, "page": 2, "perPage": 20, "totalPages": 7, "hasMore": true } }
FieldTypeDescription
totalintegerTotal matching resources.
pageintegerCurrent one-based page.
perPageintegerResources requested per page.
totalPagesintegerTotal available pages.
hasMorebooleanWhether another page exists.

Idempotency

Send an Idempotency-Key header on resource-creating POST requests. Use a UUID or another unique value for each logical operation and reuse it only when retrying that same operation.

Idempotency-Key: 9b2e1c7a-1f3d-4c2a-b8e1-7d6f5a4c3b2a
  • Same key and same request body: the original response is replayed with Idempotency-Replayed: true.
  • Same key and different body: 409 idempotency_key_reused.
  • Same key while the original request is still running: 409 request_in_progress.
  • Keys are retained for 24 hours.

Some older create endpoints also accept idempotency_key in the body. Prefer the header; it is the canonical mechanism and works consistently across modules.

Rate limits and retries

Developer API keys receive 120 requests per minute per key. A 429 response includes a Retry-After header in seconds and the same value in meta.retryAfter when available.

Retry only requests that are safe to repeat:

  • Retry 429 and transient 5xx responses with exponential backoff and jitter.
  • Honor Retry-After when present.
  • Retry writes only when they carry an Idempotency-Key.
  • Do not automatically retry other 4xx responses.

Timeouts have an unknown outcome. Treat them like transient failures: retry an idempotent request with the same key instead of creating a new operation.

Compatibility

The major version is part of the path. Additive changes - new optional fields, new endpoints, and new event types - may ship within v1. Breaking field removals, type changes, or semantic changes require a new major version. Clients should ignore response fields they do not use.

Last updated on