Billing
Three endpoints. Reads the current plan + usage, and mints Stripe-hosted
URLs for checkout (upgrade) and the customer portal (manage card,
invoices, downgrade). All require a JWT session cookie or
Authorization: Bearer <jwt> — see auth.
| Method | Path | Permission |
|---|---|---|
GET | /v1/billing/subscription | billing:read |
POST | /v1/billing/checkout-session | billing:write |
POST | /v1/billing/portal-session | billing:write |
GET /v1/billing/subscription
Current plan, status, and receipts processed since the first of the month (UTC).
curl https://api.receipts.oligontech.com/v1/billing/subscription \
-H "Authorization: Bearer $JWT"Response — 200 OK
{
"plan": "startup",
"status": "active",
"quota_receipts_month": 10000,
"usage_this_month": 4237,
"rate_limit_per_minute": 300,
"cancel_at_period_end": false,
"current_period_end": "2026-07-01T00:00:00+00:00"
}status is "active" when there is no Subscription row yet (e.g. a
brand-new org still on free). current_period_end is null until
Stripe confirms the first invoice.
POST /v1/billing/checkout-session
Returns a Stripe Checkout URL the user must visit to start (or change)
a paid subscription. If the org has no Stripe customer yet, one is
created on the fly using org.billing_email.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
plan | string | yes | One of indie, startup, scale. |
success_url | string | yes | Where Stripe redirects on success. |
cancel_url | string | yes | Where Stripe redirects on abort. |
curl https://api.receipts.oligontech.com/v1/billing/checkout-session \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"plan": "startup",
"success_url": "https://app.example.com/billing?ok=1",
"cancel_url": "https://app.example.com/billing?cancelled=1"
}'Response — 200 OK
{ "url": "https://checkout.stripe.com/c/pay/cs_test_..." }Errors
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | plan is not indie/startup/scale. |
| 404 | not_found | The actor's org row is missing. |
POST /v1/billing/portal-session
Returns a Stripe Customer Portal URL where the user can update payment
method, view invoices, or cancel. Requires the org already has a
stripe_customer_id (i.e. they completed at least one Checkout).
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
return_url | string | yes | Where Stripe sends the user when they close the portal. |
curl https://api.receipts.oligontech.com/v1/billing/portal-session \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"return_url": "https://app.example.com/billing"}'Response — 200 OK
{ "url": "https://billing.stripe.com/p/session/test_..." }Errors
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | Org has no stripe_customer_id yet — run Checkout first. |