Skip to content

Conventions

These conventions apply to every endpoint in the API reference. Read them once; the resource pages assume them.

All API paths live under /api/* on the production host:

https://thesidedoor.co

Requests and responses are JSON. Send Content-Type: application/json on any request with a body. Authenticated requests send an Authorization: Bearer <token> header - see Authentication.

Multipart uploads are blocked at the edge. Images are uploaded inline as base64 data URLs inside the JSON body - for example a venue logo, a bundle image or an event cover. Pass the data URL in the documented field, e.g.:

{ "logo_url": "data:image/png;base64,iVBORw0KGgoAAAANS..." }

Payload size is capped per endpoint, typically between 500 KB and 3 MB. Oversized images are rejected with a 400.

Errors return a JSON body with a single error string and an appropriate HTTP status code:

{ "error": "Party size exceeds the maximum for this rule." }
Status Meaning
400 Validation error - a field is missing or invalid.
401 Not authenticated, or the session token has expired.
402 Payment required - a card or payment step is needed.
403 Authenticated but not allowed - role or venue-access (ACL) failure.
404 Resource not found.
409 Conflict - the resource is in a state that blocks this action (e.g. a hold already captured).
429 Rate limited - retry after Retry-After.
500 Unexpected server error.
502 Upstream dependency failed.
503 A dependency is unconfigured, or the platform is in maintenance mode.

List endpoints accept limit (default 50, max 200) and offset query parameters and return an offset-paged envelope:

{
"data": [ { "id": "bkg_1" }, { "id": "bkg_2" } ],
"total": 128,
"limit": 50,
"offset": 0,
"has_more": true
}

Iterate by incrementing offset by limit while has_more is true.

A few high-volume endpoints support cursor paging instead. Request it with ?paging=cursor and follow the returned cursor value on the next call:

GET /api/.../items?paging=cursor&cursor=eyJpZCI6...

Reference and slowly-changing endpoints (for example the shared reference lists and your reservations calendar) return an ETag header. Send it back as If-None-Match and the API replies 304 Not Modified with an empty body when nothing has changed, saving you the payload:

GET /api/reference/event-types
→ 200 OK, ETag: "a1b2c3"
GET /api/reference/event-types
If-None-Match: "a1b2c3"
→ 304 Not Modified
  • Public endpoints (discovery, widget configs) are edge-cached for roughly 30–120 seconds with stale-while-revalidate. Expect brief propagation delays after a change.
  • Authenticated responses are served private, no-store - never cache a bearer-scoped response in a shared cache.
  • Static assets are served public, max-age=31536000, immutable.

Sensitive and public endpoints are rate limited per IP (and, for some, per email). Exceeding a limit returns 429 with a Retry-After header giving the number of seconds to wait:

HTTP/1.1 429 Too Many Requests
Retry-After: 42
{ "error": "Too many requests. Please try again shortly." }

Back off and retry after the indicated delay.

Payment and hold actions are safe to retry. The .../finalize endpoints re-read the payment intent and converge on the same outcome, and hold capture/release are idempotent - repeating a completed action is a no-op, while an action that contradicts the current state (capturing an already-released hold, releasing an already-captured one) returns 409. This makes it safe to retry after a dropped connection without double-charging.

When the platform is put into maintenance mode, /api/* requests return a branded 503. Treat it as transient and retry later.