Import invoices

Operation ID: importInvoices

Group: Accounting › Invoices

Open in the interactive reference

POST /accounting/invoices/import

Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.

Imports invoices from another system. Use it for sales orders that another system invoices, where externalizedInvoicing is true on GET accounting/sales-orders/:id.

Each invoice is created exactly as you send it. It doesn't group lines into invoices, calculate tax, or apply one-time discounts for these orders.

For each line, send its total and its tax. For a tax-exempt line, send an empty taxLines array. Include any one-time discount in the line's totalPrice.

Each line is matched to its planned invoicing line on the sales order by salesOrderProductLineId and plannedDate. If the sales order has no planned line for that date and its invoicing schedule is still open, one is created.

If any invoice can't be imported, none are. The response lists the new invoices' ids in the order you sent them. To read one, use GET accounting/invoices/:id.

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: importInvoicesInput.

Field Type Required Description
salesOrderId string yes The sales order these invoices bill. It must be flagged for externalized billing. One whose billing is scheduled in this system is refused with SALES_ORDER_NOT_EXTERNALISED. See id on GET accounting/sales-orders/:id. Example: "8c1f4a92-3e77-4d21-9b05-6a2f1c8e4d33".
invoices array<object> yes The invoices to record, one entry each, exactly as the source system grouped them. Every entry yields one invoice, and the ids come back in this order. Example: [{"externalInvoiceId":"NS-INV-88213","invoiceDate":"2026-09-01","lines":[{"salesOrderProductLineId":"b2d7f309-6c14-4a85-9f37-0e8c5d1b7a26","plannedDate":"2026-09-01","serviceEndDate":"2026-09-30","totalPrice":"1000.00","taxAmount":"210.00","taxCode":"NL-VAT-STD","taxMode":"TaxExclusive","taxLines":[{"name":"NL VAT","amount":"210.00","rate":"0.21","taxableAmount":"1000.00","taxType":"VAT","rateType":"Standard","taxTreatment":"domesticStandardRate"}]}]}].
invoices[].externalInvoiceId string no The id of this invoice in the source system, stamped on the created invoice for reconciliation.
invoices[].invoiceDate date yes The date to stamp on this invoice. Format YYYY-MM-DD.
invoices[].lines array<object> yes The lines of this invoice. Each resolves to one invoice-plan line by salesOrderProductLineId and plannedDate.
invoices[].lines[].salesOrderProductLineId string yes The unique identifier for the sales-order product line this invoice line bills against. There is no endpoint that returns a single sales-order product line.
invoices[].lines[].plannedDate date yes The planned billing date of the target invoice-plan line. Format YYYY-MM-DD. When no plan line exists for this date and the schedule is still open, one is created using this as both its service start date and its invoice date.
invoices[].lines[].serviceEndDate date no The last day of the service period, for a plan line created on an open schedule. Format YYYY-MM-DD. Ignored when the line matches an existing plan line.
invoices[].lines[].totalPrice decimal yes The pre-tax line total, in the sales order's currency. It must match the resolved plan line's total price. Any one-time discount must already be netted in.
invoices[].lines[].dutiesAndCustomsAmount decimal no The duties and customs charged on this line, in the sales order's currency. Requires the entity's Duties and Customs feature to be enabled.
invoices[].lines[].taxAmount decimal no The total tax on this line, in the sales order's currency. When given, it must equal the sum of taxLines[].amount. When omitted, it is computed from that sum.
invoices[].lines[].taxCode string no The line-level tax code, as the source tax engine issued it.
invoices[].lines[].taxDate date no The date the line's tax was determined on. Format YYYY-MM-DD. Typically the service end date or the invoice date.
invoices[].lines[].taxExemptCode string no The exempt code applied to this line.
invoices[].lines[].exceptionNumber string no The exception number for the tax exemption, as the source tax engine issued it.
invoices[].lines[].taxMode any no Whether totalPrice already includes tax. One of: "TaxInclusive", "TaxExclusive".
invoices[].lines[].taxLines array<object> yes The per-jurisdiction tax for this line. You supply it, because no tax engine is called on this path. Send an empty array for a tax-exempt line.
invoices[].lines[].taxLines[].name string yes The name of the tax as it should appear on the document, for example 'CA Sales Tax' or 'VAT'.
invoices[].lines[].taxLines[].amount decimal yes The tax amount for this jurisdiction, in the sales order's currency.
invoices[].lines[].taxLines[].rate decimal no The tax rate as a decimal fraction. For example, 0.21 is 21%. A rate, not an amount, so it has no currency.
invoices[].lines[].taxLines[].taxableAmount decimal no The pre-tax amount the rate was applied to, in the sales order's currency.
invoices[].lines[].taxLines[].exemptAmount decimal no The portion of the taxable base that was exempted, in the sales order's currency.
invoices[].lines[].taxLines[].taxType string no The engine classification of the tax, for example 'Sales' or 'VAT'.
invoices[].lines[].taxLines[].rateType string no The VAT rate classification, for example 'Standard', 'Reduced' or 'Zero'.
invoices[].lines[].taxLines[].vatCode string no The VAT code for this jurisdiction.
invoices[].lines[].taxLines[].isItemTaxable boolean no Whether the item is taxable in this jurisdiction.
invoices[].lines[].taxLines[].taxTreatment any no The VAT treatment for this jurisdiction. When supplied, the built-in VAT determination is bypassed. One of: "salesTax", "domesticStandardRate", "domesticReducedRate", "domesticSalesZeroRated", "domesticSalesExempt", "vatIntraEuStandardRate", "vatIntraEuReducedRate", "nonEuExempt", "reverseCharge".
{
  "salesOrderId": "8c1f4a92-3e77-4d21-9b05-6a2f1c8e4d33",
  "invoices": [
    {
      "externalInvoiceId": "NS-INV-88213",
      "invoiceDate": "2026-09-01",
      "lines": [
        {
          "salesOrderProductLineId": "b2d7f309-6c14-4a85-9f37-0e8c5d1b7a26",
          "plannedDate": "2026-09-01",
          "serviceEndDate": "2026-09-30",
          "totalPrice": "1000.00",
          "taxAmount": "210.00",
          "taxCode": "NL-VAT-STD",
          "taxMode": "TaxExclusive",
          "taxLines": [
            {
              "name": "NL VAT",
              "amount": "210.00",
              "rate": "0.21",
              "taxableAmount": "1000.00",
              "taxType": "VAT",
              "rateType": "Standard",
              "taxTreatment": "domesticStandardRate"
            }
          ]
        }
      ]
    }
  ]
}

Response

200 Success

Under data (importInvoicesOutput):

Field Type Required Description
ids array<string> yes The ids of the invoices recorded, one per requested entry and in the same order. Read each back with GET accounting/invoices/:id. Example: ["f3b9c1d4-2a68-4e07-8c15-9d0b7e5a3f62"].
{
  "data": {
    "ids": [
      "f3b9c1d4-2a68-4e07-8c15-9d0b7e5a3f62"
    ]
  },
  "diagnostics": [
    {
      "code": "INVOICES_IMPORTED",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": {
        "count": 0
      }
    }
  ]
}

Response headers

Errors

Code Status
SALES_ORDER_NOT_FOUND 400
SALES_ORDER_NOT_EXTERNALISED 400
SALES_ORDER_NOT_APPROVED 400
NO_INVOICES_REQUESTED 400
NO_LINES 400
IDEMPOTENCY_KEY_CONFLICT 409
IDEMPOTENCY_PAYLOAD_MISMATCH 422

Advisories

Warnings and information a successful response can carry under diagnostics:

Statuses