Invoices API
List invoices, generate one on demand, and download PDFs.
Every endpoint on this page accepts either an admin session token (Authorization: Bearer <JWT>)
or an admin-role API key in X-API-Key. See Authentication.
Each one also takes the X-Environment header. With a session token it is what picks the
environment, and leaving it out means live. See
Choosing the Environment.
The Invoice Object
Every endpoint on this page returns invoices in this shape.
| Field | Type | Description |
|---|---|---|
id | string | Id of the invoice, unique within your account. |
external_id | string | Invoice number as it appears on the invoice. Its format comes from your invoicing settings. |
customer_id | string | Id of the customer this invoice belongs to. |
customer_name | string | Customer display label. |
customer_external_id | string | Account id you gave the customer. |
amount | number | Total amount of this invoice. |
amount_due | number | Amount still owed on this invoice: its total less what has been paid against it, never less than zero. It is not the customer's overall balance, which can be larger. |
amount_paid | number | Amount already paid against this invoice. |
currency | string | null | ISO 4217 currency code for every amount on this invoice. null when the billing backend names none, so read the account's own currency rather than assuming one. |
status | string | do_not_pay, unpaid, partially_paid, overdue, paid, previous_balance_remaining, under_review, rejected, approved, voided, or issued for a status this API does not recognize. |
issue_date | string | Date the invoice was issued (YYYY-MM-DD). |
due_date | string | Date payment is due (YYYY-MM-DD). |
period_from | string | null | Billing period start. |
period_to | string | null | Billing period end. |
usage_charges | number | Total usage charges on this invoice. |
subscriptions | number | Subscription charges. |
manual_charges | number | Manual one-time charges. |
taxes | number | Tax amount. |
adjustments | number | Credits and debits applied to this invoice. A negative value reduces what is owed. |
previous_balance | number | Carried-over balance from the prior period. |
payments_applied | number | Payments applied during the period. |
pdf_available | boolean | Whether a PDF can be downloaded for this invoice. |
{
"id": "101",
"external_id": "INV-001",
"customer_id": "789",
"customer_name": "Acme Corp",
"customer_external_id": "acme-corp",
"amount": 149.80,
"amount_due": 0.00,
"amount_paid": 149.80,
"currency": "USD",
"status": "paid",
"issue_date": "2026-03-01",
"due_date": "2026-03-30",
"period_from": "2026-02-01",
"period_to": "2026-02-28",
"usage_charges": 120.00,
"subscriptions": 20.00,
"manual_charges": 0.00,
"taxes": 9.80,
"adjustments": 0.00,
"previous_balance": 0.00,
"payments_applied": -149.80,
"pdf_available": true
}
Amounts are plain decimal numbers in major units. See Money & Currency.
List Invoices
GET /api/v1/invoices
Lists invoices across every customer, newest first.
Parameters:
| Parameter | Type | Description |
|---|---|---|
limit | int | Invoices per page, from 1 to 500 (default: 50). |
offset | int | Invoices to skip before this page (default: 0). |
status | string | Filter to one status: do_not_pay, unpaid, partially_paid, overdue, paid, previous_balance_remaining, under_review, rejected, approved or voided. Send all, or omit it, for every status. A value outside this list is ignored rather than refused, and you get an unfiltered page. Filtering on voided turns hide_void off for you, so the two never have to be sent together. |
hide_void | boolean | Leave out invoices that were voided or superseded. Defaults to true. |
from_date | string | Only invoices issued on or after this date (YYYY-MM-DD). Omit for no lower bound. |
to_date | string | Only invoices issued on or before this date (YYYY-MM-DD). Omit for no upper bound. |
include_summary | boolean | Also return category totals over every matching invoice, not just this page. Defaults to false. |
X-Environment header | string | live or sandbox (header, not a query parameter). |
Both date bounds are inclusive. A value that does not start with a date the API can read is
rejected with 422 naming the field; a trailing time component is accepted and ignored. Omitting
both returns every invoice, not a default window.
There is no customer_id filter here. To read one customer's invoices, use
List a Customer's Invoices.
Response:
{
"invoices": [
{
"id": "101",
"external_id": "INV-001",
"customer_id": "789",
"customer_name": "Acme Corp",
"customer_external_id": "acme-corp",
"amount": 149.80,
"amount_due": 0.00,
"amount_paid": 149.80,
"currency": "USD",
"status": "paid",
"issue_date": "2026-03-01",
"due_date": "2026-03-30",
"period_from": "2026-02-01",
"period_to": "2026-02-28",
"usage_charges": 120.00,
"subscriptions": 20.00,
"manual_charges": 0.00,
"taxes": 9.80,
"adjustments": 0.00,
"previous_balance": 0.00,
"payments_applied": -149.80,
"pdf_available": true
}
],
"total": 1,
"limit": 50,
"offset": 0,
"has_more": false,
"summary": null
}
Category Totals
With include_summary=true, summary carries the totals over every matching invoice rather than
over the page you asked for. Without it, summary is null. The totals cost extra work, so ask
for them only when you display them.
| Field | Type | Description |
|---|---|---|
open_amount | number | Remaining balance awaiting payment. |
open_count | int | Number of invoices awaiting payment. |
overdue_amount | number | Remaining balance on invoices marked overdue. |
overdue_count | int | Number of overdue invoices. |
collected_amount | number | Full amount of fully-paid invoices. |
collected_count | int | Number of fully-paid invoices. |
total_outstanding | number | Combined open debt (open_amount + overdue_amount). |
scanned | int | Number of invoices actually aggregated into these totals. |
excluded_count | int | Invoices counted in no total, because they contribute money to none: credit notes, invoices marked do-not-pay, voided invoices, anything in the review workflow, and rows with nothing collectible or collected. Show this count, so the totals are not read as covering every invoice scanned. |
truncated | boolean | true when the matching set exceeded the aggregation cap of 5,000 invoices, so the totals cover only the newest scanned of them. Label such totals as partial rather than presenting them as complete. |
{
"summary": {
"open_amount": 9096.66,
"open_count": 7,
"overdue_amount": 5.20,
"overdue_count": 2,
"collected_amount": 0.00,
"collected_count": 0,
"total_outstanding": 9101.86,
"scanned": 9,
"excluded_count": 0,
"truncated": false
}
}
List a Customer's Invoices
GET /api/v1/customers/{customer_id}/invoices
Lists one customer's invoices, newest first, in the same envelope as the tenant-wide list. It is documented with the rest of the customer endpoints, under Get Customer Invoices.
It takes no status or hide_void parameter, and unlike the tenant-wide list it returns a
customer's history complete, voided invoices included.
Get a Single Invoice
GET /api/v1/invoices/{invoice_id}
| Parameter | Type | Description |
|---|---|---|
invoice_id | string | Id of the invoice, from the id field of a listed invoice. |
X-Environment header | string | live or sandbox (header, not a query parameter). |
Returns one invoice object at the top level, with no envelope around it.
Generate an On-Demand Invoice
POST /api/v1/customers/{customer_id}/invoices
Issues an invoice right now, covering charges accrued so far in the current billing period, instead of waiting for the billing period to close. Useful for a prepaid signup flow where the customer pays immediately and needs an invoice for that payment without delay.
{customer_id} takes the numeric id or the customer's external_id, as on every other
customer route.
To issue it, the billing backend closes the billing period up to the cut-off: it charges the subscription fees and taxes that fall inside it and moves the customer's period on, so the next invoice starts where this one stopped. The customer's billing anniversary stays where it was. None of this can be undone, and the billing backend emails the finished invoice to the customer when that customer's billing settings allow it.
| Parameter | Type | Description |
|---|---|---|
customer_id | string | Id of the customer to invoice. |
X-Environment header | string | live or sandbox (header, not a query parameter). |
Request Body: every field is optional. Send {} to invoice everything accrued so far.
| Field | Type | Description |
|---|---|---|
invoice_due | string | Day the invoice falls due (YYYY-MM-DD), no earlier than today. Omit to use the net terms your invoicing settings apply to every customer. |
period_to | string | Invoice everything charged up to the start of this day (YYYY-MM-DD), leaving that day's own charges for the next invoice. No earlier than the day after the current billing period started, and no later than today. Omit to invoice everything charged up to now. |
allow_duplicate | boolean | Issue a second on-demand invoice for a period that already has one. Defaults to false. |
Both dates are read in the customer's own timezone, and both must be zero-padded: a value in any
other shape is rejected with 422 naming the field. Note that omitting period_to is not the same
as passing today's date: the first covers everything up to now, the second stops at the start of
today.
Response: 201 Created
{
"invoice": {
"id": "424",
"external_id": "INV-424",
"customer_id": "715",
"customer_name": "Prepaid Signup",
"customer_external_id": "prepaid-signup-715",
"amount": 49.00,
"amount_due": 49.00,
"amount_paid": 0.00,
"currency": "USD",
"status": "unpaid",
"issue_date": "2026-07-15",
"due_date": "2026-07-30",
"period_from": "2026-07-01",
"period_to": "2026-07-15",
"usage_charges": 45.00,
"subscriptions": 0.00,
"manual_charges": 0.00,
"taxes": 4.00,
"adjustments": 0.00,
"previous_balance": 0.00,
"payments_applied": 0.00,
"pdf_available": true
},
"warning": null
}
invoice is a full invoice object. warning names something that went
wrong while the invoice was generated anyway; it is null when nothing did.
When the Request Is Refused
If an invoice was already issued on demand for this customer in the current billing period, the
request returns 409 Conflict naming it, and details carries that invoice so you can offer it
instead:
{
"error": {
"code": "CONFLICT",
"message": "An invoice has already been issued on demand for this customer in the current billing period (invoice INV-424).",
"details": { "invoice_id": "424", "invoice_number": "INV-424" }
}
}
Pass allow_duplicate: true to issue a second one anyway. A terminated customer is refused with
409 as well, and cannot be invoiced at all. Both carry error.code CONFLICT, so tell them
apart by error.details: only the duplicate names an invoice there.
A customer the billing backend will not invoice for a reason you can act on is refused with 422,
and the message names the reason: an invoice_due earlier than today where the customer is, no
invoice template, billing paused, nothing charged inside the period, the period already billed,
the previous period not yet closed, the customer closed or exported, or billed as part of another
customer's invoice.
The most common of those is a customer with nothing charged yet. A plan's first fee is charged at the start of the customer's next day in their own timezone, not at the moment you create them, so an invoice issued before that covers nothing:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "There is nothing to invoice: no charges fall inside the period this invoice would cover. Issue the invoice after the plan's first fee has been charged, which you can confirm in the customer's transactions.",
"details": {}
}
}
Retrying does not clear this: the answer changes only after that fee exists. GET /api/v1/customers/{customer_id}/transactions is where you can see whether it does.
Read error.message rather than treating every 422 as "nothing was written". One of them
says the invoice was issued but its PDF is not ready yet: the write landed, so do not send the
request again. Another says a billing operation is already running for this customer, which wrote
nothing, so that one you can retry in a moment.
A 502 with error.code CONNECTION_ERROR means the billing backend stopped answering
mid-write. The invoice may still have been issued, so read the customer's invoices before you
try again: issuing one cannot be undone, and a blind retry with allow_duplicate bills the period
twice. Any other 502 means the billing backend could not name a cause.
Download Invoice PDF
GET /api/v1/invoices/{invoice_id}/pdf
| Parameter | Type | Description |
|---|---|---|
invoice_id | string | Id of the invoice, from the id field of a listed invoice. |
X-Environment header | string | live or sandbox (header, not a query parameter). |
Returns the invoice as a PDF file download, with Content-Type: application/pdf and
Content-Disposition: attachment; filename=invoice-{invoice_id}.pdf. Check pdf_available on the
invoice first: a PDF the billing backend has not produced yet answers 404.