11 operations. Every schema and example on this page is generated from the platform contract.
The store’s campaigns, newest first, with the deliverability-suspension flag the composer must surface.
Authorises the merchant; campaigns are store-scoped.
allsegmentmanualTrue when deliverability protection has paused this store’s marketing sends.
curl -G "https://www.membber.com/api/v1/campaigns" \
-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/campaigns", {
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.listCampaigns2(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"campaigns": [
{
"id": "00000d1b-0000-4000-8000-d0c500000000",
"name": "Example name",
"subject": "<subject>",
"template_type": "<template_type>",
"target_type": "all",
"status": "<status>",
"scheduled_at": "<scheduled_at>",
"sent_at": "<sent_at>",
"total_recipients": 1,
"total_sent": 1,
"total_delivered": 1,
"total_opened": 1,
"total_clicked": 1,
"created_at": "<created_at>"
}
],
"email_sending_suspended": true
}Creates a draft. Nothing sends until the send endpoint is called.
custompromowe_miss_youeventupdateCustom template only; branded templates render server-side.
allsegmentmanualAd-hoc audience filter (there are no stored segments); resolved live at send time.
championloyalregularcasualnewnonelowmediumhighThe merchant’s own headline. Replaces the hardcoded ‘Special Offer’.
The merchant’s own message. One paragraph per line; {{first_name}}/{{store_name}} survive to send time.
WHAT THE OFFER IS, in their words: “2 for 1 on all pastries”. The only thing the highlighted box may contain.
The code a member shows or types. Rendered in its own block.
When it ends. Printed in the STORE’s timezone, never UTC. Empty string clears it.
The merchant’s own photo of what is on offer. https only; missing means no image, never a grey box. Empty string clears it.
The button’s words. Omit for the template’s own default.
Where the button goes. Omit for the store’s own page, then their website. NEVER the platform homepage. Empty string clears it.
allsegmentmanualThe merchant’s own headline. Replaces the hardcoded ‘Special Offer’.
The merchant’s own message. One paragraph per line; {{first_name}}/{{store_name}} survive to send time.
WHAT THE OFFER IS, in their words: “2 for 1 on all pastries”. The only thing the highlighted box may contain.
The code a member shows or types. Rendered in its own block.
When it ends. Printed in the STORE’s timezone, never UTC. Empty string clears it.
The merchant’s own photo of what is on offer. https only; missing means no image, never a grey box. Empty string clears it.
The button’s words. Omit for the template’s own default.
Where the button goes. Omit for the store’s own page, then their website. NEVER the platform homepage. Empty string clears it.
curl -X POST "https://www.membber.com/api/v1/campaigns" \
-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",
"name": "Example name"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
name: "Example name"
},
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.createCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
name: "Example name"
))
).ok.body.json
print(response){
"campaign": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"name": "Example name",
"subject": "<subject>",
"template_type": "<template_type>",
"target_type": "all",
"status": "<status>",
"scheduled_at": "<scheduled_at>",
"sent_at": "<sent_at>",
"total_recipients": 1,
"total_sent": 1,
"total_delivered": 1,
"total_opened": 1,
"total_clicked": 1,
"created_at": "<created_at>",
"preview_text": "<preview_text>",
"html_content": "<html_content>",
"target_filter": {},
"target_customer_ids": [
"dde07b6e-0000-4000-8000-d0c5000000dd"
],
"total_bounced": 1,
"total_unsubscribed": 1,
"headline": "<headline>",
"body_text": "<body_text>",
"offer_detail": "<offer_detail>",
"promo_code": "EXAMPLE10",
"offer_expires_at": "2026-07-01T09:00:00Z",
"image_url": "https://example.com",
"cta_label": "<cta_label>",
"cta_url": "https://example.com"
}
}Matched vs reachable (consent-filtered) counts. Requires the segmentation entitlement for filters.
allsegmentAd-hoc audience filter (there are no stored segments); resolved live at send time.
championloyalregularcasualnewnonelowmediumhighMembers matching the filter.
The REAL sendable number after consent/bounce/suppression filtering, show this one.
curl -X POST "https://www.membber.com/api/v1/campaigns/audience-preview" \
-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",
"target_type": "all"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/audience-preview", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
target_type: "all"
},
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.campaignAudiencePreview(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
targetType: .all
))
).ok.body.json
print(response){
"matched": 1,
"reachable": 1
}Scheduled → cancelled before the cron picks it up. Sent campaigns cannot be cancelled.
curl -X POST "https://www.membber.com/api/v1/campaigns/cancel" \
-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",
"campaign_id": "7c3416aa-0000-4000-8000-d0c50000007c"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/cancel", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c"
},
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.cancelCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
campaignId: "7c3416aa-0000-4000-8000-d0c50000007c"
))
).ok.body.json
print(response){
"status": "<status>"
}Drafts and cancelled campaigns only. A sent campaign keeps its delivery record and can never be deleted; a scheduled one must be cancelled first.
curl -X POST "https://www.membber.com/api/v1/campaigns/delete" \
-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",
"campaign_id": "7c3416aa-0000-4000-8000-d0c50000007c"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/delete", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c"
},
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.deleteCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
campaignId: "7c3416aa-0000-4000-8000-d0c50000007c"
))
).ok.body.json
print(response){
"ok": true
}Full campaign row including delivery/open/click rollups.
allsegmentmanualThe merchant’s own headline. Replaces the hardcoded ‘Special Offer’.
The merchant’s own message. One paragraph per line; {{first_name}}/{{store_name}} survive to send time.
WHAT THE OFFER IS, in their words: “2 for 1 on all pastries”. The only thing the highlighted box may contain.
The code a member shows or types. Rendered in its own block.
When it ends. Printed in the STORE’s timezone, never UTC. Empty string clears it.
The merchant’s own photo of what is on offer. https only; missing means no image, never a grey box. Empty string clears it.
The button’s words. Omit for the template’s own default.
Where the button goes. Omit for the store’s own page, then their website. NEVER the platform homepage. Empty string clears it.
curl -G "https://www.membber.com/api/v1/campaigns/detail" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
--data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
--data-urlencode "campaign_id=7c3416aa-0000-4000-8000-d0c50000007c"import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.GET("/api/v1/campaigns/detail", {
params: { query: { store_id: "6659c139-0000-4000-8000-d0c500000066", campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c" } },
});
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.getCampaign2(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066", campaignId: "7c3416aa-0000-4000-8000-d0c50000007c")
).ok.body.json
print(response){
"campaign": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"name": "Example name",
"subject": "<subject>",
"template_type": "<template_type>",
"target_type": "all",
"status": "<status>",
"scheduled_at": "<scheduled_at>",
"sent_at": "<sent_at>",
"total_recipients": 1,
"total_sent": 1,
"total_delivered": 1,
"total_opened": 1,
"total_clicked": 1,
"created_at": "<created_at>",
"preview_text": "<preview_text>",
"html_content": "<html_content>",
"target_filter": {},
"target_customer_ids": [
"dde07b6e-0000-4000-8000-d0c5000000dd"
],
"total_bounced": 1,
"total_unsubscribed": 1,
"headline": "<headline>",
"body_text": "<body_text>",
"offer_detail": "<offer_detail>",
"promo_code": "EXAMPLE10",
"offer_expires_at": "2026-07-01T09:00:00Z",
"image_url": "https://example.com",
"cta_label": "<cta_label>",
"cta_url": "https://example.com"
},
"email_sending_suspended": true
}Server-rendered branded HTML with sample personalisation tokens, the composer shows this in a web view.
curl -G "https://www.membber.com/api/v1/campaigns/preview" \
-H "Authorization: Bearer $MEMBBER_TOKEN" \
--data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
--data-urlencode "campaign_id=7c3416aa-0000-4000-8000-d0c50000007c"import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.GET("/api/v1/campaigns/preview", {
params: { query: { store_id: "6659c139-0000-4000-8000-d0c500000066", campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c" } },
});
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.previewCampaign2(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066", campaignId: "7c3416aa-0000-4000-8000-d0c50000007c")
).ok.body.json
print(response){
"html": "<html>"
}Queues the campaign to the consent-filtered marketable audience (unsubscribed, withdrawn, bounced and anonymized members are always excluded). Suspended stores queue nothing, check the suspension flag first.
Omit to send now; ISO datetime to schedule.
curl -X POST "https://www.membber.com/api/v1/campaigns/send" \
-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",
"campaign_id": "7c3416aa-0000-4000-8000-d0c50000007c"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/send", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c"
},
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.sendCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
campaignId: "7c3416aa-0000-4000-8000-d0c50000007c"
))
).ok.body.json
print(response){
"status": "<status>",
"total_recipients": 1
}The branded template pick-list (promo, win-back, event, update, custom).
curl -G "https://www.membber.com/api/v1/campaigns/templates" \
-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/campaigns/templates", {
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.campaignTemplates(
query: .init(storeId: "6659c139-0000-4000-8000-d0c500000066")
).ok.body.json
print(response){
"templates": [
{
"type": "<type>",
"name": "Example name",
"description": "Added at the front desk"
}
]
}Renders with sample tokens and sends to the signed-in merchant’s own email, subject-prefixed [TEST].
curl -X POST "https://www.membber.com/api/v1/campaigns/test" \
-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",
"campaign_id": "7c3416aa-0000-4000-8000-d0c50000007c"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/test", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c"
},
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.testCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
campaignId: "7c3416aa-0000-4000-8000-d0c50000007c"
))
).ok.body.json
print(response){
"sent_to": "<sent_to>"
}Drafts only, scheduled/sent campaigns are immutable (cancel first).
custompromowe_miss_youeventupdateallsegmentmanualAd-hoc audience filter (there are no stored segments); resolved live at send time.
championloyalregularcasualnewnonelowmediumhighThe merchant’s own headline. Replaces the hardcoded ‘Special Offer’.
The merchant’s own message. One paragraph per line; {{first_name}}/{{store_name}} survive to send time.
WHAT THE OFFER IS, in their words: “2 for 1 on all pastries”. The only thing the highlighted box may contain.
The code a member shows or types. Rendered in its own block.
When it ends. Printed in the STORE’s timezone, never UTC. Empty string clears it.
The merchant’s own photo of what is on offer. https only; missing means no image, never a grey box. Empty string clears it.
The button’s words. Omit for the template’s own default.
Where the button goes. Omit for the store’s own page, then their website. NEVER the platform homepage. Empty string clears it.
allsegmentmanualThe merchant’s own headline. Replaces the hardcoded ‘Special Offer’.
The merchant’s own message. One paragraph per line; {{first_name}}/{{store_name}} survive to send time.
WHAT THE OFFER IS, in their words: “2 for 1 on all pastries”. The only thing the highlighted box may contain.
The code a member shows or types. Rendered in its own block.
When it ends. Printed in the STORE’s timezone, never UTC. Empty string clears it.
The merchant’s own photo of what is on offer. https only; missing means no image, never a grey box. Empty string clears it.
The button’s words. Omit for the template’s own default.
Where the button goes. Omit for the store’s own page, then their website. NEVER the platform homepage. Empty string clears it.
curl -X POST "https://www.membber.com/api/v1/campaigns/update" \
-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",
"campaign_id": "7c3416aa-0000-4000-8000-d0c50000007c"
}'import { createMembberClient } from "@membber/sdk-ts";
const membber = createMembberClient({
getAccessToken: () => process.env.MEMBBER_TOKEN,
});
const { data, error } = await membber.raw.POST("/api/v1/campaigns/update", {
body: {
store_id: "6659c139-0000-4000-8000-d0c500000066",
campaign_id: "7c3416aa-0000-4000-8000-d0c50000007c"
},
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.updateCampaign2(
body: .json(.init(
storeId: "6659c139-0000-4000-8000-d0c500000066",
campaignId: "7c3416aa-0000-4000-8000-d0c50000007c"
))
).ok.body.json
print(response){
"campaign": {
"id": "00000d1b-0000-4000-8000-d0c500000000",
"name": "Example name",
"subject": "<subject>",
"template_type": "<template_type>",
"target_type": "all",
"status": "<status>",
"scheduled_at": "<scheduled_at>",
"sent_at": "<sent_at>",
"total_recipients": 1,
"total_sent": 1,
"total_delivered": 1,
"total_opened": 1,
"total_clicked": 1,
"created_at": "<created_at>",
"preview_text": "<preview_text>",
"html_content": "<html_content>",
"target_filter": {},
"target_customer_ids": [
"dde07b6e-0000-4000-8000-d0c5000000dd"
],
"total_bounced": 1,
"total_unsubscribed": 1,
"headline": "<headline>",
"body_text": "<body_text>",
"offer_detail": "<offer_detail>",
"promo_code": "EXAMPLE10",
"offer_expires_at": "2026-07-01T09:00:00Z",
"image_url": "https://example.com",
"cta_label": "<cta_label>",
"cta_url": "https://example.com"
}
}