Documentation

Errors, Pagination and Rate Limits

Pagination parameters, status and error codes, rate-limit buckets, per-endpoint limits and rate-limit headers.

Request conventions are described in Making Requests.

Pagination

ParameterDefaultDescription
page11-based page number
page_size20Items per page. Maximum 100
sort_bynoneField to sort by. Endpoint-specific
sort_orderascasc or desc

total_items counts the records paged over, not the items returned. An endpoint that filters after paging can return a short or empty page while later pages still hold items. Continue while current_page < total_pages.

Status and error codes

StatuscodeMeaning
400bad_requestMalformed request
401invalid_credentialsMissing or invalid token
403forbiddenAuthenticated, but not allowed: missing scope or role
404not_foundResource doesn't exist, or isn't yours
409conflictConflicts with current state
422validation_errorRequest body failed validation
429rate_limit_exceededRate limit exceeded. See Retry-After
500internal_errorError on Davi's side

A 404 for a record that does not exist and a 404 for a record not visible to the caller are indistinguishable.

The OAuth 2.0 endpoints use the RFC 6749 error shape instead: { "error": "...", "error_description": "..." }.

errors is a map of context to value. For validation failures the keys are field names and the values are messages; for other refusals a key does not necessarily name an input.

Specific codes

These codes are narrower than their status. Several share a status and need different handling.

codeStatusRaised when
identifier_unresolved404The supplied identifier matched no wallet or user
membership_required403The action needs an active membership, or one at a specific tier
idempotency_conflict409An idempotency key was reused for a different subject
forbidden_not_authorized403Acting on somebody else without the authority to do so

membership_required means the user must join or change tier. forbidden_not_authorized means the caller is the wrong actor.

POST /sessions/{slug}/admit does not use error statuses for refusals at the door. Every gate that turns somebody away, including closed doors and hours, answers 200 with decision: "blocked" and a blocked_reason. Only an unresolvable identifier, a caller without the authority, and a reused idempotency key return error statuses. See Org Activities.

Rate limits

Requests are counted over a sliding window: an allowance refills continuously rather than resetting on the minute. Authenticated buckets are keyed on user and client together, so traffic from another application the same user has authorized does not count against yours.

BucketLimit
Reads300 / minute
Writes60 / minute
Expensive writes20 / minute
Unauthenticated30 / minute, per IP and per path

Expensive writes

The expensive bucket is a fixed list of routes:

RouteChargedReason
POST /sessions/{session_slug}/admitper callRecords attendance at a door
POST /rewards/{reward_slug}/claimper callLedger write
POST /rewards/{reward_slug}/redeemper callLedger write
POST /rewards/{reward_slug}/propagateper callLedger write
POST /rewards/{reward_slug}/triggerper callLedger write
POST /organizations/{organization_slug}/cardsper itemTakes cards into inventory, up to 20 per call
POST /activities/{activity_slug}/sessions/batchper callCreates a run of sessions in one statement

POST /organizations/{organization_slug}/cards is the only endpoint charged per item: twenty cards in one request spend twenty of the expensive allowance. The batch cap and the bucket are both 20, so one full batch uses the whole minute's allowance. A session batch is one charge however many dates it creates.

Per-endpoint overrides

These endpoints have their own limits in addition to the buckets. Where both apply, the tighter one decides.

EndpointLimitKeyed on
POST /oauth2/token20 / minuteclient_id + IP
POST /oauth2/token (failed auth)5 / minuteIP
GET /oauth2/authorize60 / minuteUser session
POST /oauth2/introspect100 / minuteclient_id
POST /oauth2/revoke30 / minuteclient_id
Wallet transaction signing100 / hourWallet
Account lookup15 / minutenone

Headers

Every response carries these headers, not only a 429:

HeaderMeaning
RateLimit-LimitThe bucket's allowance
RateLimit-RemainingWhat is left in the current window
RateLimit-ResetSeconds until the window refills
RateLimit-PolicyThe policy, e.g. 300;w=60
Retry-AfterOn a 429 only. Seconds to wait

The same values are also sent under the older X-RateLimit-* names.

A 429 is refused before the request runs. Retrying after Retry-After needs no reconciliation of state.

Caching an access token for its lifetime, rather than requesting one per call, is the largest saving on the token endpoint's allowance.

Next