Billing & payouts
Endpoints for two separate things: billing is what you pay to Sidedoor
for your subscription and add-ons; payouts is what Sidedoor pays out to
your venues’ bank accounts from guest payments. Every endpoint is
operator (client role) and scoped to your own account - send an
Authorization: Bearer token from the
Authentication flow. Amounts are integer
pence (or minor currency units). Card and bank details are always
returned pre-redacted - brand and last 4 digits only, never a full card or
account number.
Endpoints
Section titled “Endpoints”| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/my-billing/plan |
Plan tiers, your current plan and bundled add-ons | operator |
| POST | /api/my-billing/plan |
Change plan | operator |
| GET | /api/my-billing/addons |
Your add-on selections and metered usage | operator |
| POST | /api/my-billing/addons |
Change an add-on’s tier | operator |
| GET | /api/my-billing/payment-method |
Card or Direct Debit, whichever you pay with | operator |
| PUT | /api/my-billing/payment-method |
Switch which rail you pay by | operator |
| GET | /api/my-billing/card |
Your saved subscription card | operator |
| POST | /api/my-billing/card/setup-intent |
Start capturing a new card | operator |
| POST | /api/my-billing/card/default |
Set the saved card as default | operator |
| POST | /api/my-billing/gocardless/setup |
Start setting up a Direct Debit mandate | operator |
| POST | /api/my-billing/gocardless/refresh |
Re-check a mandate’s live status | operator |
| GET | /api/my-billing/upcoming |
A live preview of your next invoice | operator |
| GET | /api/my-billing/invoices |
List invoices | operator |
| GET | /api/my-billing/invoices/:id |
One invoice, with its line items | operator |
| GET | /api/my-billing/invoices/:id/pdf |
Download one invoice as a PDF | operator |
| GET | /api/my-operator/payout/accounts |
Your Stripe Connect payout accounts | operator |
| GET | /api/my-operator/payout/groups |
Which venues pay out to which account | operator |
| GET | /api/my-payouts |
List payouts | operator |
| GET | /api/my-payouts/summary |
Payout totals (paid out, in transit, failed) | operator |
| GET | /api/my-payouts/:payoutId/breakdown |
What one payout is made up of | operator |
Plan and add-ons
Section titled “Plan and add-ons”GET /api/my-billing/plan returns every plan tier with its price and
bundled add-ons, your current plan, any scheduled plan change, and
complimentary add-ons granted to your account free of charge. GET /api/my-billing/addons returns your actual add-on selections and this
period’s metered usage per channel (email, SMS, push, Aimy tokens).
curl "https://thesidedoor.co/api/my-billing/plan" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"
curl "https://thesidedoor.co/api/my-billing/addons" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const plan = await fetch("https://thesidedoor.co/api/my-billing/plan", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },}).then((r) => r.json());
const addons = await fetch("https://thesidedoor.co/api/my-billing/addons", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },}).then((r) => r.json());$ch = curl_init("https://thesidedoor.co/api/my-billing/plan");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$plan = json_decode(curl_exec($ch), true);{ "plan": "growth", "pending_plan": null, "currency": "gbp", "plans": [{ "id": "growth", "base_price_pence": { "gbp": 9900 }, "bundled_addons": ["bi_advanced"] }], "complimentary": []}{ "addons": { "bi_advanced": { "active": true }, "aimy": { "active": true, "pending": false } }, "usage": { "email": { "used": 3800, "allowance": 10000 }, "aimy_tokens": { "used": 1200, "allowance": 5000 } }, "period": "2026-07"}POST /api/my-billing/plan changes plan - send { "plan": "growth" } with
one of the plan ids from the GET above. An upgrade applies immediately; a
downgrade is scheduled for the start of your next billing period and shows
up as pending_plan until then. POST /api/my-billing/addons changes a
single add-on’s tier - send { "addon": "aimy", "package_key": "standard" }.
Enabling an add-on (or upgrading its tier) applies immediately; disabling it
(or downgrading) is scheduled for next period in the same way.
curl -X POST "https://thesidedoor.co/api/my-billing/plan" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"plan": "growth"}'
curl -X POST "https://thesidedoor.co/api/my-billing/addons" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"addon": "aimy", "package_key": "standard"}'const plan = await fetch("https://thesidedoor.co/api/my-billing/plan", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ plan: "growth" }),}).then((r) => r.json());
const addon = await fetch("https://thesidedoor.co/api/my-billing/addons", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ addon: "aimy", package_key: "standard" }),}).then((r) => r.json());$ch = curl_init("https://thesidedoor.co/api/my-billing/plan");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}", "Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["plan" => "growth"]),]);$plan = json_decode(curl_exec($ch), true);{ "ok": true, "plan": "growth" }A scheduled downgrade instead returns { "ok": true, "scheduled": true, "effective_at": "2026-08-01" }.
{ "ok": true, "addon": "aimy", "package_key": "standard", "allowance": 5000, "price_pence": 2900 }A scheduled change (a downgrade or turning an add-on off) instead returns
{ "ok": true, "scheduled": true, "effective_at": "2026-08-01" }.
Payment method
Section titled “Payment method”GET /api/my-billing/payment-method returns which rail your subscription is
paid by - card or direct_debit - with the state of each. GET /api/my-billing/card returns just the card half: hasCard, and if true, its
brand and last4.
curl "https://thesidedoor.co/api/my-billing/payment-method" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch("https://thesidedoor.co/api/my-billing/payment-method", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-billing/payment-method");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "method": "card", "card": { "hasCard": true, "brand": "visa", "last4": "4242" }, "directDebit": { "mandate": false, "status": null }}PUT /api/my-billing/payment-method switches which rail your subscription
collects from - send { "method": "direct_debit" } or { "method": "card" }.
You need a card already saved, or a set-up Direct Debit mandate, before you
can switch to it.
To save a new card, POST /api/my-billing/card/setup-intent starts a Stripe
Elements capture and returns a client_secret to confirm client-side; once
Stripe confirms it, the card is saved automatically. POST /api/my-billing/card/default then marks a saved
payment_method id (from the Stripe confirmation) as the one to charge.
To start Direct Debit instead, POST /api/my-billing/gocardless/setup
returns an authorisationUrl - redirect the operator there to set up the
mandate with GoCardless - and POST /api/my-billing/gocardless/refresh
re-checks the mandate’s live status once they’re back.
curl -X PUT "https://thesidedoor.co/api/my-billing/payment-method" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"method": "direct_debit"}'
curl -X POST "https://thesidedoor.co/api/my-billing/card/setup-intent" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"
curl -X POST "https://thesidedoor.co/api/my-billing/gocardless/setup" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"await fetch("https://thesidedoor.co/api/my-billing/payment-method", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ method: "direct_debit" }),});
const setupIntent = await fetch( "https://thesidedoor.co/api/my-billing/card/setup-intent", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },).then((r) => r.json());$ch = curl_init("https://thesidedoor.co/api/my-billing/payment-method");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}", "Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode(["method" => "direct_debit"]),]);$data = json_decode(curl_exec($ch), true);{ "ok": true, "method": "direct_debit" }{ "client_secret": "seti_...", "publishable_key": "pk_live_..." }{ "authorisationUrl": "https://pay.gocardless.com/flow/..." }Upcoming invoice and invoice history
Section titled “Upcoming invoice and invoice history”GET /api/my-billing/upcoming computes a live preview of your next
invoice - nothing is saved or charged by reading it. GET /api/my-billing/invoices lists past invoices (search with q, filter with
status, sort with sort/dir). GET /api/my-billing/invoices/:id reads
one invoice with its line items.
curl "https://thesidedoor.co/api/my-billing/upcoming" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"
curl "https://thesidedoor.co/api/my-billing/invoices?limit=20" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const upcoming = await fetch("https://thesidedoor.co/api/my-billing/upcoming", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },}).then((r) => r.json());
const invoices = await fetch( "https://thesidedoor.co/api/my-billing/invoices?limit=20", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },).then((r) => r.json());$ch = curl_init("https://thesidedoor.co/api/my-billing/upcoming");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$upcoming = json_decode(curl_exec($ch), true);{ "period": "2026-08", "lines": [{ "kind": "plan", "description": "Growth plan", "amount_pence": 9900 }], "total_pence": 9900 }{ "data": [{ "id": "inv_1", "number": "INV-1042", "status": "paid", "total_pence": 9900, "issued_at": "2026-07-01T00:00:00Z" }], "balancePence": 0, "suspended": false}A single invoice adds lines: [{ "kind": "plan", "description": "Growth plan", "quantity": 1, "unit_pence": 9900, "amount_pence": 9900 }].
GET /api/my-billing/invoices/:id/pdf streams the invoice as a PDF
(Content-Type: application/pdf). It returns 404 if a PDF hasn’t been
generated yet for that invoice.
curl "https://thesidedoor.co/api/my-billing/invoices/INVOICE_ID/pdf" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -o invoice.pdfconst res = await fetch( "https://thesidedoor.co/api/my-billing/invoices/INVOICE_ID/pdf", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const pdfBytes = await res.arrayBuffer();$ch = curl_init("https://thesidedoor.co/api/my-billing/invoices/INVOICE_ID/pdf");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$pdf = curl_exec($ch);Payout accounts and routing
Section titled “Payout accounts and routing”GET /api/my-operator/payout/accounts lists the Stripe Connect accounts
your payouts go to. GET /api/my-operator/payout/groups shows which venues
are routed to which account, for operators with more than one.
curl "https://thesidedoor.co/api/my-operator/payout/accounts" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch("https://thesidedoor.co/api/my-operator/payout/accounts", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-operator/payout/accounts");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "accounts": [{ "id": "pacc_1", "label": "Main account", "status": "enabled", "is_default": true, "connected": true }] }{ "groups": [{ "id": "pgrp_1", "label": "Main account", "venue_ids": ["ven_123", "ven_456"] }], "venues": [{ "id": "ven_123", "name": "The Attic" }] }Payouts
Section titled “Payouts”GET /api/my-payouts lists payouts with search (q), filters (status,
from, to, venue) and sort (sort/dir); add ?format=csv to
download the filtered set as a file. GET /api/my-payouts/summary returns
just the totals. GET /api/my-payouts/:payoutId/breakdown reconciles one
payout into the sales, refunds and adjustments it’s made up of.
curl "https://thesidedoor.co/api/my-payouts?status=paid&limit=25" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-payouts?status=paid&limit=25", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-payouts?status=paid&limit=25");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "data": [ { "id": "po_1", "amount_minor": 420000, "fee_minor": 1200, "net_minor": 418800, "currency": "gbp", "status": "paid", "arrival_date": "2026-07-18", "bank_last4": "6789", "bank_name": "Example Bank" } ]}{ "paid_out": 4200000, "in_transit": 420000, "failed": 0, "payout_fees": 12000, "payouts": 42 }{ "data": { "payout_id": "po_1", "amount_minor": 420000, "gross_minor": 430000, "refunds_minor": 10000, "venues": [{ "id": "ven_123", "name": "The Attic" }], "sales": [{ "category": "deposits", "amount_minor": 300000 }], "adjustments": [] }}