Billing

Check the plan and what is left of it, and hand a person the page that changes it. Useful for a script, the CLI or an agent that should not need the dashboard.

Who Can Call It

#

A team runs on its owner's plan, and only the owner sees the billing page. The billing API follows the same rule: only a key created by the team owner can call it. A key made by an admin or a member gets a 403, whatever its scopes. A team with more than one owner gets a 409 with the code multiple_owners, because it is not clear whose plan it runs on; use the billing page there.

Reading needs billing:read. Checkout and portal links need billing:manage. Neither is in the default set, so name them when you create the key. Connected apps (OAuth) cannot hold them yet.

Nothing Is Charged Through the API

#

No call here changes a plan or takes a payment. A plan change is a link for a person to open. Without a subscription it is a Stripe Checkout page. With one it is the billing page with the change open in its confirmation dialog, which shows the amount due today, or the date a smaller plan starts, before anything happens. Moving up is charged the prorated difference when the person confirms; moving down waits for the end of the period already paid for, with no credit.

Each team can request 20 links an hour and read billing 60 times a minute, on top of the per-key rate limit.

Endpoints

#
GET/v1/billing

The plan, how it is billed, its status in Stripe, when it renews or ends, a smaller plan waiting for the end of the period, a payment that failed, usage this month against the plan's limits, and the price and limits of the next plan up.

Usage is the same as GET /v1/usage and counts the calendar month in UTC, not the billing period. members counts people on the team plus pending invitations, as the seat limit does. Prices are in cents. A 503 means Stripe could not be read; try again.

Response

json
{
  "object": "billing",
  "plan": "growth",
  "interval": "monthly",
  "status": "active",
  "current_period_end": "2026-11-29T10:12:00.000Z",
  "cancel_at_period_end": false,
  "cancel_at": null,
  "scheduled_change": null,
  "payment_issue": null,
  "usage": {
    "period_start": "2026-10-01T00:00:00.000Z",
    "resets_at": "2026-11-01T00:00:00.000Z",
    "emails": { "used": 8420, "limit": 100000, "credits": 0 },
    "contacts": { "used": 1840, "limit": 50000 },
    "segments": { "used": 4, "limit": 50 },
    "broadcasts": { "used": 3, "limit": 50 },
    "domains": { "used": 2, "limit": null },
    "members": { "used": 3, "limit": 10 }
  },
  "next_tier": {
    "plan": "scale",
    "name": "Scale",
    "price": { "monthly": 4900, "yearly": 49000, "currency": "USD" },
    "limits": {
      "emails": 300000,
      "contacts": 150000,
      "segments": 200,
      "broadcasts": 200,
      "domains": null,
      "members": 25
    }
  }
}
POST/v1/billing/checkout

A link to start a subscription or change the plan. action is checkout (a Stripe Checkout page) or confirm (the billing page with the change ready to confirm). direction says how a change is billed: up or same are charged when confirmed, down starts at effective_at.

Refused with 409 when the last payment failed (pay the open invoice first), when the subscription is set to end, or when an upgrade would run into a smaller plan already scheduled. 400 when the account is already on that plan.

Parameters

ParameterTypeDescription
planrequiredstringstarter, growth or scale
intervalstringmonthly or yearly. Defaults to the subscription's interval, or monthly.

Request Body

json
{
  "plan": "scale",
  "interval": "yearly"
}

Response

json
{
  "object": "billing_checkout",
  "action": "confirm",
  "url": "https://smtpfa.st/billing?plan=scale&interval=yearly",
  "plan": "scale",
  "interval": "yearly",
  "direction": "up",
  "effective_at": null
}
POST/v1/billing/portal

A Stripe billing portal link for cards, invoices, billing details and cancelling. Plan changes are not offered there; use POST /v1/billing/checkout.

Parameters

ParameterTypeDescription
flowstringcancel opens the portal on the page that cancels the subscription

Response

json
{
  "object": "billing_portal_session",
  "url": "https://billing.stripe.com/p/session/live_a1b2"
}
GET/v1/billing/invoices

Invoices from Stripe, newest first, with links to pay an open one and to download the PDF. Amounts are in cents.

Parameters

ParameterTypeDescription
limitinteger1 to 100, default 10
afterstringAn invoice id: return older invoices
beforestringAn invoice id: return newer invoices

Response

json
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "invoice",
      "id": "in_1QxYz2",
      "number": "A1B2C3D4-0007",
      "created_at": "2026-10-29T10:12:00.000Z",
      "amount": 1900,
      "amount_paid": 1900,
      "currency": "USD",
      "status": "paid",
      "hosted_invoice_url": "https://invoice.stripe.com/i/acct_1/live_a1b2",
      "invoice_pdf": "https://pay.stripe.com/invoice/acct_1/live_a1b2/pdf"
    }
  ]
}