Requests and responses
Every request and response follows the same conventions. This page specifies content types, naming, the shape of single objects and lists, request ids and the headers returned on every response.
JSON only
- Request and response bodies are JSON, UTF-8. Send
Content-Type: application/jsonwith every body; any other type answersunsupported_media_type, and a body that does not parse answersinvalid_json. - An operation that documents no request body (for example pausing a campaign) ignores a body sent with it: it is not read, so it is never refused as
415,413orinvalid_json. HEADis accepted whereverGETis and answers the same status and headers with no body.- Field names, query parameters and enum values are
snake_case. - Request bodies are strict. A field the operation does not accept is
validation_error, never silently ignored, so a typo cannot go unnoticed. Unknown query parameters are refused the same way, and each query parameter may be given once. - Request bodies are limited to 1 MB (
payload_too_large). Some fields have their own limits, documented in the API reference. GETrequests have no body. Build JSON with a JSON library, not by concatenating strings.
validation_error lists every problem it found, not just the first:
JSON
Request headers
| Header | When |
|---|---|
Authorization: Bearer <key> | Every request. See Authentication. |
Content-Type: application/json | Every request with a body. |
Idempotency-Key | Optional on POST and DELETE: makes a retry safe. See Idempotency. |
If-Match | Required on PATCH of campaigns and contacts. See Concurrency. |
Range | Optional, when downloading recording audio. |
Objects
A single object is returned as itself, never wrapped. Every object has:
id: an opaque, prefixed id such ascall_6Vb3Nm8Qw2Er5Ty9Ui4Op7;object: its type, such ascall,contactorcampaign;livemode:truefor live mode,falsefor test mode.
Fields are always present in responses: a value that is not known or not set is null, not missing. A delete returns a small confirmation object:
JSON
Lists
Every list operation returns the same envelope:
JSON
data holds full objects (a call in a list is the same as from GET/calls/{call_id}). See Pagination and Filtering.
Status codes
| Status | Meaning |
|---|---|
200 OK | Success. |
201 Created | A new object was created (for example a contact or a campaign). |
202 Accepted | Accepted; the work continues after the response (placing a call, sending a test event, retrying a delivery). Follow the object's status. |
206 Partial Content | A byte range of recording audio. |
400, 413, 415, 422 | The request cannot be done as sent; fix it. |
401, 403 | Authentication or permission problem. |
402 | Balance or spend limit. |
404 | No such object for this key, or no such route. |
405 | Method not supported on this path. |
409 | Conflict with the current state, or an idempotency conflict. |
410 | A recording link has expired. |
412, 428 | If-Match stale or missing. |
429 | Rate limit, daily call limit or calling capacity. |
500, 503 | A problem on our side. |
Every error has the same envelope and a stable code; the status of each code never changes. See Error handling.
Request ids
Every response, success or error, has an X-Request-Id header:
Text
Error bodies repeat it as error.request_id (a replayed answer, marked Idempotent-Replayed: true, can carry the original request's id in the body; the header is always this request's). The id is generated by RelationsAI for each request; a value you send in an X-Request-Id request header is ignored. Log it with your own correlation id for every request, and quote it when you write to support: it is how we find your request.
Response headers
| Header | On | Meaning |
|---|---|---|
X-Request-Id | Every response | The request id. |
RateLimit-Limit | Every response | Requests allowed in the current one-minute window. |
RateLimit-Remaining | Every response | Requests left in the window. |
RateLimit-Reset | Every response | Seconds until the window resets. |
Retry-After | 429, 503 and errors whose retry is after | Seconds to wait before retrying. |
Idempotent-Replayed | Replayed POST and DELETE responses | true when the response is the stored result of an earlier request with the same key. |
ETag | Campaigns and contacts | The version to send as If-Match. |
Allow | 405 | The methods the path supports. |
See Rate limits. Browser code (from origins the API allows) can read X-Request-Id, the RateLimit-* headers, Retry-After, Idempotent-Replayed and ETag; the other headers are for server-side clients.
Asynchronous operations
Calls and campaigns run after the request returns. POST /v1/calls answers 202 with the call queued. Starting a campaign answers 200 when the run has started, or 202 when the start was sent and is still being confirmed. Follow progress with webhooks or by reading the object, and treat the object's own status as the truth.