Skip to content

Create Portal Session

POST
/api/billing/portal-session
curl --request POST \
--url 'https://example.com/api/billing/portal-session?organization_id=2489E9AD-2EE2-8E00-8EC9-32D5F69181C0' \
--header 'Authorization: Bearer <token>'

Mint a Stripe Billing Portal session (TODO-089/COMM-007).

The portal itself is where invoices, receipts, payment-method updates and self-service cancellation actually live — Stripe hosts all of that, this route’s only job is minting the one-time, short-lived URL into it. Deliberately NOT _find_or_create_customer: unlike the setup-intent route (whose whole point is establishing a customer to attach a first card to), a billing owner with no gateway customer yet has never checked out and has nothing for the portal to show — 404, not a portal session for an empty account.

return_url is server-constructed from settings.app_base_url, never echoed from the request, same discipline as the Google OAuth redirect (see that setting’s own docstring) — the portal must never be steerable to an attacker-supplied return target.

organization_id
required
Organization Id
string format: uuid

Successful Response

Media type application/json
PortalSessionResponse

POST /api/billing/portal-session — TODO-089/COMM-007.

A single-use, short-lived URL to Stripe’s own hosted Customer Portal (invoices, receipts, payment-method update, cancellation) — the portal_url itself is the whole response; the console just redirects the browser to it. Never store or reuse this URL past the redirect.

object
portal_url
required
Portal Url
string
Example generated
{
"portal_url": "example"
}

Validation Error

Media type application/json
HTTPValidationError
object
detail
Detail
Array<object>
ValidationError
object
ctx
Context
object
input
Input
loc
required
Location
Array
msg
required
Message
string
type
required
Error Type
string
Example generated
{
"detail": [
{
"ctx": {},
"input": "example",
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
]
}