3 operations. Every schema and example on this page is generated from the platform contract.
Merchant advances ONE child_collect leg of a multi-collect parent along the fulfilment axis (none → accepted → preparing → ready). Backed by the idempotent, store-scoped advance_collect_leg_status RPC (single writer; outbox event in the same tx). The terminal collected step is scanCollect; cancel is overrideCollectLeg. Idempotent: a re-tap or out-of-order request no-ops.
Store that owns the leg (also authorises the merchant).
The child_collect leg (its own guest_orders row) to advance.
Forward-only target on the fulfilment axis for ONE collect leg.
acceptedpreparingreadyA guest order row (Order & Collect).
curl -X POST "https://www.membber.com/api/v1/collect/advance" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"child_order_id": "f1ff3b6f-0000-4000-8000-d0c5000000f1",
"to_status": "accepted"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/collect/advance", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
child_order_id: "f1ff3b6f-0000-4000-8000-d0c5000000f1",
to_status: "accepted"
},
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.advanceCollectLeg(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
childOrderId: "f1ff3b6f-0000-4000-8000-d0c5000000f1",
toStatus: .accepted
))
).ok.body.json
print(response){
"leg": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"order_type": "<order_type>",
"fulfilment_status": "<fulfilment_status>",
"collection_code": "EXAMPLE10",
"collection_mode": "<collection_mode>",
"requested_collection_at": "<requested_collection_at>",
"accepted_at": "<accepted_at>",
"preparing_at": "<preparing_at>",
"ready_at": "<ready_at>",
"collected_at": "<collected_at>"
}
}FOUNDER-LOCKED (25 Jun 2026). Sets ONE collect leg to any meaningful status, forward, →collected (force-collect), a backward correction, or →cancelled (which routes through the full Stripe refund path: refund + free slot + release stock + cancel notify + proportional loyalty reversal). Behind a confirm gate. HARD LIMIT: a cancelled (money-returned) leg cannot be re-activated (409 CANNOT_REVERSE_REFUND). Idempotent: setting a leg to its current status no-ops.
Store that owns the leg (also authorises the merchant).
The collect leg to override (UUID).
Any MEANINGFUL target: a backward correction, a force-collect, or a cancel. 'none' is deliberately excluded (no useful merchant target).
acceptedpreparingreadycollectedcancelledMust be true. A force / backward / cancel override is a deliberate, confirmed action, it can bypass the normal forward flow and, on →cancelled, refund money.
Optional UUID for ONE →cancelled tap so the resulting Stripe refund dedups on retry (a re-tap with the same id never double-refunds).
A guest order row (Order & Collect).
What kind of transition was applied.
forwardbackwardcancellednoopTrue when leaving 'collected' reversed the leg's un-redeemed loyalty stamps.
curl -X POST "https://www.membber.com/api/v1/collect/override" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"child_order_id": "f1ff3b6f-0000-4000-8000-d0c5000000f1",
"to_status": "accepted",
"confirm": true
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/collect/override", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
child_order_id: "f1ff3b6f-0000-4000-8000-d0c5000000f1",
to_status: "accepted",
confirm: true
},
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.overrideCollectLeg(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
childOrderId: "f1ff3b6f-0000-4000-8000-d0c5000000f1",
toStatus: .accepted,
confirm: true
))
).ok.body.json
print(response){
"leg": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"order_type": "<order_type>",
"fulfilment_status": "<fulfilment_status>",
"collection_code": "EXAMPLE10",
"collection_mode": "<collection_mode>",
"requested_collection_at": "<requested_collection_at>",
"accepted_at": "<accepted_at>",
"preparing_at": "<preparing_at>",
"ready_at": "<ready_at>",
"collected_at": "<collected_at>"
},
"transition": "forward",
"loyalty_reversed": true
}Counter handoff, resolve a leg by (store, collection_code), require it to be ready, stamp collected_at + status='collected' atomically, and (via the fulfilment_collected outbox row) award loyalty + end the customer Live Activity. Backed by the idempotent collect_scan_to_collect RPC: a re-scan of an already-collected code is a safe no-op.
Store performing the counter handoff (also authorises the merchant).
The collection code scanned or typed at the counter.
A guest order row (Order & Collect).
curl -X POST "https://www.membber.com/api/v1/collect/scan" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"collection_code": "EXAMPLE10"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/collect/scan", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
collection_code: "EXAMPLE10"
},
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.scanCollect(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
collectionCode: "EXAMPLE10"
))
).ok.body.json
print(response){
"leg": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"order_type": "<order_type>",
"fulfilment_status": "<fulfilment_status>",
"collection_code": "EXAMPLE10",
"collection_mode": "<collection_mode>",
"requested_collection_at": "<requested_collection_at>",
"accepted_at": "<accepted_at>",
"preparing_at": "<preparing_at>",
"ready_at": "<ready_at>",
"collected_at": "<collected_at>"
}
}