3 operations. Every schema and example on this page is generated from the platform contract.
Public so a walk-in can join from the shop-window QR with no account: a signed-in customer is identified from their session, a guest supplies a name. Refused with WALK_INS_NOT_ACCEPTED when the store does not take walk-ins. A signed-in customer who taps twice gets their existing place back (reused: true), enforced by a unique index, not a check. The quoted wait is DERIVED from the live diary, not position times an average, and is snapshotted so a later dispute is answerable. No money, no consent, no fee.
The store whose walk-in queue to join.
The service being asked for. Its duration shapes the wait estimate and the eventual booking length.
A specific chair/stylist to wait for. Omit for "anyone".
People in the party (a table waitlist). 1 for a normal walk-in.
Who the walk-in is, when there is no signed-in customer. A signed-in customer is identified from their session and this is ignored.
Optional contact number for a guest walk-in, so the shop can chase a no-answer.
Your place in the queue. A guest keeps this to check status or leave, it is your bearer capability for this entry.
true when you were already waiting and your existing place was returned unchanged (a double-tap never takes a second place).
DERIVED minutes until a chair is expected free for you, snapshotted as your quote.
'waiting' | 'called' | 'assigned' | 'completed' | 'left' | 'no_show' | 'converted_to_appointment'.
curl -X POST "https://www.membber.com/api/v1/queue/join" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"store_id": "6659c139-0000-4000-8000-d0c500000066"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/queue/join", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066"
},
headers: { "Idempotency-Key": crypto.randomUUID() },
});
if (error) {
// Typed error envelope: { error: { code, message, requestId } }
throw new Error(`${error.error.code}: ${error.error.message}`);
}
console.log(data);import MembberSwift
let client = MembberClient(
serverURL: MembberClient.productionServerURL,
tokenProvider: { session.accessToken }
)
let response = try await client.api.joinWalkInQueue(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066"
))
).ok.body.json
print(response){
"entry_id": "83353c08-0000-4000-8000-d0c500000083",
"reused": true,
"expected_wait_min": 1,
"status": "<status>"
}Removes YOUR entry from the queue (status becomes "left", a customer can never mark themselves a no-show). Ownership is enforced: a signed-in customer may only leave an entry that is theirs; a guest may only leave an entry whose id they hold. Idempotent, leaving an already-ended entry is a no-op. Everyone behind you moves up one place automatically, because position is derived.
The queue entry to leave, the id returned when you joined.
'left' once you have left.
true when the entry had already ended, an idempotent no-op, never a second state change.
curl -X POST "https://www.membber.com/api/v1/queue/leave" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"entry_id": "83353c08-0000-4000-8000-d0c500000083"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/queue/leave", {
body: {
entry_id: "83353c08-0000-4000-8000-d0c500000083"
},
headers: { "Idempotency-Key": crypto.randomUUID() },
});
if (error) {
// Typed error envelope: { error: { code, message, requestId } }
throw new Error(`${error.error.code}: ${error.error.message}`);
}
console.log(data);import MembberSwift
let client = MembberClient(
serverURL: MembberClient.productionServerURL,
tokenProvider: { session.accessToken }
)
let response = try await client.api.leaveWalkInQueue(
body: .json(.init(
entryId: "83353c08-0000-4000-8000-d0c500000083"
))
).ok.body.json
print(response){
"entry_id": "83353c08-0000-4000-8000-d0c500000083",
"status": "<status>",
"noop": true
}Reads one entry's live, DERIVED position and wait estimate, what powers "you're #3, about 20 minutes" and the "you're up" countdown. Ownership is enforced exactly as leave: a signed-in customer sees only their own entry (by entry_id or by store_id), a guest sees only an entry whose id they hold. Returns a null entry (not an error) when there is no active entry, so the client can render the join card.
Look up one entry by id (the guest bearer path, or your own entry).
For a signed-in customer: find your active entry in this store. Ignored for a guest, give entry_id instead.
Your entry with its DERIVED position + wait, or null when you have no active entry.
Present on the merchant surface; omitted from the customer surface.
The service the walk-in asked for, shapes the wait estimate and the assigned duration.
A chair/stylist the walk-in asked for, if any.
'waiting' | 'called' | 'assigned' | 'completed' | 'left' | 'no_show' | 'converted_to_appointment'.
ISO instant they joined the queue (the join order that derives position).
ISO instant they were called, if they have been.
The wait we quoted AT JOIN, snapshotted, so a later dispute is answerable.
Set once assigned, the booking the entry converted into.
DERIVED 1-based place in line. Null once called/assigned/left, a stored position is a second source of truth that drifts.
DERIVED minutes until a chair is expected free for this person, from the live blocks. Null when not waiting.
DERIVED ISO instant a chair is expected free for this person. Null when not waiting.
The earliest-free chair the estimate would hand this person, the rail's default assign target. Null when not waiting.
How long a called walk-in is held before they lose their place, so the client can show the "you're up, arrive within N min" countdown.
curl -G "https://www.membber.com/api/v1/queue/status"import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.GET("/api/v1/queue/status");
if (error) {
// Typed error envelope: { error: { code, message, requestId } }
throw new Error(`${error.error.code}: ${error.error.message}`);
}
console.log(data);import MembberSwift
let client = MembberClient(
serverURL: MembberClient.productionServerURL,
tokenProvider: { session.accessToken }
)
let response = try await client.api.getWalkInQueueStatus().ok.body.json
print(response){
"entry": {
"entry_id": "83353c08-0000-4000-8000-d0c500000083",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"customer_id": "96607d1c-0000-4000-8000-d0c500000096",
"guest_name": "<guest_name>",
"guest_phone": "+44 7700 900123",
"service_id": "993232e5-0000-4000-8000-d0c500000099",
"preferred_unit_id": "cb0a9bf8-0000-4000-8000-d0c5000000cb",
"party_size": 1,
"status": "<status>",
"joined_at": "<joined_at>",
"called_at": "<called_at>",
"quoted_wait_min": 1,
"appointment_id": "0d727dbb-0000-4000-8000-d0c50000000d",
"queue_position": 1,
"expected_wait_min": 1,
"expected_start": "<expected_start>",
"suggested_unit_id": "28cd789a-0000-4000-8000-d0c500000028"
},
"called_hold_minutes": 1
}