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 org-level credit balance (creditBalance, in minor units), which is automatically applied to upcoming invoices. This can be non-zero even when there is no active subscription.
Auth: Required (member)
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
}
}List Invoices
GET /v1/projects/:projectId/billing/invoicesReturns the project's last 24 invoices, newest first.
Auth: Required (member)
Response:
{
"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
}
]
}This list is read from Stripe, so it 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.
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)
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 non-recoverable payment-processing fees
Refund Credit Balance
POST /v1/projects/:projectId/billing/credit/refundRefunds the project's Stripe credit balance (creditBalance from the subscription-breakdown response) back to the original payment method(s) that funded it, spreading the refund across recent charges if needed. 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. Our payment processor does not return its processing (transaction) fees on refunded charges — since those fees are a non-recoverable cost to us, they are deducted from the refund. amount is the amount actually refunded (credit consumed minus fees) and feeWithheld is the fee amount deducted, both in minor units. The amount refunded to the payment method is therefore less than the credit balance consumed, except where no fees apply.
If there is no credit to refund, this is a no-op and still returns 200 with refunded: false.
Auth: Required (biller)
Response (200):
{
"success": true,
"data": {
"refunded": true,
"amount": 776,
"feeWithheld": 24,
"currency": "USD"
}
}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). Rate-limited to one notification per member per project per 6 hours; 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)
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)
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)
Request Body: { "paymentMethodId": "pm_..." }
Set Default Payment Method
PATCH /v1/projects/:projectId/billing/payment-methodsAuth: Required (biller)
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)
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