Billing API
Manage subscriptions, invoices, payment methods, and setup intents.
Project Billing
Get Subscription Breakdown
GET /v1/projects/:projectId/billingReturns the subscription breakdown and upcoming invoice preview. Maps subscription items to resources (servers, apps, storage, registries).
The response also includes the project's balance, in minor units: creditBalance is credit applied automatically to upcoming invoices, and owedBalance is an amount carried forward onto the next invoice. At most one of the two is ever non-zero, and either can be non-zero when there is no active subscription.
Auth: Required (member, scope: billing:read)
Response (200):
{
"success": true,
"data": {
"items": [
{
"product": "App — Professional-1",
"quantity": 1,
"unitAmount": 2400,
"currency": "usd",
"metered": false
}
],
"upcomingInvoice": {
"amountDue": 2400,
"currency": "usd",
"periodEnd": "2025-02-25T00:00:00Z"
},
"creditBalance": 0,
"owedBalance": 0
}
}List Invoices
GET /v1/projects/:projectId/billing/invoicesReturns one page of the project's invoices, newest first.
Auth: Required (member, scope: billing:read)
Query parameters:
| Parameter | Type | Description |
|---|---|---|
limit | integer | Invoices per page, 1–100. Defaults to 10. |
starting_after | string | Invoice id to page after — pass the previous page's nextCursor. |
status | string | Only invoices at this stage: draft, open, paid, uncollectible, void. |
created_gte | string | Only invoices dated on or after this ISO date or date-time. |
created_lte | string | Only invoices dated on or before this ISO date or date-time. |
Response:
{
"success": true,
"data": {
"data": [
{
"id": "in_1234",
"number": "DB-0001",
"created": "2026-02-01T09:15:00.000Z",
"amountDue": 4900,
"currency": "USD",
"status": "paid",
"hostedInvoiceUrl": "https://invoice.stripe.com/i/...",
"project": null
}
],
"hasMore": true,
"nextCursor": "in_1234"
}
}Pages run forwards only: there is no total count and no page number. Keep requesting with starting_after set to the previous response's nextCursor until hasMore is false, at which point nextCursor is null. An out-of-range limit or an unknown status is rejected with a 400.
This list covers every stage — draft, open, paid, void, uncollectible. number and hostedInvoiceUrl are null while an invoice is still a draft. project is always null here, since the route already scopes the list to one project; only the account-wide list below populates it.
Export Invoices
GET /v1/projects/:projectId/billing/invoices/exportReturns every invoice matching the filters in one response, newest first, for exporting to a spreadsheet. Takes the same status, created_gte and created_lte parameters as the list above; it does not page, and the number of invoices returned is capped.
Auth: Required (member, scope: billing:read)
Response: the same invoice objects as the list endpoint, as a flat array.
{
"success": true,
"data": [
{
"id": "in_1234",
"number": "DB-0001",
"created": "2026-02-01T09:15:00.000Z",
"amountDue": 4900,
"currency": "USD",
"status": "paid",
"hostedInvoiceUrl": "https://invoice.stripe.com/i/...",
"project": null
}
]
}List Your Invoices
GET /v1/account/invoicesReturns the last 24 invoices you have paid, newest first, with each attributed to the project it was billed for via project: { id, name }. The project name is the one it had when the invoice was paid.
This is a payment history, so it is recorded per invoice at the moment that invoice settles rather than derived from who bills for a project today:
- Invoices stay on your list after you leave a project or hand billing to someone else.
- Taking over billing for a project does not move that project's earlier invoices to you.
- Only paid invoices appear. Upcoming, open, and draft invoices are on the project's own invoice list.
Auth: Required
Billing History
GET /v1/projects/:projectId/billing/history?limit=50Returns a unified timeline of charges, proration credits, and refunds. Useful for showing users exactly what they were charged, what credits they received when removing resources, and any refunds issued.
Auth: Required (member, scope: billing:read)
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max entries to return (capped at 100) |
Response (200):
{
"success": true,
"data": [
{
"id": "il_...",
"type": "charge",
"description": "App — Professional-1",
"amount": 2400,
"currency": "USD",
"date": "2026-05-25T00:00:00.000Z"
},
{
"id": "il_...",
"type": "credit",
"description": "Unused time on App — Professional-1 after 10 Jun 2026",
"amount": 1200,
"currency": "USD",
"date": "2026-06-10T00:00:00.000Z"
},
{
"id": "re_...",
"type": "refund",
"description": "Credit balance refund requested by biller",
"amount": 1176,
"currency": "USD",
"date": "2026-06-15T00:00:00.000Z"
}
]
}Each entry has a type field:
charge— a subscription charge or prorated first paymentcredit— a proration credit from removing a resource mid-cyclerefund— a refund of the project's credit balance to the payment method, requested by the biller (see Refund Credit Balance); the amount is net of withheld payment-processing fees
Refund Credit Balance
POST /v1/projects/:projectId/billing/credit/refundRefunds the project's credit balance (creditBalance from the subscription-breakdown response) back to the payment method(s) that funded it. This is the general way for a biller to cash out the project's credit balance — for example after a resource removal generates a credit the biller would rather receive back than leave applied to future invoices, or before transferring billing responsibility to another member.
Cosmoner never issues this refund automatically; it only happens when a biller explicitly calls this endpoint. Payment-processing fees on the original charges are not recoverable and are withheld from the refund, so the amount that reaches the payment method is less than the credit balance consumed unless no fees apply. amount is what was actually refunded and feeWithheld is what was withheld, both in minor units.
Not all credit can always be paid back to a card. creditRemaining is the portion that could not be, in minor units; it stays on the project's credit balance and is applied to future invoices as usual. A successful refund can therefore return a non-zero creditRemaining — check it rather than assuming the balance is now zero.
If there is no credit to refund, this is a no-op and still returns 200 with refunded: false.
Auth: Required (biller, scope: billing:write)
Response (200):
{
"success": true,
"data": {
"refunded": true,
"amount": 776,
"feeWithheld": 24,
"currency": "USD",
"creditRemaining": 0
}
}Notify the Biller
POST /v1/projects/:projectId/billing/notify-billerNotifies the project's biller (email + in-app) that a payment method is needed. Used when a non-biller tries to deploy a resource into a project that has no card yet.
Auth: Required (member, scope: billing:write). Rate-limited per member per project so the biller cannot be flooded; a repeat within the window returns 429 RATE_LIMITED. Returns 400 if the caller is the biller.
Project Payment Methods
The project's card is managed only by its biller (the pending biller of an in-flight transfer may also add a card while accepting). All resource billing charges this card.
Create Setup Intent
POST /v1/projects/:projectId/billing/setup-intentCreates a setup intent bound to the project's Stripe customer. Confirm it in the browser to tokenise the card, then register it with the endpoint below.
Auth: Required (biller or pending biller, scope: billing:write)
Response (200): { "success": true, "data": { "clientSecret": "seti_..._secret_..." } }
List Payment Methods
GET /v1/projects/:projectId/billing/payment-methodsReturns the project's card(s) and the default payment method ID.
Auth: Required (biller, scope: billing:read)
Add / Replace Payment Method
POST /v1/projects/:projectId/billing/payment-methodsRegisters a card confirmed via the setup intent. The card always becomes the project's default (so this doubles as "replace"), and its billing address is copied onto the customer for automatic tax.
Auth: Required (biller or pending biller, scope: billing:write)
Request Body: { "paymentMethodId": "pm_..." }
Set Default Payment Method
PATCH /v1/projects/:projectId/billing/payment-methodsAuth: Required (biller, scope: billing:write)
Request Body: { "paymentMethodId": "pm_..." }
Remove Payment Method
DELETE /v1/projects/:projectId/billing/payment-methodsDetaches a card. Removing the default card while a subscription is active is rejected with 409 ORG_PAYMENT_METHOD_IN_USE — add a replacement first.
Auth: Required (biller, scope: billing:write)
Request Body: { "paymentMethodId": "pm_..." }
Deploy Payment Errors
Resource-create endpoints (servers, apps, databases, storage, etc.) bill the project card off-session and return { "success": true, "data": { "deployed": true } } on success. When payment can't proceed they return 402 with a machine-readable code:
| Code | Meaning |
|---|---|
ORG_PAYMENT_METHOD_REQUIRED | The project has no card and the caller is the biller — add one and retry. |
BILLER_PAYMENT_METHOD_REQUIRED | The project has no card and the caller is not the biller. error.details carries { billerUserId, billerName, billerEmail }. |
ORG_BILLING_ADDRESS_REQUIRED | Automatic tax could not resolve the billing address — re-add the card with a full address. |
PAYMENT_REQUIRED | The card was declined, or the subscription is past due. |
Deprecated: Account-level Payment Methods
The following user-level endpoints are deprecated now that billing runs on the project card, and cards are no longer collected at signup. They remain for one release for historical access; new integrations should use the project payment-method endpoints above.
GET /v1/stripe/payment-methods,POST/PATCH/DELETE /v1/stripe/payment-methodsPOST /v1/stripe/setup-intent(now requires authentication; the pre-signup, unauthenticated path was removed)GET /v1/stripe/invoices— lists invoices on the user-level Stripe customer. Nothing has been billed to that customer since billing moved to the project, so this returns an empty list for accounts created after the move. UseGET /v1/account/invoicesinstead.
Auth: Required