Conventions
Pagination, dates, limits and response headers.
Envelopes
Every successful response is { "data": ..., "request_id": "..." }. Quote the
request_id when you report a problem; it is how a log line is found.
Pagination
Cursor-based, never offset. Pass the next_cursor from the previous response
back as cursor.
An offset walks the whole prefix on every page and, worse, SKIPS rows when something is inserted between two requests, so a client paging through a list that is being written to silently misses items. A cursor cannot.
Forgiving inputs
limit is clamped, not rejected: asking for 500 gets you 100 and the response
reports the limit that was applied. A non-numeric limit still fails, because
that is a mistake rather than an intent.
Dates accept 2026-07-01 or a full ISO 8601 timestamp. A date-only value on
the to side means the END of that day, which is what a person means by “up to
the 31st”.
Writing safely
Every write that replaces something can say what it expects to replace. Send the
expected_hash of a file or entry you read, and the change is refused with
409 conflict if it moved. Publishing, rolling back and unpublishing take the
expected_generation you read, so you only change the state you reviewed. Both
conflicts carry what you need to read again in error.details.
Long operations
A build returns at once with a queued state and you poll it until it is
ready, failed or cancelled. Those are final: stop polling when you see one.
Rate limits
Five limits apply, and they defend different things: per-credential throughput,
a short burst window, per-IP volume, client registration, and authentication
FAILURES specifically. Every response carries the remaining budget in its
headers; a 429 carries Retry-After.