9 operations. Every schema and example on this page is generated from the platform contract.
Merchant lists their store orders: scope=active is the live fulfilment queue; scope=history is terminal orders newest-first, keyset-paged by the `before` created_at cursor. Same per-order shape as the fulfilment ops.
Store whose orders to list (also authorises the merchant).
active = the live queue (default); history = terminal orders, newest-first.
activehistoryPage size (default 50, max 100).
Keyset cursor (an order created_at) for history pagination.
activehistorycurl -G "https://www.membber.com/api/v1/orders" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
--data-urlencode "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.GET("/api/v1/orders", {
params: { query: { store_id: "6659c139-0000-4000-8000-d0c500000066" } },
});
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.listOrders(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"orders": [
{
"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>"
}
],
"scope": "active"
}/api/v1/ordersCustomer creates an in-app order; returns a destination-charge PaymentIntent + ephemeral key for the native Apple Pay / card sheet. Idempotent via the body `idempotency_key` (order-level replay in the service).
Public store code.
Store id.
Client-generated order idempotency key (UUID v4).
Basket lines.
Fulfilment selection (defaults to counter collection).
buy_nowcollectnowscheduledMixed-cart groups: take-now lines + one or more collect groups.
nowcollectnowscheduledCollect children of a £0 mixed order (empty/absent for a normal order).
curl -X POST "https://www.membber.com/api/v1/orders" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"store_code": "EXAMPLE10",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"idempotency_key": "1f0e2d3c-4b5a-4678-9abc-def012345678",
"items": [
{
"menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
"quantity": 1
}
]
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.createOrder({
body: {
store_code: "EXAMPLE10",
store_id: "6659c139-0000-4000-8000-d0c500000066",
idempotency_key: "1f0e2d3c-4b5a-4678-9abc-def012345678",
items: [
{
menu_item_id: "ca65a6e7-0000-4000-8000-d0c5000000ca",
quantity: 1
}
]
},
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.createOrder(
body: .json(.init(
storeCode: "EXAMPLE10",
storeId: "6659c139-0000-4000-8000-d0c500000066",
idempotencyKey: "1f0e2d3c-4b5a-4678-9abc-def012345678",
items: [.init(
menuItemId: "ca65a6e7-0000-4000-8000-d0c5000000ca",
quantity: 1
)]
))
).ok.body.json
print(response){
"order_id": "4991ffac-0000-4000-8000-d0c500000049",
"order_number": "<order_number>",
"status": "<status>",
"requires_payment": true,
"client_secret": "<client_secret>",
"ephemeral_key": "<ephemeral_key>",
"stripe_customer_id": "847b302a-0000-4000-8000-d0c500000084",
"connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
"payment_intent_id": "0b6642a5-0000-4000-8000-d0c50000000b",
"amount_pence": 1500,
"currency": "GBP",
"merchant_name": "<merchant_name>",
"collection_code": "EXAMPLE10",
"children": [
{
"order_id": "4991ffac-0000-4000-8000-d0c500000049",
"order_number": "<order_number>",
"order_type": "<order_type>",
"collection_code": "EXAMPLE10",
"fulfilment_status": "<fulfilment_status>",
"prep_minutes": 1,
"requested_collection_at": "<requested_collection_at>",
"total_pence": 1500
}
]
}Merchant fetches a single order (items, fulfilment state, totals). Store-scoped, a store cannot read another store's order (returns 404).
Store the order belongs to (also authorises the merchant).
A guest order row (Order & Collect).
curl -G "https://www.membber.com/api/v1/orders/b80cf509-0000-4000-8000-d0c5000000b8" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
--data-urlencode "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.GET("/api/v1/orders/{orderId}", {
params: { path: { orderId: "b80cf509-0000-4000-8000-d0c5000000b8" }, query: { store_id: "6659c139-0000-4000-8000-d0c500000066" } },
});
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.getOrder(
path: .init(orderId: "b80cf509-0000-4000-8000-d0c5000000b8"),
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"order": {
"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>"
}
}Staff transition of a collect order: accepted → preparing → ready → collected, or cancelled. Backed by the idempotent advance_order_fulfilment RPC (single writer; outbox event in same tx).
Store that owns the order.
Target fulfilment state.
acceptedpreparingreadycollectedcancelledA guest order row (Order & Collect).
curl -X PATCH "https://www.membber.com/api/v1/orders/b80cf509-0000-4000-8000-d0c5000000b8/fulfilment" \
-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",
"to_status": "accepted"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.advanceOrderFulfilment({
params: { path: { orderId: "b80cf509-0000-4000-8000-d0c5000000b8" } },
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
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.advanceOrderFulfilment(
path: .init(orderId: "b80cf509-0000-4000-8000-d0c5000000b8"),
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
toStatus: .accepted
))
).ok.body.json
print(response){
"order": {
"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>"
}
}Staff records that a collect order is running late (delayMinutes) or sets an explicit newReadyAt, with an optional note. Persists a revised ETA on the order (never the actual ready_at or the booked slot), then notifies the customer on the existing channels: push for a linked customer, email fallback for a guest. Writes an append-only order_status_events audit row (kind running_late). Not money-moving; a short per-order dedupe window keeps a double-tap from spamming.
Store that owns the order (also authorises the merchant).
Minutes the order is running late (1-480). New ETA = current ETA + this.
Explicit new expected ready/collection time (ISO 8601). Overrides delayMinutes.
Optional short merchant message shown to the customer with the new time.
pushemailnonecurl -X POST "https://www.membber.com/api/v1/orders/b80cf509-0000-4000-8000-d0c5000000b8/notify-delay" \
-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"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/orders/{orderId}/notify-delay", {
params: { path: { orderId: "b80cf509-0000-4000-8000-d0c5000000b8" } },
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.notifyOrderDelay(
path: .init(orderId: "b80cf509-0000-4000-8000-d0c5000000b8"),
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066"
))
).ok.body.json
print(response){
"ok": true,
"newEta": "<newEta>",
"notified": "push",
"channel_detail": "<channel_detail>"
}Staff-initiated refund. Validates the refund window + amount, then creates a Stripe refund, idempotent via a Stripe idempotency key derived from order + refund_attempt_id (a retry of the same tap never double-refunds, while two distinct taps with distinct ids each go through). Reverses loyalty + promo on a full refund.
Why the order is being refunded (audited).
Partial refund amount in pence; omit/null for a full refund.
Optional UUID for ONE merchant refund tap. A fresh id per tap makes two legitimate equal-amount/equal-reason refunds distinct; a retry of the same tap (same id) safely dedups via the Stripe idempotency key.
curl -X POST "https://www.membber.com/api/v1/orders/b80cf509-0000-4000-8000-d0c5000000b8/refund" \
-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",
"reason": "Added at the front desk"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.refundOrder({
params: { path: { orderId: "b80cf509-0000-4000-8000-d0c5000000b8" } },
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
reason: "Added at the front desk"
},
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.refundOrder(
path: .init(orderId: "b80cf509-0000-4000-8000-d0c5000000b8"),
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
reason: "Added at the front desk"
))
).ok.body.json
print(response){
"refund": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"status": "<status>",
"amount": 1500
}
}Staff transition of the merchant status axis: ready, fulfilled, or cancelled. Backed by the idempotent updateGuestOrderStatus service (noop on a same-status repeat; store-scoped transition guard). Writes a store_payment_audit_logs row, bridges buy_now status changes onto the order_status_events outbox so the customer is notified, and releases slot + stock holds on cancel. Not money-moving (a refund is refundOrder).
Store that owns the order (also authorises the merchant).
Target business status: ready, fulfilled, or cancelled.
readyfulfilledcancelledOptional staff note, stored on the status change + audit row.
curl -X PATCH "https://www.membber.com/api/v1/orders/b80cf509-0000-4000-8000-d0c5000000b8/status" \
-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",
"status": "ready"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.PATCH("/api/v1/orders/{orderId}/status", {
params: { path: { orderId: "b80cf509-0000-4000-8000-d0c5000000b8" } },
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
status: "ready"
},
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.updateOrderStatus(
path: .init(orderId: "b80cf509-0000-4000-8000-d0c5000000b8"),
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
status: .ready
))
).ok.body.json
print(response){
"order": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"status": "<status>",
"order_type": "<order_type>",
"fulfilled_at": "<fulfilled_at>",
"cancelled_at": "<cancelled_at>"
}
}For an order that is ready before its booked slot, moves it onto an earlier open slot. Atomic and first-come-first-served: a full slot returns SLOT_JUST_FILLED (409) and the order keeps its original time; a slot that is not actually earlier returns NOT_EARLIER (400). Idempotent on the same slot.
Store that owns the order (also part of the ownership check).
The customer’s own order to move earlier.
The earlier, open collection slot to move onto.
The slot the order now holds.
The new collection instant (ISO 8601, UTC).
Places still free in the new slot, or null when it is uncapped.
curl -X POST "https://www.membber.com/api/v1/orders/collect-earlier" \
-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",
"order_id": "4991ffac-0000-4000-8000-d0c500000049",
"slot_id": "820a5efc-0000-4000-8000-d0c500000082"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/orders/collect-earlier", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
order_id: "4991ffac-0000-4000-8000-d0c500000049",
slot_id: "820a5efc-0000-4000-8000-d0c500000082"
},
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.collectEarlier(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
orderId: "4991ffac-0000-4000-8000-d0c500000049",
slotId: "820a5efc-0000-4000-8000-d0c500000082"
))
).ok.body.json
print(response){
"to_slot_id": "098d6938-0000-4000-8000-d0c500000009",
"requested_collection_at": "<requested_collection_at>",
"orders_remaining": -9007199254740991
}Resolves the order by PaymentIntent id, verifies ownership + the PI on the platform account, then runs the idempotent reconcile (mark paid + ledger + loyalty award). Safe alongside the Stripe webhook.
The PaymentIntent the sheet reported as succeeded.
Collect children of a mixed order (empty/absent for a normal order).
curl -X POST "https://www.membber.com/api/v1/orders/confirm" \
-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",
"payment_intent_id": "0b6642a5-0000-4000-8000-d0c50000000b"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.confirmOrder({
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
payment_intent_id: "0b6642a5-0000-4000-8000-d0c50000000b"
},
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.confirmOrder(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
paymentIntentId: "0b6642a5-0000-4000-8000-d0c50000000b"
))
).ok.body.json
print(response){
"order_id": "4991ffac-0000-4000-8000-d0c500000049",
"order_number": "<order_number>",
"status": "<status>",
"total_pence": 1500,
"currency": "GBP",
"subtotal_pence": 1500,
"discount_pence": 1500,
"promo_discount_pence": 1500,
"voucher_discount_pence": 1500,
"applied_promo_code": "EXAMPLE10",
"applied_promo_label": "<applied_promo_label>",
"payment_intent_id": "0b6642a5-0000-4000-8000-d0c50000000b",
"order_type": "<order_type>",
"collection_code": "EXAMPLE10",
"fulfilment_status": "<fulfilment_status>",
"prep_minutes": 1,
"collection_mode": "<collection_mode>",
"requested_collection_at": "<requested_collection_at>",
"loyalty_awarded": true,
"stamps_added": 1,
"stamp_count": 1,
"goal": 1,
"voucher_minted": true,
"reward_title": "<reward_title>",
"is_first_order": true,
"reward_unlock_message": "Added at the front desk",
"welcome_message": "Added at the front desk",
"children": [
{
"order_id": "4991ffac-0000-4000-8000-d0c500000049",
"order_number": "<order_number>",
"order_type": "<order_type>",
"collection_code": "EXAMPLE10",
"fulfilment_status": "<fulfilment_status>",
"prep_minutes": 1,
"requested_collection_at": "<requested_collection_at>",
"total_pence": 1500
}
]
}