Reference
Billing
7 routes, generated from Morse’s own OpenAPI document. Every path below hangs off the base URL, and every one needs the bearer header.
The examples are built from each route’s schema, so the shapes and types are exactly what the API declares. The values are illustrative, and no one has run them.
Begin buying a plan. Returns the Stripe Checkout URL to send the browser to.
/billing/checkoutNothing is granted here: the session only starts the payment, and the plan follows from the webhook once Stripe says it was paid.
POST/billing/checkout
curl -X POST "$MORSE/billing/checkout" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "lookup_key": "…" }'{
"url": "https://onmorse.com/priya"
}The launch offer: thirty days of Scale, free, one to a customer.
/billing/free-monthNo Stripe and no card. It was £1.99 through Checkout; now nothing is
charged, so there is no session to send them to and the month starts here
rather than when a webhook lands.
passes already means "one per person, ever" and already expires itself, so
day 31 needs no sweeper: plan_for stops counting a pass the moment
expires_at passes.
POST/billing/free-month
curl -X POST "$MORSE/billing/free-month" \
-H "Authorization: Bearer $MORSE_TOKEN"What you are on: your plan, and every source of it — subscription, launch pass, and a comp Morse gave you.
/billing/meRead from our projection rather than from Stripe, so rendering the settings page does not depend on Stripe being up. Where the two disagree Stripe wins and the webhook rebuilds the row (F1).
GET/billing/me
curl "$MORSE/billing/me" \
-H "Authorization: Bearer $MORSE_TOKEN"{
"grant": null,
"pass": null,
"pass_used": true,
"plan": "…",
"subscription": null
}A free account's 25% off for its first months, while its 48 hours last.
/billing/offerAsking opens the window the first time. Null when there is none for this account.
GET/billing/offer
curl "$MORSE/billing/offer" \
-H "Authorization: Bearer $MORSE_TOKEN"Close the offer card.
/billing/offer/dismissThe offer itself stands until it ends.
POST/billing/offer/dismiss
curl -X POST "$MORSE/billing/offer/dismiss" \
-H "Authorization: Bearer $MORSE_TOKEN"Manage the subscription: cards, invoices, cancelling. Returns a Stripe URL.
/billing/portalOne endpoint for all of it because Stripe hosts the whole of that UI, and rebuilding any of it here would be a second place that has to stay correct about what someone is paying.
POST/billing/portal
curl -X POST "$MORSE/billing/portal" \
-H "Authorization: Bearer $MORSE_TOKEN"{
"url": "https://onmorse.com/priya"
}What you have used this month, and what your plan allows.
/billing/usage**Alone among these routes it does not need Stripe.** What somebody has used
is true whether or not this server can take money, and the plan comes from
billing.plan_for, which reads our own rows. Holding it to _require_stripe
would make a fresh clone unable to say "3 of 10" about meetings it had
itself recorded.
It reports and refuses nothing. Enforcement is plan 026, and is blocked on
the open questions in docs/PRICING.md §6.
GET/billing/usage
curl "$MORSE/billing/usage" \
-H "Authorization: Bearer $MORSE_TOKEN"{
"context_months": 1,
"meetings_limit": 20,
"meetings_used": 1,
"minutes_limit": 30,
"period_end": "2026-10-02T10:30:00+05:30",
"period_start": "2026-10-02T10:30:00+05:30",
"plan": "…"
}