Docs
API

Billing API

Read a team's credit and charges, pay with a checkout and manage add-ons.

Team Billing

GET /api/v1/teams/{teamSlug}/billing

Every member of the team may read it.

Response 200: the team's plan and pending_plan, its period, its balance_cents, has_paid, whether a payment has reached the team, pause_at, when its apps stop if they are due to, next_charge, the soonest charge its credit has to meet (at, amount_cents, name), or null when none is due, this period's usage, topup_limits (min_cents, max_cents), the last 50 ledger entries, its topups, its bundles: each bundle it holds, an install of it with an app left, oldest first, with its install_id, bundle_id and name, its monthly price_cents as it stands, which its next charge takes, and trial_ends_at, when the free trial its install started ends, or null for an install that paid its first month as it installed, and its addons: each add-on it pays for, oldest first, with its purchase_id, kind, name, app, app_slug, price_cents, which its next charge takes, and ends_at, when it stops if it is being removed.

Checkouts

One route for anything bought. Owners and admins buy; anyone else gets 403 role_required.

GET /api/v1/teams/{teamSlug}/checkouts/terms?kind=credit
GET /api/v1/teams/{teamSlug}/checkouts/terms?kind=addon&item={addonID}&project={projectSlug}
GET /api/v1/teams/{teamSlug}/checkouts/terms?kind=bundle&item={bundleID}

Response 200: what the dialog shows and sends back: due_cents, what leaves the team's credit today; monthly_cents; next_charge_at; credit_cents; short_cents; pay_cents, what is short, at least the smallest top-up; topup_limits; public_key, Revolut's public key, empty while the checkout uses its hosted page; mode, prod or sandbox; and for an add-on item, project, app, name and move, up or down when it changes what the app has of that kind. A move down takes effect at the next charge, with nothing due today. For a solution, item and name are the bundle's, and due_cents and monthly_cents its price for the team; it refuses as an install does (404 bundle_not_found, 409 bundle_not_offered, 409 bundle_installed).

POST /api/v1/teams/{teamSlug}/checkouts

Body {"kind": "credit", "amount_cents": 5000} or \{"kind": "addon", "item": "...", "project": "...", "amount_cents": ..., "monthly_cents": ..., "pay_cents": ...\} or \{"kind": "bundle", "item": "...", "amount_cents": ..., "monthly_cents": ..., "pay_cents": ...\}: the amounts the dialog showed. amount_cents is what is due today for an add-on or a solution, or what to add for credit.

Response 201: checkout. When credit covered it, its state is done and the add-on is on. Otherwise it is open, with token for Revolut's widget and checkout_url for its hosted page, and nothing is on until Revolut's webhook, on a re-read order, says the payment landed. A payment always lands as credit first. Every order is in euros, in cents, and a payment Revolut reports at another amount or currency is not credited. A solution is only ever paid for here: the webhook installs it once the payment lands, its first month taken at the price shown, and the hourly read of open orders installs one the webhook missed. One the team's credit covers is refused with 409 invalid_request, and installed from its page instead.

Refusals 409 price_changed with terms, the terms as they stand, when any amount differs; 409 addon_not_offered, and it answers so for a team that has never paid; 409 addon_already_on; 409 project_stopped for an app that is stopped, which is started first; 400 amount_out_of_range with min and max; 503 payments_unavailable; 502 payment_not_started.

GET /api/v1/teams/{teamSlug}/checkouts/{checkoutID}

Response 200: the id, kind, state, kept, payment and amount_cents. state is open, done, credited, when the payment stayed as credit and nothing was switched on, with kept saying why (already_on, not_offered, app_gone, price_changed, short, expired, cannot_install), or expired, when it was not paid within its life. payment is its top-up's state, and only failed, cancelled or declined there means Revolut refused it: an hourly read of each order still open credits a payment whose webhook never came.

Add-ons

GET /api/v1/teams/{teamSlug}/projects/{projectSlug}/addons

Every member may read it.

Response 200: security_pack, backup_retention_days, included_backup_days, can_buy, and rows, one for each kind that can be bought or that the app has: its kind; options, each with id, name, days and price_cents; purchase with id, addon_id, name, price_cents, which its next charge takes, next_charge_at, ends_at, ends_now, when removing it stops it at once since its month has not been paid for, and moves_to; and given, when staff gave it free. A team that has never paid is offered nothing, and an add-on nobody can buy has no row.

DELETE /api/v1/teams/{teamSlug}/projects/{projectSlug}/addons/{purchaseID}
POST   /api/v1/teams/{teamSlug}/projects/{projectSlug}/addons/{purchaseID}/keep

DELETE has a purchase stop at the end of the month paid for, or at once in a month not yet charged, with no refund and no more charges. keep takes back a removal, or a move down still to come. Each answers as the GET does, and 409 addon_not_on when the purchase has ended. Owners and admins only.

Staff give an add-on free with PUT .../addons, for platform admins alone: {"waf": bool, "security_pack": bool, "backup_retention_days": n}, any of them. It is never charged.