Developers

Authentication, scopes, errors and limits

API keys

Every call to /api/v1/... needs an API key in the Authorization header:

Authorization: Bearer key_live_3f9a1c0...

Create keys in Aphra under Settings → API. You give each key a name (say “Website” or “Zapier”) and one or more scopes. The full key is shown once, right after you create it. Aphra only stores a hash, so a lost key cannot be recovered: revoke it and make a new one.

The settings page shows, per key, the first characters, its scopes, when it was made and when it was last used. A revoked key stops working immediately.

Keep keys on your server. Never put one in JavaScript that runs in a browser or in a mobile app: anyone could read it and see your customer list.

Scopes

A key can only do what its scopes allow. Give each integration the smallest set it needs.

Scope Allows
bookings:read GET /api/v1/bookings
bookings:write POST /api/v1/bookings
customers:read GET /api/v1/customers
invoices:read GET /api/v1/invoices

bookings:write does not include bookings:read. If your integration creates bookings and also lists them, tick both.

Errors

Errors come back as JSON with a single error field and an HTTP status code:

{ "error": "slot_unavailable" }
Status Meaning
400 The request is malformed: a missing or invalid field, or a bad query parameter.
401 No key, an unknown key, or a revoked key.
403 The key is valid but lacks the scope for this endpoint.
404 The account or resource does not exist.
422 The request was understood but could not be carried out, for example the time slot is taken. The error field holds a code; see Bookings.
429 Too many requests. See below.
5xx Something went wrong on our side. Retry later.

Base your code on the status code and, for 422, on the error code. The human-readable messages for 400, 401, 403 and 429 may change and are not translated.

Rate limits

Endpoints Limit
/api/v1/... 120 requests per minute, per key and IP address
/api/public/services 60 requests per minute, per business and IP address
/api/availability 60 requests per minute, per business and IP address

The window is fixed: it resets every 60 seconds, not on a rolling basis. Above the limit you get 429. Wait until the next minute before you try again. There is no Retry-After header yet.

Pagination

List endpoints take limit and offset:

Parameter Default Maximum
limit 50 100
offset 0

The response repeats the limit and offset it used. There is no total count. To walk through everything, keep raising offset by limit until a page comes back with fewer items than limit.

curl "https://app.aphrabooking.com/api/v1/customers?limit=100&offset=200" \
  -H "Authorization: Bearer $APHRA_KEY"

Dates and times

All timestamps are ISO 8601 in UTC. When you send one, end it with Z: 2026-11-20T14:00:00Z. A time with an offset such as +02:00 is rejected. The business’s own time zone is set in Aphra; convert on your side when you show times to people.

Field names

The /api/v1 endpoints use camelCase (startsAt). The public endpoints and webhook payloads use snake_case (starts_at). That difference is historical. We will not change existing fields without a new API version; see the changelog.