Pagination & sync
Lists page through an opaque cursor. Syncing works with updatedSince and includeDeleted, next to webhooks too.
Paging
Ask for the next page with
nextCursor as long as hasMore is true.GET /v1/contacts?limit=100&sort=-updatedAt
{
"data": [ { "id": "con_4Nf7…", … }, … ],
"hasMore": true,
"nextCursor": "eyJ2IjoxLCJrIjoi…"
}
GET /v1/contacts?limit=100&sort=-updatedAt&cursor=eyJ2Ijox…The SDKs walk the cursor themselves: for await (const contact of bodo.contacts.list()) or for contact in bodo.contacts.list().
Rules
| Parameter | Rule |
|---|---|
| limit | default 25, at most 100 |
| cursor | bound to filters and sort order, otherwise 400 CURSOR_MISMATCH; expires after 24 h |
| sort | only values from the reference, such as -updatedAt |
| unknown | 400 UNKNOWN_PARAMETER, there is no filter language |
| total count | not in lists, use GET /v1/contacts/count (2 points) |
Syncing, next to webhooks too
Remember the start time of every run and ask only for changes next time. Deletion markers come with
includeDeleted=true. Webhooks only report that something changed; you fetch the state exactly like this. If a resource has no deletion markers, its reference says so.GET /v1/contacts?updatedSince=2026-11-03T09:00:00Z&includeDeleted=true&limit=100
{"data": [
{"id": "con_4Nf7…", "lastName": "Mustermann", "updatedAt": "2026-11-03T09:14:22Z"},
{"id": "con_9Qa2…", "deleted": true, "updatedAt": "2026-11-03T10:02:51Z"}
], "hasMore": false, "nextCursor": null}If your receiver is down for a while, catch up with
updatedSince. You can also read events for 30 days through GET /v1/events.