Starts an invoice run for a sales order, customer or legal entity.
POST /accounting/invoice-runs
Group: Accounting › Invoice Runs · Operation ID: startInvoiceRun
Starts an invoice run and returns its id. A run is the audited way to create invoices: it records how many it created, links each one back to itself, and records a row for every line that failed. Read it back with GET accounting/invoice-runs/:id.
A run bills at one level. Name exactly one of 'salesOrderId', 'customerId' or 'entityId', widest last: 'entityId' covers every approved order in the entity, which is what the scheduled nightly run bills. The three nest, since an order belongs to one customer and a customer to one entity, so naming two adds nothing the narrower one did not already say and is refused with MULTIPLE_TARGETS_GIVEN.
The two date inputs are not interchangeable. 'billingDates' names the exact planned dates to bill and belongs to a sales-order run. Omit it to bill every pending line on the order. 'billUpTo' is an upper bound, billing everything planned on or before it, and is required on a customer or entity run. Sending the wrong one for the scope is refused rather than reinterpreted.
'invoiceDate' is the date stamped on the invoices created, defaulting to today. It must fall in an open accounting period or the call is refused with ACCOUNTING_PERIOD_CLOSED.
By default the run is asynchronous: the response has 'status' of 'running' and you poll. Set 'sync' to true to block until it settles and get the final status and count back directly, which suits a single sales order but not a customer-wide run that can take minutes.
A run whose lines already failed in an earlier run comes back empty, because fresh runs hold those lines back. When that happens the response has 'unresolvedRunId': call POST accounting/invoice-runs/:id/rerun on that run instead of repeating this call.
A retry while the run is still working is safe. While an asynchronous run is in flight, another call naming the same target, scope, dates and invoice date joins it rather than billing the same lines twice, and the response has 'deduplicated' set to true alongside that run's id. So a timed-out call can simply be retried. The guard lasts only as long as the run does: once a run has finished, asking to bill the same scope again starts a new run. It does not apply to a 'sync' call, so two concurrent identical 'sync' calls can each bill.
Externalized sales orders are refused with SALES_ORDER_EXTERNALISED: their invoices include tax calculated by the source system, so they are imported through POST accounting/invoices/import rather than created by a run.
Open in the interactive reference
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Api-Version |
header | "v1" |
yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version v1. |
Idempotency-Key |
header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with 400 IDEMPOTENCY_NOT_SUPPORTED rather than silently ignored. |
Request body
Required, application/json: startInvoiceRunInput.
| Field | Type | Required | Description |
|---|---|---|---|
salesOrderId |
string | no | The sales order to bill. Give exactly one of 'salesOrderId', 'customerId' or 'entityId'. Alone, it bills every pending line on the order. See id on GET accounting/sales-orders/:id. |
customerId |
string | no | The customer to bill, across all their sales orders. Give exactly one of the three targets. Requires 'billUpTo'. See id on GET accounting/customers/:id. |
entityId |
string | no | The legal entity to bill, across every approved order in it. This is the scope the scheduled nightly run uses, and the broadest this API offers. Give exactly one of the three targets. Requires 'billUpTo'. See id on GET base/entities. |
billingDates |
array of string (date) | no | Sales-order runs only. The exact planned dates to bill. Use format YYYY-MM-DD. Bills only instalments that fall on them. Omit to bill every pending line on the order. Rejected on a customer or entity run, which bounds by date instead. |
billUpTo |
string (date) | no | Customer and entity runs only. Bills everything planned on or before this date, an upper bound rather than an exact match. Use format YYYY-MM-DD. Required on both. Rejected on a sales-order run, which names exact dates instead. |
invoiceDate |
string (date) | no | The date stamped on the invoices created. Use format YYYY-MM-DD. Defaults to today. Must fall in an open accounting period. |
scope |
array of string, one of "all", "oneTimeAndRecurring", "usageInArrears", "billableProducts" |
no | The line types to bill. Defaults to all line types. 'oneTimeAndRecurring' covers one-time, recurring and commit lines. 'billableProducts' covers billable time, travel and expense, and pass-through. |
sync |
boolean | no | Whether to block until the run has settled and report its final status and count. Defaults to false, which returns immediately with 'status' of 'running' for you to poll. Use it for one sales order, not for a customer-wide run. |
Example:
{
"salesOrderId": "8f14e45f-ceea-467a-9f6b-2c1d0e5a7b39",
"customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"billingDates": [
"2026-01-15"
],
"billUpTo": "2026-01-15",
"invoiceDate": "2026-09-30",
"scope": [
"all"
],
"sync": true
}
Response
200 Success
Under data (startInvoiceRunOutput):
| Field | Type | Required | Description |
|---|---|---|---|
data |
object | yes | The invoice run that was started. |
data.id |
string | yes | The unique identifier for the created invoice run. |
data.invoiceRunNumber |
string | yes | The invoice run number, for example 'ISR-1042'. |
data.status |
string | yes | The execution status of the run. On an async call it is 'created' or 'running'. With 'sync' set to true it is settled: 'completed', 'error', or 'completeAndError' when some invoices were created and some lines failed. |
data.invoicesCreated |
number | yes | The number of invoices the run has created so far. Meaningful once the run has settled. 0 on an async call that has not finished. |
data.unresolvedRunId |
string | no | The unique identifier for an earlier failed run, present only when this run could produce nothing because that run's failed lines are held back. Rerun that one rather than repeating this call. |
data.deduplicated |
boolean | no | True when an identical run was already in flight and this call joined it instead of starting a second. The id above is that run's. Absent when this call started the run. |
Response headers
Idempotency-Replayed— Present andtrueonly when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent asfalse.
Errors
| Code | Status |
|---|---|
NO_TARGET_GIVEN |
400 |
MULTIPLE_TARGETS_GIVEN |
400 |
SALES_ORDER_NOT_FOUND |
400 |
CUSTOMER_NOT_FOUND |
400 |
ENTITY_NOT_FOUND |
400 |
SALES_ORDER_EXTERNALISED |
400 |
SALES_ORDER_NOT_APPROVED |
400 |
BILL_UP_TO_NOT_APPLICABLE |
400 |
BILLING_DATES_NOT_APPLICABLE |
400 |
BILL_UP_TO_REQUIRED |
400 |
ACCOUNTING_PERIOD_CLOSED |
400 |
IDEMPOTENCY_KEY_CONFLICT |
409 |
IDEMPOTENCY_PAYLOAD_MISMATCH |
422 |
Statuses
400— Invalid request or business failure. Codes: NO_TARGET_GIVEN, MULTIPLE_TARGETS_GIVEN, SALES_ORDER_NOT_FOUND, CUSTOMER_NOT_FOUND, ENTITY_NOT_FOUND, SALES_ORDER_EXTERNALISED, SALES_ORDER_NOT_APPROVED, BILL_UP_TO_NOT_APPLICABLE, BILLING_DATES_NOT_APPLICABLE, BILL_UP_TO_REQUIRED, ACCOUNTING_PERIOD_CLOSED401— Missing or invalid access token403— Not authorized404— Not found409— The Idempotency-Key is still being processed by another request.422— The Idempotency-Key was already used with a different request payload.429— Too many requests500— Internal server error