> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Requests & responses

> The envelope every /v1 endpoint returns, how to page through a list, and the conventions that hold across the API.

Every Custral endpoint speaks the same shape. Learn it once and the rest of the
reference is just which fields come back.

## The base URL and the surface

```
https://api.custral.com/v1
```

`/v1` is the **entire public API**. Anything you can reach from the
[SDK](/dev/sdks/typescript), the [CLI](/dev/cli/overview), or
[MCP](/dev/mcp/overview) goes through it. Other paths on that host serve
Custral's own apps, aren't versioned, and change without notice. Don't build
against them.

## Making a request

Authenticate with an API key as a bearer token, and send JSON:

```bash theme={null}
curl https://api.custral.com/v1/records/contacts \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json"
```

See [Authentication](/dev/auth/overview) for creating and scoping keys.

|          |                                                                   |
| -------- | ----------------------------------------------------------------- |
| Methods  | `GET` reads · `POST` creates · `PATCH` updates · `DELETE` removes |
| Body     | JSON on `POST` / `PATCH`. Only the keys you send are touched.     |
| Encoding | UTF-8 throughout.                                                 |

## The response envelope

**Every** response (success or failure) is an object with a `reqId`. On
success the payload is under `data`:

```json theme={null}
{
  "data": { "id": "rec_3Ab9xK2mQ7", "object": "contacts", "name": "Ada Lovelace" },
  "reqId": "req_2a1f9c8e7b6d5"
}
```

On failure there is no `data`; there's an `error` with a stable machine-readable `code`:

```json theme={null}
{
  "error": { "code": "insufficient_scope" },
  "reqId": "req_2a1f9c8e7b6d5"
}
```

Branch on the **`code`**, never on the human-readable message. Messages are
written for people and get reworded. The full list is in
[Errors](/dev/errors/overview).

<Tip>
  **Log the `reqId`.** It identifies one specific request end-to-end. Quoting it
  in a support message is the difference between us finding your failure in
  seconds and asking you to reproduce it.
</Tip>

## Lists and paging

A list endpoint puts the rows in `data.data`, alongside the counts and cursors
that describe the page:

```json theme={null}
{
  "data": {
    "data": [ { "id": "rec_3Ab9xK2mQ7" }, { "id": "rec_7Cd1yL8nR2" } ],
    "total": 128,
    "limit": 50,
    "offset": 0,
    "nextCursor": "eyJrIjoi…",
    "prevCursor": null
  },
  "reqId": "req_2a1f9c8e7b6d5"
}
```

| Field              | Meaning                                                        |
| ------------------ | -------------------------------------------------------------- |
| `data`             | The rows in this page.                                         |
| `total`            | Total matching rows, or `null` when the count isn't available. |
| `limit` · `offset` | The window this page represents.                               |
| `nextCursor`       | Token for the next page, `null` on the last page.              |
| `prevCursor`       | Token for the previous page, `null` on the first.              |

### Cursor paging is the one to use

Pass the previous response's `nextCursor` back as `cursor`, and stop when it
comes back `null`:

```ts theme={null}
let cursor: string | undefined;
do {
  const page = await custral.records.list({object: "contacts", cursor, limit: 100});
  for (const record of page.data) handle(record);
  cursor = page.nextCursor ?? undefined;
} while (cursor);
```

<Warning>
  `limit` / `offset` also work, but offsets **drift while you page**: a record
  created or deleted mid-walk shifts every later row, so you can miss records or
  see one twice. Use `offset` for a one-off jump to a known position; use
  `cursor` for anything that walks a whole object.
</Warning>

### Narrowing a list

`filter`, `sort`, and `q` narrow a list before it's paged, so they change what
`total` and the cursors describe. See
[Records](/dev/api-reference/records) for the filter grammar.

## Ids

Every id is a prefixed, URL-safe string: `rec_` records, `obj_` objects,
`conv_` conversations, `req_` requests. The prefix tells you what a thing is,
so log ids as-is rather than stripping it.

Ids are **opaque**: treat them as strings, don't parse them, and don't assume a
length. Where an endpoint takes an object it also accepts the object's
human-readable key (`contacts`) in place of its `obj_…` id.

<Note>
  Ids identify things, they don't describe them, a `rec_…` is not something to
  show a person. Resolve it to the record's name before it reaches your UI.
</Note>

## Conventions worth knowing

* **Partial updates.** `PATCH` touches only the keys you send. Sending `null`
  clears a field; omitting it leaves it alone.
* **Writes are forgiving, reads are canonical.** On the way in, a field key may
  be a property's `key`, its display name, or its id. Reads always come back
  keyed by `key`. See [How the schema works](/dev/api-reference/schema).
* **Computed fields are read-only.** `formula` and `rollup` values are
  recalculated on read; sending one is ignored.
* **Unknown fields are rejected, not dropped**, so a typo surfaces as an error
  instead of silently doing nothing.

## What's next

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/dev/auth/overview">
    Create an API key and scope it.
  </Card>

  <Card title="How the schema works" icon="sitemap" href="/dev/api-reference/schema">
    Objects, properties, records, and fields.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/dev/errors/overview">
    Every code the API can return.
  </Card>

  <Card title="Records" icon="table-rows" href="/dev/api-reference/records">
    The endpoints you'll use most.
  </Card>
</CardGroup>


## Related topics

- [Get support](/support.md)
- [Real-time updates](/dev/realtime/overview.md)
- [Request logs](/dev/logs.md)
- [API Reference](/dev/api-reference/overview.md)
- [Errors](/dev/errors/overview.md)
