Start an invoice run
Operation ID: startInvoiceRun
Group: Accounting › Invoice Runs
Open in the interactive reference
POST /accounting/invoice-runs
Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.
Starts an invoice run for a sales order, customer, or legal entity. A run links each invoice it creates to itself and saves an error for each line that fails. The response contains the run's id.
Send exactly one of 'salesOrderId', 'customerId', or 'entityId'. The date field you send depends on that choice. A sales order run takes 'billingDates', the exact planned dates to invoice, and a customer or legal entity run needs 'billUpTo', which invoices everything planned on or before that date.
By default the request returns while the run is working, and you poll GET accounting/invoice-runs/:id. Set 'sync' to true to wait for the final status instead. A customer or legal entity run can take minutes, so use 'sync' for a single sales order.
A new run leaves out lines that failed in an earlier run. For a sales order run with 'sync' set to true, if that leaves nothing to invoice, the response includes 'unresolvedRunId'. Send POST accounting/invoice-runs/:id/rerun for that run instead of repeating this request.
While an asynchronous run is working, another request with the same target, 'scope', dates, and 'invoiceDate' joins that run and doesn't invoice the same lines twice. The response then has 'deduplicated' set to true, so you can retry a request that timed out. After the run finishes, the same request starts a new run.
This protection doesn't apply when 'sync' is true, so two identical requests with 'sync' set to true can both invoice the same lines.
For a sales order that another system invoices, the request fails with SALES_ORDER_EXTERNALISED. Import its invoices with POST accounting/invoices/import.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Api-Version |
header | string · 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. Example: "8f14e45f-ceea-467a-9f6b-2c1d0e5a7b39". |
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<date> | no | The exact planned dates to bill, for sales-order runs only. Use format YYYY-MM-DD. Only planned invoicing lines on these dates are billed. Omit to bill every pending line on the order. Not accepted on a customer or entity run, which uses billUpTo instead. |
billUpTo |
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 |
date | no | The date stamped on the invoices created. Use format YYYY-MM-DD. Defaults to today. Must fall in an open accounting period. Example: "2026-09-30". |
scope |
array<any> | 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: true. |
{
"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. Example: {"id":"1c9d6b73-4a20-4e58-8f31-7b2e0c4a9d15","invoiceRunNumber":"ISR-1042","status":"completed","invoicesCreated":3}. |
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. |
{
"data": {
"data": {
"id": "1c9d6b73-4a20-4e58-8f31-7b2e0c4a9d15",
"invoiceRunNumber": "ISR-1042",
"status": "completed",
"invoicesCreated": 3
}
},
"diagnostics": [
{
"code": "INVOICE_RUN_STARTED",
"message": "<message>",
"severity": "error",
"path": [
"<path>"
],
"details": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "<status>"
}
}
]
}
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 |
Advisories
Warnings and information a successful response can carry under diagnostics:
INVOICE_RUN_STARTEDINVOICE_RUN_HELD_BACKINVOICE_RUN_ALREADY_RUNNING
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— The request carries no valid access token.403— 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 requests from this client address. The response has no body and noRetry-Afterheader.500— Internal server error