Pagination & caching

Cursors

List endpoints use cursor-based pagination. The cursor is opaque — the server may change its encoding at any time. Treat it as a round-trip token.

  • ?limit=<n> — page size. Default 50, maximum 200.
  • ?cursor=<opaque> — start position. Omit on the first call.

Response envelope

{
  "data": [ /* ...entities... */ ],
  "pageInfo": {
    "nextCursor": "eyJrIjoiMjAyNi0wNS0wMVQxMDowMDowMC4wMDBaIiwiaSI6IjY1ZjBhZSJ9",
    "hasMore": true
  }
}

Stop calling when hasMore is false or nextCursor is null.

Incremental sync

For polling, combine cursors with ?updatedSince=<iso8601>:

GET /v1/participants?updatedSince=2026-05-15T00:00:00Z

Only participants updated after that instant are returned. Save the max updatedAt you saw and pass it on the next poll.

Test data

Records made by an organizer’s Test Mode, a flow preview or a rehearsal are left out of every read by default, as webhooks never send them. A test record read by id answers 404. To include them, pass ?includeTestData=true; each one then carries isTestData: true. Use the same value on every page of a sync, and start from the first page when you change it.

Caching with ETag

Every read response carries a weak ETag header and Cache-Control: private, max-age=0, must-revalidate. Cache responses locally; revalidate by sending the ETag back in If-None-Match:

GET /v1/participants/65f0ae12ab34cd5678ef9012
If-None-Match: W/"r42"

# When unchanged:
HTTP/1.1 304 Not Modified
ETag: W/"r42"
# (empty body)

For single-entity reads the ETag is the document's revision counter — extremely cheap to compute. For list pages it's a hash of the filter, cursor, page size, and the maximum updatedAt in the returned rows.

Rate limits

Every authenticated response carries three rate-limit headers so you can pace your client without guessing:

  • X-RateLimit-Limit — your account's max requests per minute.
  • X-RateLimit-Remaining — what's left in the current 60-second window.
  • X-RateLimit-Reset — unix-seconds when the window rolls over.

Default limit is 60 req/min per account, shared across every token issued under that account. Exceeding the limit returns 429 Too Many Requests with a Retry-After header naming the seconds until the next window. Ask your administrator to raise the limit if your integration needs higher throughput.