3 operations. Every schema and example on this page is generated from the platform contract.
For a lapsed store (billing_status=suspended, prior_paid_tier=club|vip), mints a Stripe SetupIntent + ephemeral key so the native PaymentSheet can collect a card. Fails closed on an unconfigured price/key, a non-lapsed store, a gym tier (separate slice), or a still-live subscription (update the card instead). No charge here.
The lapsed store to renew (also authorises the caller).
Always "checkout", the client presents the native PaymentSheet.
SetupIntent client secret for the PaymentSheet (SETUP mode).
Customer ephemeral key secret for the PaymentSheet.
The platform Stripe customer id.
Stripe publishable key for the PaymentSheet.
The SetupIntent id, echoed back to /complete.
The standard tier being renewed (club | vip).
The monthly price in pence, for the confirm copy.
curl -X POST "https://www.membber.com/api/v1/business/billing/resubscribe/begin" \
-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/business/billing/resubscribe/begin", {
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.beginResubscribe(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066"
))
).ok.body.json
print(response){
"mode": "<mode>",
"client_secret": "<client_secret>",
"ephemeral_key_secret": "<ephemeral_key_secret>",
"customer_id": "96607d1c-0000-4000-8000-d0c500000096",
"publishable_key": "<publishable_key>",
"setup_intent_id": "fab3625c-0000-4000-8000-d0c5000000fa",
"tier": "<tier>",
"price_pence": 1500
}After the card is confirmed client-side, creates the standard-tier subscription (error_if_incomplete → the first invoice is charged now), guards the first charge (activates nothing on a decline), and reactivates the store to its prior paid tier (clearing prior_paid_tier so the renew banner disappears). Idempotent: reuses a sub created from this SetupIntent OR an already-paying sub for this tier, so a concurrent tap / retry never double-charges.
The lapsed store to renew (also authorises the caller).
The SetupIntent id returned by /begin, now card-confirmed.
The store is back on its paid plan.
The new (or reused) Stripe subscription id.
The tier reactivated (club | vip).
curl -X POST "https://www.membber.com/api/v1/business/billing/resubscribe/complete" \
-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",
"setup_intent_id": "fab3625c-0000-4000-8000-d0c5000000fa"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/business/billing/resubscribe/complete", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
setup_intent_id: "fab3625c-0000-4000-8000-d0c5000000fa"
},
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.completeResubscribe(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
setupIntentId: "fab3625c-0000-4000-8000-d0c5000000fa"
))
).ok.body.json
print(response){
"status": "<status>",
"subscription_id": "ac6da77d-0000-4000-8000-d0c5000000ac",
"tier": "<tier>"
}Merchant fetches their special events (one-off workshops, seminars, competitions, open days) for the store, ordered by event date ascending. Active events only by default; pass include_inactive=true to include inactive ones.
Store whose events to list (also authorises the merchant).
Pass 'true' to include inactive (unpublished/archived) events. Defaults to active only.
curl -G "https://www.membber.com/api/v1/business/events" \
-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/business/events", {
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.listBusinessEvents(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"events": [
{
"id": "00000d1b-0000-4000-8000-d0c500000000",
"store_id": "6659c139-0000-4000-8000-d0c500000066",
"name": "Example name",
"description": "Added at the front desk",
"event_date": "<event_date>",
"start_time": "<start_time>",
"end_time": "<end_time>",
"location": "<location>",
"price_pence": 1500,
"currency": "GBP",
"max_capacity": 1,
"is_members_only": true,
"is_active": true,
"image_url": "https://example.com/image.jpg",
"created_by": "<created_by>",
"created_at": "<created_at>",
"updated_at": "<updated_at>"
}
]
}