3 operations. Every schema and example on this page is generated from the platform contract.
Every store on the platform, name, code, template, test marking, lifecycle status, billing status, resolved served state, whether the operator kill switch holds it closed, and real per-store activity counts (stamps, orders, current members). Operator-only; feeds the Membber HQ Operations surface.
'gym' or a standard template.
stores.status: draft | invited | claimed | live | paused.
'platform' = closed by the Membber operator; 'non_payment' = the billing pause.
What a customer is currently served, from the ONE resolver every customer surface uses: live | renewing | paused | own_app_paused | dark | not_live.
True when the kill switch holds this store closed (is_active with status=paused).
Total stamp rows ever collected at this store.
Total orders ever placed at this store.
Current gym members (active/trialing/past_due/frozen), deleted customers excluded.
ISO timestamp of the newest stamp/order/membership.
curl -G "https://www.membber.com/api/v1/ops/stores" \
-H "Authorization: Bearer $MEMBBER_TOKEN"import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.GET("/api/v1/ops/stores");
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.listOpsStores().ok.body.json
print(response){
"stores": [
{
"id": "00000d1b-0000-4000-8000-d0c500000000",
"name": "Example name",
"code": "EXAMPLE10",
"store_template": "<store_template>",
"is_test": true,
"is_active": true,
"status": "<status>",
"billing_status": "<billing_status>",
"pause_reason": "Added at the front desk",
"served_state": "<served_state>",
"operator_closed": true,
"stamp_count": 1,
"order_count": 1,
"member_count": 1,
"last_activity_at": "<last_activity_at>",
"created_at": "<created_at>"
}
]
}The image half of the review door the ambient film already has at /api/admin/bg-videos/{videoId}/approve. approve moves stores.pending_background_image_url into stores.background_image_url (the one column the Wallet pass strip, the mini-store backdrop, the App Clip and the booking card read) and tells every open customer app. reject drops the waiting picture and records the reason for the Business app. Neither touches the live picture until the moment of approval, so an issued pass and an open card keep the picture they were built with. Operator-only; the write is conditional on the store still being in pending_review, so a replayed decision writes nothing.
approve = the waiting picture becomes the live one every customer surface reads. reject = it is dropped and the picture customers already have stands.
approverejectWhy it was turned down, shown to the merchant in the Business app. Rejections only.
stores.background_image_status after the decision: published | rejected.
The LIVE, reviewed picture after the decision. Unchanged on a rejection.
curl -X POST "https://www.membber.com/api/v1/ops/stores/00000d1b-0000-4000-8000-d0c500000000/background-image-review" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"decision": "approve"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/ops/stores/{id}/background-image-review", {
params: { path: { id: "00000d1b-0000-4000-8000-d0c500000000" } },
body: {
decision: "approve"
},
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.reviewOpsStoreBackgroundImage(
path: .init(id: "00000d1b-0000-4000-8000-d0c500000000"),
body: .json(.init(
decision: .approve
))
).ok.body.json
print(response){
"ok": true,
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"status": "<status>",
"background_image_url": "https://example.com/image.jpg",
"review_notes": "Added at the front desk"
}OFF (off=true) writes status='paused' + pause_reason='platform': discovery hides the store from new customers, every activity endpoint refuses new writes with a kind message, prior customers keep a grayed read-only card, and the merchant's Business-app access is untouched. ON (off=false) reverses ONLY an operator pause, it can never publish a store that was not live. Naturally idempotent; every toggle is audited.
true = CLOSE the store to customers (pause). false = REOPEN (only reverses an operator pause).
Optional operator note, recorded in the audit log.
curl -X POST "https://www.membber.com/api/v1/ops/stores/00000d1b-0000-4000-8000-d0c500000000/killswitch" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
-H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
-H "Content-Type: application/json" \
-d '{
"off": true
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/ops/stores/{id}/killswitch", {
params: { path: { id: "00000d1b-0000-4000-8000-d0c500000000" } },
body: {
off: 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.setOpsStoreKillSwitch(
path: .init(id: "00000d1b-0000-4000-8000-d0c500000000"),
body: .json(.init(
off: true
))
).ok.body.json
print(response){
"ok": true,
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"status": "<status>",
"off": true,
"pause_reason": "Added at the front desk"
}