Skip to content

Marketing

Endpoints for the Marketing tools: your campaign dashboard, audience and segments, campaigns, templates, automations (“journeys”) and sender identity. See Marketing for the full feature guide. Every endpoint here is operator (client role) and scoped to your own account - send an Authorization: Bearer token from the Authentication flow.

Method Path Purpose Auth
GET /api/my-marketing/summary Dashboard: KPIs, funnel, recent and scheduled sends operator
GET /api/my-marketing/usage Per-channel send allowance for a billing period operator
GET /api/my-marketing/audience Preview who a send would reach operator
GET /api/my-marketing/audience-insights Richer reach and engagement analytics operator
GET /api/my-marketing/segments List saved audience segments operator
POST /api/my-marketing/segments Save a segment operator
PUT /api/my-marketing/segments/:id Update a saved segment operator
DELETE /api/my-marketing/segments/:id Delete a saved segment operator
POST /api/my-marketing/segments/preview Preview sample guests for ad-hoc rules operator
POST /api/my-marketing/segments/reach Live reach count for ad-hoc rules operator
GET /api/my-marketing/segment-attrs Fields and operators available to build a segment operator
GET /api/my-marketing/campaigns List campaigns operator
POST /api/my-marketing/campaigns Create a draft campaign operator
GET /api/my-marketing/campaigns/:id Read one campaign, with recipients operator
PUT /api/my-marketing/campaigns/:id Update a draft campaign operator
DELETE /api/my-marketing/campaigns/:id Delete a campaign operator
POST /api/my-marketing/campaigns/:id/duplicate Clone a campaign into a new draft operator
POST /api/my-marketing/campaigns/:id/send Send or schedule a campaign operator
POST /api/my-marketing/campaigns/:id/pause Pause a sending/scheduled campaign operator
POST /api/my-marketing/campaigns/:id/resume Resume a paused campaign operator
POST /api/my-marketing/campaigns/:id/cancel Cancel a campaign operator
GET /api/my-marketing/templates List email templates operator
POST /api/my-marketing/templates Create a template operator
GET /api/my-marketing/templates/:id Read one template operator
PUT /api/my-marketing/templates/:id Update a template operator
DELETE /api/my-marketing/templates/:id Delete a template operator
POST /api/my-marketing/templates/:id/duplicate Clone a template operator
GET /api/my-marketing/journeys List automations operator
POST /api/my-marketing/journeys Create an automation operator
PATCH /api/my-marketing/journeys/:id Edit an automation, or toggle it on/off operator
DELETE /api/my-marketing/journeys/:id Delete an automation operator
PATCH /api/my-marketing/journeys/:id/venues/:venueId Per-venue on/off override operator
POST /api/my-marketing/images Upload an image for templates/campaigns operator
POST /api/my-marketing/preview-render Render final campaign HTML operator
POST /api/my-marketing/test-send Send a test email operator
GET /api/my-marketing/settings Marketing-wide settings (double opt-in) operator
POST /api/my-marketing/settings Update marketing-wide settings operator
GET /api/my-marketing/sender-identity The address campaigns send from operator
GET /api/my-operator/marketing Operator-level sender defaults operator
POST /api/my-marketing/aimy/quietfill-draft Aimy: draft a campaign for a quiet slot operator, Aimy add-on
POST /api/my-marketing/aimy/section Aimy: generate an email section from a prompt operator, Aimy add-on

GET /api/my-marketing/summary is the Marketing dashboard: send/delivery/open/click totals, a funnel through to reservations and revenue, your best-performing and upcoming scheduled campaigns, automation health, a per-channel breakdown, and a daily/weekly/monthly series for charting. days is 7, 30, 90 or 365 (default 30).

Terminal window
curl "https://thesidedoor.co/api/my-marketing/summary?days=30" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"kpi": { "sent": 4200, "delivered": 4100, "opened": 1800, "clicked": 420, "open_rate": 44, "click_rate": 10, "revenue_pence": 86000 },
"recent": [{ "id": "cmp_1", "name": "Summer launch", "status": "sent", "sent_at": "2026-07-01T09:00:00Z" }],
"scheduled": [],
"channels": { "email": { "sent": 3800 }, "sms": { "sent": 400 }, "push": { "sent": 0 } }
}

GET /api/my-marketing/usage returns how much of this billing period’s per-channel send allowance you’ve used. period defaults to the current period.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/usage" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{ "channel": "email", "used": 3800, "allowance": 10000, "remaining": 6200 }

Hitting 0 remaining doesn’t block this read - it blocks the send endpoints elsewhere in the API.

GET /api/my-marketing/audience previews who a send would reach for a given audience type (all, recent, lapsed, venue or segment), with totals, how many have opted out, and a short sample of recipients. GET /api/my-marketing/audience-insights returns the richer analytics version of the same scope: suppression, estimated volume, historical open/click rates and a new-vs-returning breakdown. Both take months, days, venueId, segmentId and channel (email, sms or push) to narrow the audience.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/audience?type=recent&months=3&channel=email" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"totalGuests": 1840,
"deliverable": 1790,
"optedOut": 50,
"eligibleEstimate": 1790,
"sample": [{ "name": "Sam Lee", "contact": "sam@example.com" }],
"credits": { "used": 3800, "allowance": 10000, "remaining": 6200 }
}

sample shows real guest names and contact details, so swap in your own fake values when trying this - don’t publish a real recipient list.

GET /api/my-marketing/segments lists saved audience segments. GET /api/my-marketing/segment-attrs returns the schema used to build one: the fields you can filter on, the comparison operators available for each, and supporting dropdown data (guest tags, loyalty tags, months, weekdays) - useful if you’re building your own segment-rule editor against the segments API.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/segments" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{ "data": [{ "id": "seg_1", "name": "Big spenders", "rules": [{ "field": "total_spend_pence", "op": "gte", "value": 50000 }] }] }

POST /api/my-marketing/segments saves a reusable segment - send { "name", "match": "all" | "any", "rules": [...] }, where each rule names a field from /segment-attrs, an op, and a value. PUT /api/my-marketing/segments/:id updates a saved segment’s name or rules; DELETE /api/my-marketing/segments/:id removes it. Before saving, POST /api/my-marketing/segments/preview takes the same { match, rules } shape for an ad-hoc (unsaved) rule set and returns a reach estimate plus up to 20 sample guests; POST /api/my-marketing/segments/reach returns just the live count, useful while a rule editor is still being adjusted.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/segments" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Big spenders", "match": "all", "rules": [{"field": "total_spend_pence", "op": "gte", "value": 50000}]}'
curl -X POST "https://thesidedoor.co/api/my-marketing/segments/reach" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"match": "all", "rules": [{"field": "total_spend_pence", "op": "gte", "value": 50000}]}'
{ "ok": true, "id": "seg_1" }
{ "reach": 214, "sample": [{ "name": "Sam Lee", "contact": "sam@example.com" }] }

GET /api/my-marketing/campaigns lists campaigns with their results, sortable and searchable with q, sort, dir, channel, status, limit (max 200) and page. GET /api/my-marketing/campaigns/:id reads one campaign in full, including its recipients (up to 500) and, for an A/B test, each variant’s performance.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/campaigns?status=sent&limit=50" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
curl "https://thesidedoor.co/api/my-marketing/campaigns/cmp_1" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"data": [
{ "id": "cmp_1", "name": "Summer launch", "channel": "email", "status": "sent", "total_recipients": 1800, "sent_count": 1800, "opened_count": 790, "clicked_count": 180 }
]
}
{
"id": "cmp_1",
"name": "Summer launch",
"channel": "email",
"subject": "Summer's here",
"status": "sent",
"recipients": [{ "contact": "sam@example.com", "status": "opened", "open_count": 1 }]
}

contact on a recipient row is a real guest email or phone - fake it in your own examples.

POST /api/my-marketing/campaigns creates a draft - send { "name", "subject", "channel": "email" | "sms" | "push", "body_html" (or body_text), "template_id"? }, plus ab for an A/B test and sender overrides (from_name, reply_to, …) if you don’t want the account defaults. PUT /api/my-marketing/campaigns/:id updates a campaign with the same body shape, but only while it’s still a draft (409 otherwise). POST /api/my-marketing/campaigns/:id/duplicate clones a campaign’s content and settings into a fresh draft (not its audience or stats). DELETE /api/my-marketing/campaigns/:id removes a campaign (409 while it’s sending).

POST /api/my-marketing/campaigns/:id/send sends now, or schedules it with { "scheduled_at" }; pass segment to target an audience the same way as audience preview. It enforces your plan’s send limits (402 with over_limit if you’re out of allowance). Once sending or scheduled, POST .../pause pauses it, POST .../resume restarts or re-schedules it, and POST .../cancel cancels it for good.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/campaigns" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Summer launch", "subject": "Summer'"'"'s here", "channel": "email", "body_html": "<p>...</p>"}'
curl -X POST "https://thesidedoor.co/api/my-marketing/campaigns/cmp_1/send" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"segment": {"type": "all"}}'
{ "ok": true, "id": "cmp_1" }
{ "ok": true, "sending": true, "recipients": 1800 }

A scheduled send instead returns { "ok": true, "scheduled": true, "recipients": 1800 }.

GET /api/my-marketing/templates lists your reusable email templates. GET /api/my-marketing/templates/:id reads one, including its body_html. The design field on a template is the visual builder’s own internal model - treat it as opaque and edit templates through the portal rather than hand-authoring it.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/templates" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{ "data": [{ "id": "tpl_1", "name": "Welcome email", "thumbnail_url": "https://...", "system_key": null }] }

POST /api/my-marketing/templates creates a template - send { "name", "body_html", "design"?, "thumbnail_url"? }. PUT /api/my-marketing/templates/:id updates one; DELETE /api/my-marketing/templates/:id deletes one (400 if it’s one of Sidedoor’s non-deletable default templates). POST /api/my-marketing/templates/:id/duplicate clones a template under a new id.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/templates" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Welcome email", "body_html": "<p>...</p>"}'
{ "ok": true, "id": "tpl_1" }

GET /api/my-marketing/journeys lists your automations: triggered sends like a welcome email or a post-visit review request, each with its trigger, channel(s) and send counts, plus any per-venue on/off overrides.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/journeys" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"data": [
{ "id": "jrn_1", "name": "Post-visit review request", "channel": "email", "trigger_type": "post_visit", "active": true, "sent_count": 920, "opened_count": 410 }
]
}

POST /api/my-marketing/journeys creates an automation - send { "name", "trigger_type", "channels": { "email"?, "sms"?, "push"? }, ... } plus the timing fields for that trigger (e.g. days/hours after the trigger fires). PATCH /api/my-marketing/journeys/:id edits an automation’s name, content or timing, or just toggles it with { "active": boolean }. DELETE /api/my-marketing/journeys/:id deletes an automation and any per-venue overrides it has. If an automation’s global switch is off, PATCH /api/my-marketing/journeys/:id/venues/:venueId with { "active": true } turns it on for one venue only (409 if the global switch is already on - turn that off first).

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/journeys" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Post-visit review request", "trigger_type": "post_visit", "days": 1, "channels": {"email": true}}'
{ "ok": true, "id": "jrn_1" }

GET /api/my-marketing/settings returns double_optin - whether a new email subscriber must confirm before you can send to them; POST /api/my-marketing/settings sets it with { "double_optin": boolean }. GET /api/my-marketing/sender-identity returns the address your campaigns send from: sending_address, from_name and reply_to. GET /api/my-operator/marketing is the operator-wide counterpart - the same kind of sender defaults, plus your SMS sending number and its compliance status, read via your general operator-settings permissions rather than the marketing permission family above.

Terminal window
curl "https://thesidedoor.co/api/my-marketing/sender-identity" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
curl "https://thesidedoor.co/api/my-operator/marketing" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{ "sending_address": "the-blue-room@mail.thesidedoor.co", "from_name": "The Blue Room", "reply_to": "hello@blueroom.example" }
{
"from_name": "The Blue Room",
"from_email": "the-blue-room@mail.thesidedoor.co",
"reply_to": "hello@blueroom.example",
"sms_from_number": "+44...",
"sms_compliance_status": "approved"
}
Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/settings" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"double_optin": true}'
{ "ok": true, "double_optin": true }

POST /api/my-marketing/images uploads an image for use in a template or campaign - send a base64 data URL (JPEG, PNG, GIF or WebP, 3MB max) as { "image": "data:image/png;base64,..." } and get back a hosted url to reference in your HTML. POST /api/my-marketing/preview-render renders a campaign’s final HTML exactly as a real send would - merge fields resolved and the compliance footer added, with tracking disabled - from { "body_html", "subject", "preview_text" }, so you can show an accurate preview without actually sending anything. POST /api/my-marketing/test-send sends a one-off test email of { "subject", "body_html", "reply_to"? } to an address (to, defaulting to your own account email), with merge fields resolved against sample data.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/preview-render" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body_html": "<p>Hi {{first_name}}</p>", "subject": "Summer'"'"'s here", "preview_text": "Don'"'"'t miss out"}'
curl -X POST "https://thesidedoor.co/api/my-marketing/test-send" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"subject": "Summer'"'"'s here", "body_html": "<p>Hi {{first_name}}</p>"}'
{ "ok": true, "url": "https://assets.thesidedoor.co/marketing/img_1.png" }
{ "ok": true, "html": "<html>...</html>", "subject": "Summer's here" }
{ "ok": true, "sent_to": "you@example.com" }

Two endpoints use the Aimy add-on (metered, see Aimy, your portal assistant) to help write marketing content. POST /api/my-marketing/aimy/quietfill-draft drafts a campaign to fill a specific quiet slot - send { "venue", "weekday", "band" } (the day and time band you want to fill) and get back a draft plus token usage. POST /api/my-marketing/aimy/section turns a plain-language instruction into one email-safe HTML section for the campaign composer - send { "prompt" } (600 characters max). Both return 403 if the add-on isn’t enabled or you’re out of allowance for the period.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-marketing/aimy/section" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prompt": "A short section inviting guests back for happy hour on Fridays"}'
{
"html": "<table>...</table>",
"tokens": 180,
"cached": false,
"usage": { "allowance": 5000, "used": 1380, "remaining": 3620 }
}

quietfill-draft returns the same tokens/cached/usage shape alongside draft, the generated campaign content for that slot.