Getting started

Authentication & limits

Every request carries an API key. Keys are scoped, bound to allowed origins, rate limited, hashed at rest, and rotated without downtime.

Sending the key

Use a Bearer token or the x-api-key header; both are equivalent.

Headers
Authorization: Bearer rrk_live_xxxxxxxx_...
# or
x-api-key: rrk_live_xxxxxxxx_...

Keys are stored as SHA-256 hashes and shown exactly once on creation or rotation. Rotation creates a new active key and keeps the previous one valid in a rotating state until you revoke it, so deployments never race against key changes.

Scopes

Available scopes
config:readscopeRestaurant profile, locations, booking policy
availability:readscopeFree slots and opening hours
reservations:readscopeRead bookings
reservations:writescopeCreate and change bookings
reservations:cancelscopeCancel bookings
waitlist:writescopeCreate waitlist entries
customers:readscopeRead guest profiles
customers:writescopeCreate and update guest profiles
webhooks:testscopeDispatch test webhook events

A request that needs a missing scope fails with 403 scope_missing and names the scope in error.details.missingScope.

Allowed origins

Browser requests send an Origin header; if the key has an origin allowlist and the origin is not on it, the request fails with 403 origin_not_allowed. Server to server calls without an Origin header are unaffected. For website integrations prefer the proxy pattern from the widget guide so keys stay on your server entirely.

Rate limits

Each key has a per-minute limit (default 120). Above it the API answers 429 rate_limit_exceeded with a Retry-After header in seconds.

Response 429
HTTP/1.1 429 Too Many Requests
Retry-After: 21

{ "error": { "code": "rate_limit_exceeded", "message": "Too many requests for this API key." } }

Idempotency

Send an Idempotency-Key header on reservation creation. Replays with the same key and body return the original reservation; the same key with a different body fails with 409 idempotency_conflict. Records expire after 24 hours.

Error format

Every error is JSON with a stable machine-readable code.

Error envelope
{
  "error": {
    "code": "no_tables_available",
    "message": "No matching table or table combination is available.",
    "details": { }
  }
}
Common error codes
api_key_missing401No Bearer token or x-api-key header
api_key_invalid401Unknown, revoked, or inactive key
api_key_expired401Key past its expiry date
restaurant_scope_mismatch403Key belongs to another restaurant
scope_missing403Key lacks a required scope
origin_not_allowed403Browser origin not on the allowlist
validation_failed422Body or query did not match the schema
outside_opening_hours422Requested time is not bookable
no_tables_available409No table or combination fits
slot_limit_reached409Covers or bookings per slot exhausted
idempotency_conflict409Idempotency key reused with a different body
reservation_not_found404Unknown id or reference
rate_limit_exceeded429Per-minute limit hit; see Retry-After