Skip to content

Billing

Ampout uses Stripe for subscriptions. Every account has an associated Stripe customer (created at signup when STRIPE_SECRET_KEY is configured). Plan changes happen in Stripe Checkout / Customer Portal; the Stripe webhook handler flips account.plan automatically when subscription events fire.

GET /billing/subscription
Authorization: Bearer <key>
{
"plan": {
"slug": "hobby",
"name": "Hobby",
"tier": 1,
"monthly_limit": 5000
},
"current_period": {
"start": "2026-04-01T00:00:00Z",
"end": "2026-04-30T23:59:59Z",
"sends_count": 1842
},
"stripe_subscription_id": "sub_...",
"stripe_customer_id": "cus_..."
}

Period boundaries come from the Stripe subscription when one is active; otherwise they fall back to calendar months.

GET /billing/usage
Authorization: Bearer <key>
{
"monthly_limit": 5000,
"current": {
"period_start": "2026-04-01T00:00:00Z",
"period_end": "2026-04-30T23:59:59Z",
"sends_count": 1842
},
"history": [
{ "period_start": "2026-03-01T00:00:00Z", "period_end": "2026-03-31T23:59:59Z", "sends_count": 4923 }
]
}

Returns the last 12 monthly UsageRecords plus the current period’s running total.

POST /billing/checkout
Authorization: Bearer <key>
Content-Type: application/json
Idempotency-Key: optional-stable-key
{ "plan_slug": "hobby" }
{ "url": "https://checkout.stripe.com/c/pay/cs_test_..." }

Redirect the user to url. On successful payment, Stripe redirects back to BILLING_RETURN_URL?status=success and the inbound webhook flips the account’s plan automatically.

Validates that the requested plan is public, non-archived, and paid (has stripe_price_id). Free / private / archived plans return 422.

POST /billing/portal
Authorization: Bearer <key>
Content-Type: application/json
Idempotency-Key: optional-stable-key
{ "url": "https://billing.stripe.com/p/session/test_..." }

The Customer Portal lets users update payment methods, view invoices, and cancel/downgrade their subscription. Requires the account to have a stripe_customer_id (created at signup when Stripe is configured).

StatusCodeWhen
404plan_not_foundcheckout with unknown plan_slug.
422plan_not_purchasablecheckout for a free, private, or archived plan.
422no_stripe_customerAccount has no stripe_customer_id (signed up before Stripe was wired).

When current.sends_count >= monthly_limit, POST /campaigns/:id/dispatch returns 402 plan_limit_reached before enqueuing any new SendEmailJobs. Already-scheduled SendEmailJobs (e.g., step 2/3 fires from earlier dispatches) still fire — soft-cap applies to new dispatch only.