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.
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
{
"success": true,
"data": {}
}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
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "API access is not enabled for this workspace.",
"requestId": "9f3b7d4c2a61"
}
}code is a stable machine-readable string; branch on it. message is human-readable and may be reworded — do not match on it. Some errors carry an extra details field with validation specifics.
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:
{
"success": true,
"data": {
"data": [
{ "id": "…", "createdAt": "2026-08-08T09:12:44.000Z" },
{ "id": "…", "createdAt": "2026-08-07T16:03:01.000Z" }
],
"nextCursor": "1f6b1d64-6a5f-4f0e-9b0e-b1b1a0c3d2e4"
}
}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.
const body = await res.json();
if (!body.success) throw new Error(body.error.code);
const rows = body.data.data;
const next = body.data.nextCursor; // null on the last pageSee Pagination for walking every page.
Timestamps and ids
- Timestamps are ISO 8601 strings in UTC.
- Ids are UUIDs.
- A field with no value is
nullrather than omitted — for example a lead'sownerwhen 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