Reference
API documentation
A REST API for reading your queues and bookings, adding people to a queue from your own website or till system, and receiving webhooks when anything changes.
Available on the Business plan. Create a key in Account settings → Integrations.
Authentication
Every request needs an API key in the Authorization header. Keys start qj_live_ and are shown once when created — we store only a hash, so a lost key must be revoked and replaced rather than recovered.
curl https://queuejoin.com/api/v1/me \
-H "Authorization: Bearer qj_live_xxxxxxxxxxxx"Keys carry scopes:
read— Read queues, bookings, customers and locationswrite— Create and update queue entries and bookings
Treat a key like a password. It authenticates as your whole organisation, so never put one in browser JavaScript, a mobile app, or a public repository.
Conventions
- Base URL:
https://queuejoin.com/api/v1. All requests must use HTTPS. - Successful responses return
{ "data": … }. Lists also returnmetawith the limit and offset used. - Errors return
{ "error": { "code", "message" } }. Branch oncode, which is stable;messagemay be reworded. - Lists accept
limit(default 50, max 200) andoffset. - Timestamps are ISO 8601 in UTC. Locations return their own
timezoneso you can render local times correctly.
{
"error": {
"code": "insufficient_scope",
"message": "This key does not have the \"write\" scope.",
"required_scope": "write"
}
}Endpoints
GET /v1/me
Confirms which organisation a key belongs to and what it can do. The quickest way to check a key works.
GET /v1/locations
Your locations, with addresses, coordinates and timezones.
GET /v1/queues
Your queues and how many people are waiting in each. Filter with location_id.
curl "https://queuejoin.com/api/v1/queues?location_id=LOCATION_ID" \
-H "Authorization: Bearer $QJ_KEY"GET /v1/queue-entries
Visits, newest first. Filter with status (waiting, called, served, no_show, cancelled), location_id or queue_id.
POST /v1/queue-entries
Add somebody to a queue — from your own booking page, your till, or a kiosk. Requires the write scope. They are notified exactly as if they had scanned your QR code.
curl -X POST https://queuejoin.com/api/v1/queue-entries \
-H "Authorization: Bearer $QJ_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Sam Okafor",
"mobile_number": "+447700900123",
"location_id": "LOCATION_ID",
"party_size": 2
}'queue_id, service_id and notesare optional; without a queue we use the location's default. Returns 201 with the new entry, or 402if the plan's visit limit is reached.
GET /v1/queue-entries/{id}
A single visit.
PATCH /v1/queue-entries/{id}
Move a visit on: called, served, no_show or cancelled. Requires the write scope. Sends the same notifications as the dashboard, so your customer hears about it either way.
curl -X PATCH https://queuejoin.com/api/v1/queue-entries/ENTRY_ID \
-H "Authorization: Bearer $QJ_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "called"}'Returns 409 if the visit is already served, cancelled or marked a no-show — closed visits are not reopened.
GET /v1/appointments
Bookings. Filter with from and to (ISO dates) and location_id.
GET /v1/customers
Your customer directory. Filter with search, or with marketing_consent=true to get only those who have opted in and not since opted out.
Webhooks
Register an HTTPS endpoint in Account settings → Integrations and we will POST to it when something happens — whether it happened through this API or because a member of staff pressed a button.
appointment.cancelled— A booking was cancelledappointment.created— A booking was madeappointment.updated— A booking was rescheduled or changed statusqueue_entry.called— A visitor was called forwardqueue_entry.cancelled— A visitor left the queuequeue_entry.created— A visitor joined a queuequeue_entry.no_show— A visitor did not arrivequeue_entry.served— A visitor was served
POST https://your-app.example.com/hooks/queuejoin
Queuejoin-Signature: t=1756377600,v1=6f2a…
{
"id": "evt_…",
"type": "queue_entry.called",
"created_at": "2026-08-28T10:00:00.000Z",
"data": {
"id": "ENTRY_ID",
"customer_id": "CUSTOMER_ID",
"location_id": "LOCATION_ID",
"queue_id": "QUEUE_ID",
"status": "called"
}
}Verifying a webhook
Always verify the signature before acting on a webhook — otherwise anyone who learns your URL can tell your system a customer was served. The header contains a timestamp and an HMAC-SHA256 of <timestamp>.<raw body>, keyed with the signing secret shown when you created the endpoint.
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=")),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
// Constant-time compare, and reject anything older than five minutes so a
// captured request cannot be replayed later.
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return fresh && crypto.timingSafeEqual(
Buffer.from(parts.v1), Buffer.from(expected),
);
}Use the raw request body, before any JSON parsing — re-serialising changes the bytes and the signature will not match.
Delivery and retries
- Reply
2xxto acknowledge. Anything else is treated as a failure. - Reply quickly and do the work afterwards — we time out after 10 seconds.
- Failures are retried six times over about 90 minutes with increasing gaps, then given up on. Recent attempts are listed in Account settings → Integrations.
- Delivery is at-least-once: the same event may arrive twice. Use the event
idto make your handling idempotent. - Disabling an endpoint stops queued events being delivered to it.
Limits and support
Be reasonable with request volume; we may throttle keys that place unusual load on the service. Creating a visit counts towards your plan's visit allowance, exactly as it would from the dashboard.
Questions, or something missing? hello@queuejoin.com. Use of the API is governed by our terms.