Skip to content

Response envelope

Every v1 response uses the same success or error envelope, including the nested data.data shape list endpoints return. CSV exports are the one exception.

Available on every plan

Every v1 response — success or failure — uses one of exactly two shapes. A client that handles both handles the whole API, with one exception: the CSV exports (GET /conversations/export, GET /leads/export) return raw text/csv, not the envelope.

Success

Note:

A data object may include fields this reference does not list — some endpoints return a stored row with internal columns. Only documented fields are part of the contract; anything unlisted can change or disappear without notice, so read only the fields documented here.

Error

code is a stable machine-readable string; branch on it. message is human-readable and may be reworded — do not match on it. The auth, access, and rate-limit failures also carry a stable subcode (invalid_key, missing_key, key_disabled, api_access_disabled, missing_scope, rate_limited) — an optional, additive finer discriminator; see Errors. A VALIDATION_ERROR also carries details: one reason per field path, with _ for the body as a whole. When the only problem is with the body as a whole (for example "Nothing to update."), message is that reason; otherwise it reads "Please check the highlighted fields." and the reasons are in details. The generated per-field copy distinguishes a missing required field (This field is required.) from a present-but-empty string (This field cannot be empty.), unless the schema carries its own message, which is kept verbatim.

Every error response also carries a requestId — echoed both in error.requestId and in the X-Request-Id response header. It is a safe, non-secret correlation id: quote it to support and they can find the exact failed request. (Success responses do not carry one.)

The full code-to-status table is on Errors.

List endpoints nest their data

The cursor-paginated lists (/chatbots, /conversations, /leads) put the pagination object in data — which itself contains a data array:

That is why reading a list is body.data.data, and the page terminator is body.data.nextCursor. It looks redundant and it is deliberate: the envelope belongs to the transport, the inner object belongs to pagination, and every endpoint therefore reads identically.

read-a-page.mjs

See Pagination for walking every page.

Timestamps and ids

  • Timestamps are ISO 8601 strings in UTC.
  • Ids are UUIDs.
  • A field with no value is null rather than omitted — for example a lead's owner when nobody is assigned.

HTTP status

The status code always agrees with the envelope: 2xx with "success": true, 4xx or 5xx with "success": false. You can branch on either, but the error.code is the more precise signal.

Where to go next

Last updated