5 operations. Every schema and example on this page is generated from the platform contract.
The three store-wide switches (Pay now, Bank transfer, Cash when they collect), the bank details on file, and whether Stripe can honour Pay now right now. Owner only: the bank details are on this payload.
The shop's choice of how buyers pay, and whether Stripe is ready to honour Pay now.
Apple Pay, Google Pay or card, through Stripe.
The buyer sends the money from their own bank; the shop marks it paid.
Cash at the counter, recorded at handover.
Where a buyer sends a bank transfer for this shop.
As the bank shows it; Confirmation of Payee checks this first.
Six digits, no separators.
Eight digits.
The connected account's rail, when one exists.
testliveCards are not set up yet: this real shop is still on its old practice Stripe account, which takes no card. "Set up cards" makes its live account.
curl -G "https://www.membber.com/api/v1/payments/how-buyers-pay" \
-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/payments/how-buyers-pay", {
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.getHowBuyersPay(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"how_buyers_pay": {
"pay_now_enabled": true,
"pay_bank_transfer_enabled": true,
"pay_cash_enabled": true,
"bank": {
"account_name": "<account_name>",
"sort_code": "EXAMPLE10",
"account_number": "<account_number>"
},
"stripe_ready": true,
"stripe_mode": "test",
"card_practice_only": true
}
}Writes the three switches and the bank details in one transaction. Refuses with AT_LEAST_ONE_WAY_TO_PAY when every switch would be off, and with BANK_FIELD_MISSING naming the field when bank transfer is on without complete details. Owner only. Idempotent on the request body.
Six digits; separators are stripped.
Eight digits; spaces are stripped.
The shop's choice of how buyers pay, and whether Stripe is ready to honour Pay now.
Apple Pay, Google Pay or card, through Stripe.
The buyer sends the money from their own bank; the shop marks it paid.
Cash at the counter, recorded at handover.
Where a buyer sends a bank transfer for this shop.
As the bank shows it; Confirmation of Payee checks this first.
Six digits, no separators.
Eight digits.
The connected account's rail, when one exists.
testliveCards are not set up yet: this real shop is still on its old practice Stripe account, which takes no card. "Set up cards" makes its live account.
curl -X PUT "https://www.membber.com/api/v1/payments/how-buyers-pay" \
-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",
"pay_now_enabled": true,
"pay_bank_transfer_enabled": true,
"pay_cash_enabled": true
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.PUT("/api/v1/payments/how-buyers-pay", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
pay_now_enabled: true,
pay_bank_transfer_enabled: true,
pay_cash_enabled: 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.updateHowBuyersPay(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
payNowEnabled: true,
payBankTransferEnabled: true,
payCashEnabled: true
))
).ok.body.json
print(response){
"how_buyers_pay": {
"pay_now_enabled": true,
"pay_bank_transfer_enabled": true,
"pay_cash_enabled": true,
"bank": {
"account_name": "<account_name>",
"sort_code": "EXAMPLE10",
"account_number": "<account_number>"
},
"stripe_ready": true,
"stripe_mode": "test",
"card_practice_only": true
}
}Requires an Idempotency-Key header (a UUID v4). Owner only. Offered only to a Drops shop in GBP; a real shop also needs the live switch and the published seller terms. Refusals: 409 ALREADY_ON_OWN_STRIPE (a live Stripe account of her own is in use and is never replaced), 409 QUICK_SETUP_UNAVAILABLE (details.reason says why), 409 TERMS_CHANGED (show the terms again), 503 CARD_SETUP_PAUSED (Stripe is not accepting new accounts from Membber for a moment). Idempotent: a repeat, or any call once the quick account exists, returns its state. Where that account was agreed to under an older version of the seller terms, the call is also her acceptance of terms_version, which must be the current one (else 409 TERMS_CHANGED), recorded first.
The shop (also authorises the merchant; owner only).
The version of the "Set up with Membber" seller terms the screen showed. A stale version is refused with 409 TERMS_CHANGED.
Where her card set-up is: 'onboarding' (carry on in the component), 'needs_more' (Stripe needs something), 'on' (cards on: Stripe enabled charges AND payouts), 'problem' (turned off or refused).
noneonboardingneeds_moreonproblemThe rail the account is on: a test shop's is 'test'.
testliveTrue when she already had a quick set-up (or this request was a repeat) and nothing new was made.
curl -X POST "https://www.membber.com/api/v1/payments/quick-setup" \
-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",
"terms_version": 1
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/payments/quick-setup", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
terms_version: 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.startQuickSetup(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
termsVersion: 1
))
).ok.body.json
print(response){
"setup": "<setup>",
"state": "none",
"stripe_mode": "test",
"resumed": true
}Creates a NEW session on every call, as Stripe requires of fetchClientSecret, so it is safe to call again whenever the component asks. Owner only. 409 NOT_QUICK_SETUP when the shop has not started quick set-up. 409 TERMS_CHANGED when the shop agreed to an older version of the seller terms: startQuickSetup at the current terms_version records her acceptance, then this answers again.
The shop (owner only).
The Account Session secret for Stripe's embedded components. Short-lived; never store or log it.
Stripe publishable key for the account's rail. Use a dedicated client with it; never overwrite a shared one.
testliveWhether this session lets her change her bank. Always true where Stripe collects her details (Stripe asks her to sign in itself). On a legacy sandbox account: false once cards are on and she has not signed in to Membber within 15 minutes.
curl -X POST "https://www.membber.com/api/v1/payments/quick-setup/session" \
-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/payments/quick-setup/session", {
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.createQuickSetupSession(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066"
))
).ok.body.json
print(response){
"client_secret": "<client_secret>",
"publishable_key": "<publishable_key>",
"stripe_mode": "test",
"can_change_bank": true
}Idempotent. A store that already has any connected account, half-finished onboarding included, is returned untouched, and a store that has already gone live is never touched at all. Only a TEST store is given one: a real store connects its own live Stripe account instead, and is answered outcome 'failed' with reason 'real_store_connects_live' (founder, 29 Sep 2026). Provisioning is best-effort: on failure the store is left exactly as it was, unable to take a payment, which is what it already was.
Store to give a sandbox till to (also authorises the merchant; owner only).
'existing' means the store already had a connected account and nothing was done, including a store that has already gone live, which is never touched.
createdexistingfailedWhether the account can actually take a card right now.
Why it did nothing, or why it failed. Present for diagnosis, not for display.
curl -X POST "https://www.membber.com/api/v1/payments/sandbox-account" \
-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/payments/sandbox-account", {
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.createSandboxAccount(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066"
))
).ok.body.json
print(response){
"outcome": "created",
"stripe_account_id": "8d79cf9f-0000-4000-8000-d0c50000008d",
"charges_enabled": true,
"stripe_mode": "<stripe_mode>",
"reason": "Added at the front desk"
}