REST API v1
Public REST for locations, queues, and entries
Server-to-server HTTP on your Convex site URL. Staff still sign in with WorkOS AuthKit; integrations use Bearer API keys.
Base URL
All routes live under your Convex HTTP site URL (Dashboard → Settings → URL ending in .convex.site). Do not call Convex function URLs (*.convex.cloud).
{CONVEX_SITE_URL}/api/v1
Authentication
- In Esperaly: Organisation settings → Integrations (admin or owner).
- Create an API key. Copy it once — Esperaly stores a hash, not the secret.
- Send it on every request.
Authorization: Bearer esperaly_live_<secret>
Keys can be scoped and optionally restricted to specific locations. Demo organisations reject API keys. Never log the raw key. Revoked, expired, or unknown keys return 401.
Scopes
| Scope | Allows |
|---|---|
| locations:read | List organisation locations |
| queues:read | List queues and fetch one queue |
| queue_entries:read | List and get ticket entries |
| queue_entries:write | Create, call, cancel, and complete entries |
| counters:read | Include counters on queue detail when the location uses counters |
Example
curl -sS "$CONVEX_SITE_URL/api/v1/locations" \
-H "Authorization: Bearer esperaly_live_…"
Create an entry (Idempotency-Key is stored for 24 hours):
curl -sS -X POST "$CONVEX_SITE_URL/api/v1/queues/$LOCATION_ID/entries" \
-H "Authorization: Bearer esperaly_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"serviceId":"…","customerName":"Ada Lovelace"}'
Routes
| Method | Path | Scope | Notes |
|---|---|---|---|
| GET | /locations | locations:read | Organisation locations |
| GET | /queues | queues:read | Same locations as queues (id = location id) |
| GET | /queues/:queueId | queues:read | Waiting/active counts; counters if counters:read |
| GET | /queues/:queueId/entries | queue_entries:read | Active tickets, cursor pagination |
| POST | /queues/:queueId/entries | queue_entries:write | Create via the same intake engine as the dashboard |
| GET | /entries/:entryId | queue_entries:read | One ticket |
| PATCH | /entries/:entryId | — | 405 in v1 (no safe patch mutation) |
| POST | /entries/:entryId/call | queue_entries:write | Moves the ticket to the workflow Call Next status. Body { "counterId" } required when the location uses counters |
| POST | /entries/:entryId/cancel | queue_entries:write | Workflow leave/cancel status |
| POST | /entries/:entryId/complete | queue_entries:write | Workflow complete status |
Pagination: ?limit=25&cursor=… (max 100). Response { data, hasMore, cursor }.
Idempotency-Key on POST create: same key + same path returns the same ticket for 24 hours.
Errors
{ "error": { "code": "unauthorized", "message": "Invalid or missing API key", "requestId": "…" } }
CORS allows the Esperaly site origin only (not *). Server-to-server callers (curl, Zapier, your backend) do not need a browser origin.
Not in this release
Product OAuth (PKCE / refresh tokens), a hosted MCP server, and an official Zapier app are not shipped. Use API keys plus REST, or Webhooks by Zapier with a Catch Hook.