Response format
Every successful response from leadx-api goes through one helper, so the envelope is the same everywhere.
Single resource
GET /lead/:id, POST /contact, PUT /tag/:id — anything returning one document:
{
"ok": true,
"success": true,
"message": "Lead created successfully.",
"data": { "_id": "665f...", "stage": "New" }
}
| Field | Type | Meaning |
|---|---|---|
ok | boolean | Always true on success. |
success | boolean | Always true on success. Duplicate of ok; both are always sent. |
message | string | null | Human-readable result. null when the endpoint declares no message. |
data | object | null | The resource. |
The HTTP status comes from the named response message — commonly 200, or 201 for creates.
Collection
Every list endpoint returns the array in data alongside pagination metadata at the top level:
{
"ok": true,
"success": true,
"message": null,
"data": [ { "_id": "665f..." }, { "_id": "6660..." } ],
"totalCount": 1284,
"n": 25,
"p": 1,
"nextCursor": null,
"cursorPagination": false,
"sortBy": "createdAt",
"sortOrder": "desc"
}
| Field | Type | Meaning |
|---|---|---|
data | array | The page of documents. |
totalCount | number | Total matching the filter, ignoring pagination. |
p | number | null | Page echoed back. null when unpaginated. |
n | number | null | Page size echoed back. |
nextCursor | string | null | Opaque cursor for the next page, when cursor pagination is on. |
cursorPagination | boolean | Whether this response used cursor mode. |
sortBy / sortOrder | string | Sort actually applied. |
:::tip Read totalCount, not data.length
data.length is the size of the page. totalCount is the size of the result set — use it to decide whether to keep paging.
:::
Errors
Errors do not use the ok/success: true envelope:
{
"status": "fail",
"success": false,
"message": "Lead not found.",
"warning": ""
}
| Field | Type | Meaning |
|---|---|---|
status | "fail" | "error" | "fail" for 4xx, "error" for 5xx. |
success | boolean | Always false. |
message | string | What went wrong. |
warning | string | Usually empty; occasionally carries a non-fatal note. |
stack | string | Development only — never present in production. |
Detecting success reliably
Branch on the HTTP status code, not on the body. Both shapes carry success, so body.success === true also works, but the status is what your HTTP client already gives you:
const res = await fetch(url, { headers: { apikey } });
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.message}`);
return body.data;
A note on data for lists
The single-resource and collection shapes both use data, but a collection sets it to an array and adds sibling metadata keys. Code that assumes data is an object will break on list endpoints, and vice versa. The rule is simple: GET on a collection path returns an array; everything else returns an object.
What is inside data
The envelope is the same everywhere; the resource inside it is not. Each one is documented under Response schemas — every field, its type, whether it is required, what it defaults to, and which values an enumerated field accepts. Endpoint entries link to the schema for the resource they return.
Three things worth knowing before reading one:
- Fields marked
select: falseare never in a response. They exist on the stored document and the schema page lists them, flagged, precisely so you do not go looking for them in a payload.passwordon a user is the obvious one. - A
ObjectId -> Contactfield is an id string, not the object — unless that endpoint populates it, in which case its entry says so and the whole nested object arrives in its place. createdAtandupdatedAtare present on any resource whose schema page says timestamps are maintained, even though they are not listed as fields.
An endpoint entry with no Response line is one whose payload is not a stored resource — a Facebook Graph passthrough, an aggregation, a computed report. Those are shaped by the endpoint rather than by a schema.