# Pagination and success shapes

The three shapes a success body takes, and why lists are cursors rather than page numbers.

Most of this API answers a list as `{ "data": [...], "next_cursor": ... }`, but
not all of it does, and this page says which is which rather than describing a
uniformity that is not there. The table below is generated from
[the specification](https://boltclaw.ai/openapi.yaml), so it cannot describe a shape the API does
not return.

## The three success shapes

### A paged list

`{ "data": [ ... ], "next_cursor": "..." | null }`

Walk it: pass `next_cursor` back as `cursor` and stop when it is `null`.

22 of the 215 operations answer it: `GET /v1/pages`, `GET /v1/inquiries`, `GET /v1/posts`, `GET /v1/inbox`, `GET /v1/leads`, `GET /v1/referrals/programs`, `GET /v1/referrals/programs/{id}/partners`, `GET /v1/referrals/programs/{id}/commissions`, `GET /v1/referrals/programs/{id}/payouts`, `GET /v1/referrals/programs/{id}/tax-forms`, `GET /v1/referrals/broadcasts/{id}/referrers`, `GET /v1/referrals/imports/{id}/invites`, `GET /v1/referrals/programs/{id}/entries`, `GET /v1/referrals/programs/{id}/codes`, `GET /v1/referrals/programs/{id}/invites`, `GET /v1/referrals/programs/{id}/privacy-requests`, `GET /v1/referrals/programs/{id}/links`, `GET /v1/referrals/programs/{id}/referrers`, `GET /v1/referrals/programs/{id}/grants`, `GET /v1/auth/key/workspaces`, `GET /v1/logs/agent-calls`, `GET /v1/logs/audit`.

### A whole list

`{ "data": [ ... ] }`, or `{ "data": [ ... ], "next_cursor": null }`

Short by nature, and never paged. There is nothing to walk; `next_cursor`, where it appears at all, is always `null`.

20 of the 215 operations answer it: `GET /v1/keys`, `GET /v1/pages/{slug}/links`, `GET /v1/inquiries/{id}/messages`, `GET /v1/webhooks/events`, `GET /v1/webhooks`, `GET /v1/webhooks/{id}/deliveries`, `GET /v1/accounts`, `GET /v1/automations`, `GET /v1/referrals/programs/{id}/payout-providers`, `GET /v1/referrals/payouts/{id}/invoices`, `GET /v1/referrals/domains`, `GET /v1/referrals/programs/{id}/broadcasts`, `GET /v1/referrals/programs/{id}/imports`, `GET /v1/referrals/programs/{id}/library`, `GET /v1/referrals/programs/{id}/link-hosts`, `POST /v1/referrals/programs/{id}/link-hosts/check`, `GET /v1/referrals/programs/{id}/contests`, `GET /v1/referrals/programs/{id}/rewards`, `GET /v1/referrals/programs/{id}/groups`, `GET /v1/referrals/programs/{id}/flags`.

### A single object

The resource itself, unwrapped.

Creates, reads, updates and deletes all answer the object. Nothing in this API answers `204`, so a delete has a body too.

Everything else: 173 of the 215 operations.

## Cursors

`GET /v1/pages`, `GET /v1/inquiries`, `GET /v1/posts`, `GET /v1/inbox`,
`GET /v1/leads` and the referral lists (programs, partners, commissions,
payouts) are cursor-paginated, newest first — `/v1/pages` by
`created_at`, `/v1/inquiries` by `last_message_at`, `/v1/posts` by when the
post is scheduled, `/v1/inbox` by when the item arrived, a program's partners
by when they applied. Each
endpoint's cursor belongs to that endpoint and encodes a position in *its* own
ordering; passing one to the other is not meaningful and its result is not
defined.

```
GET /v1/inquiries?limit=100
  → { "data": [...100 items...], "next_cursor": "MjAyNi0wMy0w..." }

GET /v1/inquiries?limit=100&cursor=MjAyNi0wMy0w...
  → { "data": [...41 items...], "next_cursor": null }
```

`next_cursor` is `null` when you have reached the end. Treat the cursor as
opaque and pass it back unmodified.

**The first page of a list** — answers `200`

`limit` is clamped rather than refused. Pass `next_cursor` back as `cursor` for the next page, and stop when it is `null`.

```bash
curl "https://api.boltclaw.ai/v1/pages?limit=50" \
  -H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

```js
const res = await fetch("https://api.boltclaw.ai/v1/pages?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
  },
});

// 200
const data = await res.json();
```

**Cursors, not page numbers, and this matters if you are reading messages.**
With numbered pages, a message arriving while you are on page 2 pushes every
older message down one place — so page 3 repeats one row and *drops one off the
end without telling you*. A cursor names the exact row you stopped at, so the
next page starts precisely where the last one ended, whatever arrived in the
meantime. New messages are not missed; they are simply newer than your walk,
and you collect them on the next poll with `since`.

<!-- https://boltclaw.ai/docs/pagination as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
