Pagination
How to page through list endpoints.
List response shapes differ by resource; do not assume every endpoint shares a cursor or a page-number parameter. The
events timeline uses exclusive continuation ordered by
(occurredAt, id) using PostgreSQL timestamp and UUID order.
Before you poll
Use an authorised Live key with events:read at api.kuahr.com. Test keys cannot read this unpartitioned timeline. Use the same API address for every integration; Test keys are refused on this unpartitioned timeline. Initialisation and end-to-end outcomes are covered in poll and reconcile events.
Cursor paging (events)
GET /v1/events returns an array. Start without a cursor. After successfully
processing a non-empty page, persist the last event’s exact occurredAt and id
together, then send them as since and afterId. Keep the same eventType
filter throughout a traversal. An empty page leaves your cursor unchanged.
# First page
curl "https://api.kuahr.com/v1/events?limit=100" \
-H "X-Api-Key: kua_live_your_key_here"
# Next page: use the exact occurredAt and id of the last processed event.
curl --get "https://api.kuahr.com/v1/events" \
--data-urlencode 'limit=100' \
--data-urlencode 'since=2026-11-01T12:00:00.123456+00:00' \
--data-urlencode 'afterId=80000000-0000-0000-0000-000000000000' \
-H "X-Api-Key: kua_live_your_key_here"let cursor: { since: string; afterId: string } | undefined;
for (;;) {
const page = await kua.events.list({ ...cursor, limit: 100 });
if (page.length === 0) break;
for (const event of page) await handle(event);
const last = page[page.length - 1];
cursor = { since: last.occurredAt, afterId: last.id };
await saveCursor(cursor);
}cursor = {}
while True:
page = kua.events.list(**cursor, limit=100)
if not page:
break
for event in page:
handle(event)
last = page[-1]
cursor = {"since": last.occurred_at, "after_id": last.id}
save_cursor(cursor)handle, saveCursor and save_cursor represent your processing and durable
checkpoint storage. On restart, load that stored pair before continuing. Preserve
the returned timestamp string, including fractional precision and offset; avoid
rounding or parsing it through a lower-precision date type. URL-encode query
parameters. Deduplicate event IDs when retrying a partially processed page.
Continuation and reconciliation
The paired cursor handles tied timestamps among already persisted events. It is not a database commit sequence or a consistent snapshot across requests. Events inserted or committed later with a tuple at or before a saved cursor can still be missed, including backdated imports, clock skew and transactions committed out of order. ID ordering within one timestamp matters too.
For reconciliation, periodically rescan an earlier timestamp or the complete retained timeline and deduplicate event IDs. A bounded overlap only mitigates late arrival: it cannot recover events older than its window. This continuation does not provide a blanket lossless guarantee or automatic webhook replay.