Skip to main content

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.

FieldTypeDescription
idstringId of the invoice, unique within your account.
external_idstringInvoice number as it appears on the invoice. Its format comes from your invoicing settings.
customer_idstringId of the customer this invoice belongs to.
customer_namestringCustomer display label.
customer_external_idstringAccount id you gave the customer.
amountnumberTotal amount of this invoice.
amount_duenumberAmount 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_paidnumberAmount already paid against this invoice.
currencystring | nullISO 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.
statusstringdo_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_datestringDate the invoice was issued (YYYY-MM-DD).
due_datestringDate payment is due (YYYY-MM-DD).
period_fromstring | nullBilling period start.
period_tostring | nullBilling period end.
usage_chargesnumberTotal usage charges on this invoice.
subscriptionsnumberSubscription charges.
manual_chargesnumberManual one-time charges.
taxesnumberTax amount.
adjustmentsnumberCredits and debits applied to this invoice. A negative value reduces what is owed.
previous_balancenumberCarried-over balance from the prior period.
payments_appliednumberPayments applied during the period.
pdf_availablebooleanWhether 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:

ParameterTypeDescription
limitintInvoices per page, from 1 to 500 (default: 50).
offsetintInvoices to skip before this page (default: 0).
statusstringFilter 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_voidbooleanLeave out invoices that were voided or superseded. Defaults to true.
from_datestringOnly invoices issued on or after this date (YYYY-MM-DD). Omit for no lower bound.
to_datestringOnly invoices issued on or before this date (YYYY-MM-DD). Omit for no upper bound.
include_summarybooleanAlso return category totals over every matching invoice, not just this page. Defaults to false.
X-Environment headerstringlive 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.

FieldTypeDescription
open_amountnumberRemaining balance awaiting payment.
open_countintNumber of invoices awaiting payment.
overdue_amountnumberRemaining balance on invoices marked overdue.
overdue_countintNumber of overdue invoices.
collected_amountnumberFull amount of fully-paid invoices.
collected_countintNumber of fully-paid invoices.
total_outstandingnumberCombined open debt (open_amount + overdue_amount).
scannedintNumber of invoices actually aggregated into these totals.
excluded_countintInvoices 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.
truncatedbooleantrue 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}
ParameterTypeDescription
invoice_idstringId of the invoice, from the id field of a listed invoice.
X-Environment headerstringlive 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.

ParameterTypeDescription
customer_idstringId of the customer to invoice.
X-Environment headerstringlive or sandbox (header, not a query parameter).

Request Body: every field is optional. Send {} to invoice everything accrued so far.

FieldTypeDescription
invoice_duestringDay 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_tostringInvoice 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_duplicatebooleanIssue 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
ParameterTypeDescription
invoice_idstringId of the invoice, from the id field of a listed invoice.
X-Environment headerstringlive 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.