Skip to main content

CSV and Excel Import

Upload a spreadsheet of invoices and GoRoute turns each row into a Peppol document. This is the path for teams that cannot integrate with the API directly: export from your accounting system, check the file, upload it.

There are two upload routes and they end differently. One sends the invoices; the other only creates drafts for you to review. Check which one you are calling before you upload a real file.

The import accepts both CSV files and native Excel workbooks (.xlsx). An uploaded workbook is converted to CSV internally, so both formats run through exactly the same parsing, validation and identity checks.

Three different bulk mechanisms โ€” pick the right one
  • Import and send, under /api/v1/invoices/import/csv. You upload a spreadsheet and GoRoute sends every valid row. Most of this page covers it.
  • Import to drafts, under /api/v1/import-jobs. You upload a spreadsheet and GoRoute creates drafts that it does not send. See Upload a spreadsheet without sending anything.
  • Batch sending, under /api/v1/batches. You build a batch from documents you already hold โ€” see Batch Sending.

They are not interchangeable, and the first two differ in the one way that matters most: whether your customers receive an invoice.

The endpointsโ€‹

MethodPathPurpose
GET/api/v1/invoices/import/csv/templateDownload a sample file to fill in
GET/api/v1/invoices/import/csv/columnsList every column name and alias the importer accepts
POST/api/v1/invoices/import/csv/previewParse and fully validate without sending
POST/api/v1/invoices/import/csv/validation-reportTurn preview results into a downloadable error report
POST/api/v1/invoices/import/csvImport and send, asynchronously
GET/api/v1/settings/company/import-defaultsRead the default buyer and default currency the import uses โ€” see the import default buyer
PUT/api/v1/settings/company/import-defaultsSet or clear the default buyer and default currency the import uses

Preview and import require the csv_import feature to be enabled for your organisation, and the invoices:send permission. The template and column endpoints require invoices:read. Setting the import defaults requires settings:manage.

Preview first. The preview endpoint validates every invoice in the file against the same compliance stack the send path uses, so anything that would be rejected after sending is visible before you commit to it.

  1. Download the template so your column headers match.
  2. Preview the file. Fix what comes back.
  3. Import the file. This returns immediately with a job ID.
  4. Poll the job until it finishes, then fetch the result.

1. Download a templateโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/invoices/import/csv/template" \
-H "X-API-Key: YOUR_API_KEY" \
-o invoice_template_simple.csv

Two query parameters change what you get:

  • format โ€” simple (default) puts one invoice on one row. multi produces a multi-line layout, where several rows sharing an invoice_number become one invoice with several lines.
  • filetype โ€” csv (default), or xlsx for a native Excel workbook.

The template is tenant-aware. Organisations registered in Oman are given the additional Fawtara classification columns (document_type, om_invoice_kind, seller_identifier and the import, export and item-classification columns); other organisations are given a leaner template without them.

2. Check which columns are acceptedโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/invoices/import/csv/columns" \
-H "X-API-Key: YOUR_API_KEY"

This returns every field the importer understands, the aliases accepted for it, whether it is required, and a short description. It is the authoritative list โ€” it is generated from the parser itself, so it cannot drift from what the importer actually does. One exception: it does not yet list the Sage 50 report names described in Sage 50 report exports, although the importer accepts them (issue #1570).

Required columnsโ€‹

Four columns must be present and populated:

ColumnMeaning
invoice_numberUnique invoice identifier, for example INV-2026-001
buyer_peppol_idCustomer's Peppol participant ID, as scheme:value
buyer_nameCustomer's company name
line_descriptionDescription of the product or service

If your organisation has an import default buyer, a file with no buyer columns at all may leave out buyer_peppol_id and buyer_name. Only invoice_number and line_description are then required. See the next section.

Files that name no buyer: the import default buyerโ€‹

Some files go to one customer only, and the accounting system that exports them has no column for the customer. Instead of adding buyer columns to every file, your organisation can store one default buyer. The import then sends every invoice in such a file to that buyer.

It is all or nothing. The default buyer is used only when the file has no buyer column at all, meaning no column that maps to a buyer_* field. buyer_reference does not count as a buyer column. A file with even one buyer column, such as buyer_country, is read exactly as before: it must have buyer_peppol_id and buyer_name, and every row names its own buyer.

A default currency can be stored with it. An invoice whose currency is missing, because the file has no currency column or because the cell is blank, gets the default currency. With no default currency stored, such an invoice is in USD.

Read the current defaultsโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/settings/company/import-defaults" \
-H "X-API-Key: YOUR_API_KEY"

The answer is the stored object, or {} when nothing is set.

Set the defaultsโ€‹

curl -X PUT "https://app.goroute.ai/peppol-api/api/v1/settings/company/import-defaults" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "GBP",
"buyer": {
"peppol_id": "9932:gb123456789",
"name": "Example Customer Ltd",
"vat_number": "GB123456789",
"street": "1 High Street",
"city": "London",
"postal_code": "EC1A 1AA",
"country": "GB"
}
}'

This call needs the settings:manage permission. Both top-level fields are optional.

FieldRequiredFormat
currencyNoThree capital letters, for example GBP
buyer.peppol_idYes, if buyer is givenscheme:value: four digits, a colon, then up to 100 letters, digits, dots, hyphens or underscores
buyer.nameYes, if buyer is given1 to 300 characters
buyer.countryYes, if buyer is givenTwo capital letters, for example GB
buyer.vat_numberNoUp to 50 characters
buyer.streetNoUp to 500 characters
buyer.cityNoUp to 200 characters
buyer.postal_codeNoUp to 20 characters

PUT replaces the whole object. It does not merge with what is stored. Send the currency and the buyer together every time, or the one you leave out is removed. Send {} to clear both.

Where it appliesโ€‹

  • It applies to import and send and to its preview, so the preview reads the file the same way the import will.
  • It does not apply to import to drafts under /api/v1/import-jobs, or to Batch Sending. Those still need the buyer in the file.
  • A stored buyer that no longer validates as an invoice party is ignored. The file then needs buyer columns again.
  • There is no dashboard screen for setting it as of 8 October 2026. Use the two calls above. Once it is set, a file uploaded on the dashboard's import page uses it too, because that page sends the file through the same import.

Commonly used optional columnsโ€‹

Everything else is optional. Blank means "a standard invoice".

ColumnMeaning
issue_dateDate the invoice was issued; YYYY-MM-DD is preferred
due_datePayment due date
currencyThree-letter currency code
buyer_referenceThe reference the customer asked you to quote (BT-10). Not the place for a purchase order number โ€” see the note below. Left blank, it falls back to order_reference
order_referenceThe customer's purchase order number (BT-13). This is where a PO belongs
order_line_referenceThe line number on the customer's purchase order (BT-132)
line_accounting_costThe customer's accounting string for the line, such as a general-ledger code or cost centre (BT-133)
notesAdditional notes to appear on the invoice
seller_peppol_id, seller_name, seller_vat, seller_countryYour own details
buyer_vat, buyer_country, buyer_street, buyer_cityMore customer details
line_quantityQuantity; defaults to 1
line_unitUnit of measure, for example EA, HUR, DAY
line_unit_pricePrice per unit
line_tax_rateTax rate as a percentage, for example 25 for 25%
line_tax_categoryTax category for the line โ€” see Zero-rated and exempt lines
total_payableTotal amount due; calculated if you leave it out
payment_iban, payment_bic, payment_referenceAccepted without error, but not put on the invoice (as of 8 October 2026). See the warning below and issue #1584
A CSV or Excel import puts no bank details on the invoice

As of 8 October 2026, an invoice imported from a CSV or Excel file carries no payment instructions โ€” no bank account, no BIC and no payment reference. The payment_iban, payment_bic and payment_reference columns, and their aliases (iban, bank_account, bic, swift, payment_ref, remittance_info), are accepted without an error, but their values are not written to the invoice. The bank account in your company settings is not used either.

If your buyer needs your bank details on the invoice, check with them before you rely on an import. Engineering is tracking this as issue #1584.

If your seller details are missing from the file, they are filled in from your organisation's company settings. Your Own Details on the Invoice explains where those come from, how to read back exactly what will be used, and how to change it.

Purchase order numbers: use order_reference, not buyer_referenceโ€‹

Large buyers match an invoice to their purchase order, and they look for the PO in a specific place in the document. buyer_reference is not that place.

  • order_reference writes the header purchase order to cac:OrderReference/cbc:ID (BT-13). This is what a buyer such as DHL matches on. Put the PO number here.
  • buyer_reference writes BT-10, a free reference the customer asked you to quote. It is not where a PO is looked for.

If you leave buyer_reference blank, the purchase order is used for it as well, so a spreadsheet that has only ever used buyer_reference for its POs still produces the same BT-10 it always did. Adding order_reference does not take anything away โ€” it adds the field that was previously never written.

Two of the three columns are offered outside Oman only: order_line_reference and line_accounting_cost are line-level fields that PINT OM, Oman's invoice format, does not carry, so an Oman template does not offer columns that could never reach the document. The header purchase order does work on an Oman invoice: order_reference reaches cac:OrderReference there too. The downloadable template reflects this, so if you are an Oman tenant you will not see the two line columns in it.

Each of the three accepts the spellings an accounting export is likely to already use:

ColumnAlso accepted as
order_referencepo_number, purchase_order, po, order_number
order_line_referencepo_line_number, po_line
line_accounting_costaccounting_cost, gl_code, cost_centre, cost_center

As always, GET /api/v1/invoices/import/csv/columns is the authoritative list rather than this page โ€” except for the Sage 50 report names, which it does not list yet.

Column names are flexibleโ€‹

Most columns accept several spellings, so an export from your accounting system often works without renaming anything. invoice_number also accepts invoice_no, inv_num, number, invoice # and document_number. line_quantity also accepts quantity and qty. buyer_name also accepts customer_name, customer, recipient and to_name. Call the columns endpoint for the full mapping rather than guessing.

Sage 50 report exportsโ€‹

A report from the Sage 50 Accounts Report Designer, exported to CSV, can be uploaded as it is. Two things make that work.

The title line is skipped. Sage 50 puts a line such as Title: โ€ฆ,FileName: โ€ฆ,Exported On: โ€ฆ above the column headings. The importer skips it, together with any blank lines and any lines starting with # at the top of the file, and reads the first line after them as the headings.

The report's own column names are understood. A Sage 50 report names its columns after the report's variables, and the designer cannot always rename them. These ten names are read as the fields shown. Capitalisation is ignored.

Sage 50 columnRead as
Invoice.Numberinvoice_number
Invoice.Dateissue_date
Invoice.DatePaymentDuedue_date
Invoice.CustomerOrderNumberorder_reference โ€” the customer's purchase order number
InvoiceItem.Descriptionline_description
InvoiceItem.ItemDescriptionline_description
InvoiceItem.Quantityline_quantity
InvoiceItem.UnitPriceline_unit_price
InvoiceItem.VATRateline_tax_rate
InvoiceItem.AmountNetline_net_amount

Three names are deliberately not read: InvoiceItem.AmountVAT, Invoice.AmountWithCarriageGross and Invoice.VATRegistrationNumber. The invoice totals are calculated from its lines instead. A single line's VAT, or a gross amount that includes carriage, would not match the lines. Whose number Invoice.VATRegistrationNumber holds, yours or your customer's, depends on how the report was built.

Add the buyer. None of the Sage 50 names identifies your customer. Add a buyer_peppol_id and a buyer_name column to the report, as described under Required columns. If every invoice in the report goes to one customer, you can set an import default buyer instead.

The columns call does not list these ten names yet (issue #1570). The importer accepts them all the same.

Zero-rated and exempt linesโ€‹

A tax rate of zero does not say enough. Zero-rated and exempt are two different tax categories with two different reason-code lists, and a line sent under the wrong one is rejected. Three columns let you state which you mean.

ColumnAlso accepted asWhat it is for
line_tax_categorytax_category, vat_categorySays outright which category the line is
line_tax_exemption_reason_codevat_exemption_reason_code, tax_exemption_reason_code, exemption_codeThe official code that justifies the zero rate or the exemption
line_tax_exemption_reasonvat_exemption_reason, tax_exemption_reason, exemption_reasonThe same justification in words

line_tax_category accepts standard, zero, zero_rated, exempt, export, outside_scope, out_of_scope and intra_community (also intra-community). Capitalisation and surrounding spaces are ignored.

How the importer decidesโ€‹

For each line, in this order:

  1. If line_tax_category has a value, that value wins โ€” whatever the rate says.
  2. Otherwise, if the rate is zero or missing, the row's own reason code decides: VATEX-EU-IC makes the line an intra-community supply, any other code beginning VATEX makes it exempt, anything else makes it zero-rated. An intra-community supply also needs the buyer's VAT number, an invoicing period and a delivery_country โ€” see the Belgium guide.
  3. Otherwise the line is standard.

So a spreadsheet that already carries reason codes gets the right answer without a new column. Add line_tax_category when you want to be explicit, or when a line is exempt but carries no code beginning VATEX.

intra_community is refused on a PINT document โ€” and a reason code alone can trigger it

Intra-community supply is VAT category K, and category K exists in EN 16931 only โ€” the standard behind Peppol BIS Billing 3.0. A document that renders under a PINT profile has no such category, so GoRoute refuses the line before it checks anything else about it. The refusal is validation error BUS_033, and its message names the profile the document renders under and tells you to use the category that profile provides.

Two PINT profiles are in use, and which rows in your file are affected depends on the document, not on where you are:

  • Any invoice to a buyer in Australia or New Zealand renders PINT A-NZ, whoever the seller is. The buyer's country decides it on its own โ€” there is no Australian or New Zealand country pack, and the seller plays no part. An Australian or New Zealand seller invoicing an EU buyer is not affected: that document renders BIS 3.0 and the category works normally.
  • An Omani seller's invoice, an Omani buyer's import or self-billed document, and any invoice to a buyer addressed under Peppol scheme 0248 render PINT OM.

You can walk into this without ever using the category column. By rule 2 above, a line with a zero or missing rate that carries the reason code VATEX-EU-IC becomes an intra-community line on its own. So a file that inherits that code from a European template โ€” a shared export mapping, a copied row โ€” is refused on any row that is one of the documents above, with the column you would think to look at left empty. On an Omani invoice use Oman's own codes from CL-10-OM and CL-05-OM instead.

Check before you import. Send the same document to POST /api/v1/invoices/validate. One call tells you whether the value is accepted for that document on the platform you are calling, and /validate applies the same rules as every path that sends.

Two categories cannot be set from a spreadsheet

The platform recognises eight tax categories. This column reaches six of them. Reduced rate and reverse charge have no spelling here and cannot be selected through a CSV or Excel import at all.

A value the importer does not recognise is worse than a rejection: it records an error against the row and falls back to standard, and the row is still imported and sent. Read the preview before you import, and treat a tax-category error as a stop.

Oman: a reason code is mandatoryโ€‹

The Oman rulebook GoRoute ships and runs states it as a fatal rule:

[IBR-069-OM] - A VAT breakdown (IBG-23) with VAT Category code (IBT-118) "E" and/or "Z" must have a VAT exemption reason code (IBT-121).

In plain terms: every exempt or zero-rated line on an Omani invoice must carry a code in line_tax_exemption_reason_code, or the invoice fails validation. The two lists are separate โ€” zero-rating codes and exemption codes are not interchangeable. The Oman country guide lists both, and is the one place to look them up.

This quotation is taken from the Schematron rule file inside the product, not from a page on the Oman Tax Authority's website.

3. Preview before sendingโ€‹

import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://app.goroute.ai/peppol-api"

with open("invoices.csv", "rb") as fh:
response = requests.post(
f"{BASE_URL}/api/v1/invoices/import/csv/preview",
headers={"X-API-Key": API_KEY},
files={"file": ("invoices.csv", fh, "text/csv")},
)

preview = response.json()
print(preview["total_rows"], "rows")
print(preview["valid_invoices"], "valid,", preview["invalid_invoices"], "invalid")
print(preview["skipped_rows"], "rows skipped at parse time")

The response reports:

  • total_rows, valid_invoices, invalid_invoices, total_errors and total_warnings.
  • skipped_rows โ€” data rows dropped during parsing, for example a row with no invoice number. This is reported separately so that seventeen rows becoming four invoices is explained rather than looking like lost data.
  • column_mapping and unmapped_columns โ€” which of your headers were recognised, and which were ignored.
  • invoice_validations โ€” the full validation result for every invoice, not just the first few.

Every invoice is validated against the complete stack: the official UBL 2.1 schema, GoRoute's Peppol BIS 3.0 structure checks, calculation and business rules, eDEC code lists, CEN-EN16931 and Peppol BIS 3.0 Schematron rules, and the national-extension layer, which runs no national rules today. Each invoice in invoice_validations reports the layers separately as xsd_valid, structure_valid, business_valid, edec_valid, schematron_valid and cius_valid. See Validation.

4. Download an error reportโ€‹

For a file with many problems, turn the preview result into a report someone can work through in a spreadsheet.

response = requests.post(
f"{BASE_URL}/api/v1/invoices/import/csv/validation-report",
headers={"X-API-Key": API_KEY},
json={"invoice_validations": preview["invoice_validations"], "format": "csv"},
)

with open("validation_report.csv", "wb") as fh:
fh.write(response.content)

format accepts csv for a flat spreadsheet with one row per error, or json for the full structured report. The CSV columns are invoice number, status, layer, rule ID, severity, message, XPath and field.

5. Import and sendโ€‹

with open("invoices.csv", "rb") as fh:
response = requests.post(
f"{BASE_URL}/api/v1/invoices/import/csv",
headers={"X-API-Key": API_KEY},
files={"file": ("invoices.csv", fh, "text/csv")},
)

job = response.json() # HTTP 202
job_id = job["job_id"]
print(job_id, job["total_rows"], job["status_url"])

This returns 202 Accepted, not a completed result. The file is parsed immediately โ€” headers checked, rows counted, your default seller resolved โ€” and then an import job is created. The heavy work per row (rendering the Peppol XML, signing it where the jurisdiction requires it, checking it against the UBL 2.1 schema and the Schematron rules, and queueing for delivery) happens in the background, so the request returns within seconds even for tens of thousands of rows. A row that fails a layer of the stack โ€” the UBL 2.1 schema or the Schematron, for example โ€” is not sent: it appears in the job result's errors with validation_layer: COMPLIANCE_GATE, its findings prefixed with their rule id ([UBL-XSD] for the schema).

The maximum upload size is 100 MB.

Poll the jobโ€‹

The 202 response carries status_url and result_url. Poll the status until it reaches a terminal value โ€” validated, partially_validated or failed โ€” then fetch the result.

import time

while True:
status = requests.get(
f"{BASE_URL}/api/v1/import-jobs/{job_id}",
headers={"X-API-Key": API_KEY},
).json()

if status["status"] in ("validated", "partially_validated", "failed"):
break
time.sleep(2)

result = requests.get(
f"{BASE_URL}/api/v1/import-jobs/{job_id}/result.json",
headers={"X-API-Key": API_KEY},
).json()

partially_validated means some rows were accepted and others rejected. The result payload lists the transactions created and the errors for the rows that did not make it.

Upload a spreadsheet without sending anythingโ€‹

This import creates drafts. It does not send anything to your customers.

Every invoice this route accepts becomes a draft inside GoRoute, with the status ready. Nothing leaves the platform: no Peppol document is transmitted and no email is sent. You review the drafts and send them yourself afterwards.

If you want the upload itself to send, use the import-and-send route at POST /api/v1/invoices/import/csv instead.

This is the route for a business that keeps its invoices in a spreadsheet and wants them in GoRoute without installing anything: download the template, fill it in, upload it, then look at what arrived before committing to send it.

Download the Excel templateโ€‹

GET /api/v1/import-jobs/template
curl -X GET "https://app.goroute.ai/peppol-api/api/v1/import-jobs/template" \
-H "X-API-Key: YOUR_API_KEY" \
-o invoice-import-template.xlsx

You get an Excel workbook back as a file download, named invoice-import-template.xlsx. The endpoint takes no parameters.

This is a different template from /api/v1/invoices/import/csv/template described earlier on this page. That one belongs to the import-and-send route. Use the template that matches the route you intend to call.

Upload the completed workbookโ€‹

POST /api/v1/import-jobs/excel
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://app.goroute.ai/peppol-api"
XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

with open("invoices.xlsx", "rb") as fh:
response = requests.post(
f"{BASE_URL}/api/v1/import-jobs/excel",
headers={"X-API-Key": API_KEY},
files={"file": ("invoices.xlsx", fh, XLSX)},
)

result = response.json()
print(result["accepted"], "drafts created")
print(result["rejected"], "rejected,", result["duplicates"], "duplicates")

This call is synchronous. It does the work and returns the finished result, so there is no 202 and no job to poll. Send the file as a multipart upload in a field named file.

Limits and refusalsโ€‹

ConditionWhat happens
File larger than 5 MB413, with the maximum stated in the message
Empty file400
Filename does not end in .xlsx400. CSV goes to POST /api/v1/import-jobs/csv, which is the same mechanism for a comma-separated file
AuthenticationOrdinary organisation authentication. Neither endpoint requires an extra permission scope

The 5 MB cap applies to this synchronous route only. It is not the 100 MB cap of the import-and-send route described above.

What comes backโ€‹

FieldMeaning
jobThe import job record: its id, source_filename, source_format, status, the four row counts, oldest_issue_date, newest_issue_date, currency, created_at and completed_at
acceptedInvoices turned into drafts
rejectedRows refused, for example a missing required value
duplicatesInvoice numbers your organisation already holds. These are skipped, not overwritten
already_sentAccepted drafts whose row carried a date in email_sent_at โ€” your record that you had already sent that invoice yourself. This import still sent nothing
total_rowsInvoices found in the file after rows sharing an invoice number are grouped, so this counts invoices rather than spreadsheet lines
errorsUp to 200 error rows. Each carries row, column, message and value

The row number in an error is the spreadsheet row, counting the header as row 1, so it points at the line you need to fix.

job.status is validated when every invoice was accepted, partially_validated when some rows were rejected or skipped as duplicates, and failed when the file had rows but none of them was accepted. You can fetch the same summary later with GET /api/v1/import-jobs/{job_id}.

Getting the results and the failures back outโ€‹

The upload response is not the only way to see what happened. Three download calls let you come back to an import later, or hand a spreadsheet to whoever has to fix the rows. All three take ordinary organisation authentication โ€” no extra permission scope โ€” and all three exist on both the test and the live service, checked on 16 September 2026.

CallWhat you get
GET /api/v1/import-jobs/{job_id}/errorsThe rejected rows as a CSV download, columns row, column, message, value, filed as import-{job_id}-errors.csv
GET /api/v1/import-jobs/{job_id}/errors.jsonThe same rejected rows as JSON, for showing in your own screen instead of downloading
GET /api/v1/import-jobs/{job_id}/invoices.csvThe invoice drafts this one import created, as a CSV download
GET /api/v1/import-jobs/export.csvEvery invoice draft your organisation holds, as a CSV download

The rejected rows as JSONโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/import-jobs/JOB_ID/errors.json?limit=200" \
-H "X-API-Key: YOUR_API_KEY"

limit accepts 1 to 1000 and defaults to 200. The response carries job_id, filename, status, total, truncated and errors.

Count the failures with total, not with the length of errors

total is the job's own counter of rejected rows. errors is a sample โ€” the import writes a bounded list of error rows as it runs, so on a file with thousands of bad rows the array is shorter than the real number of failures, whatever limit you ask for. truncated is true when that has happened.

An integration that reports len(errors) to a user under-reports the failures, and on a big messy file it under-reports them badly. Read total.

The invoices one import createdโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/import-jobs/JOB_ID/invoices.csv" \
-H "X-API-Key: YOUR_API_KEY" \
-o import-invoices.csv

You get a CSV attachment with twelve columns: invoice_number, issue_date, due_date, buyer_name, buyer_peppol_id, currency, total_amount, status, email_status, email_to, email_sent_at, created_at. Newest issue date first. It covers the drafts this job created and nothing else, so it is the natural companion to the error report.

Every draft you holdโ€‹

curl -X GET "https://app.goroute.ai/peppol-api/api/v1/import-jobs/export.csv" \
-H "X-API-Key: YOUR_API_KEY" \
-o invoices-all.csv

Same twelve columns, filed as invoices-all.csv.

This is your whole organisation, in one response

export.csv has no date filter, no status filter and no paging. It returns every invoice draft your organisation has ever held, built in memory and sent as a single download. On a small account that is convenient. On an account with years of imports it is a very large file and a slow request, and there is no parameter that will narrow it.

If you want one import's invoices, use invoices.csv above. If you want a filtered view, list the jobs with GET /api/v1/import-jobs and fetch the ones you need.

Multi-line invoicesโ€‹

In the multi template layout, several rows that share the same invoice_number are grouped into a single invoice with several lines. This grouping is applied on both the preview and the import path, so what you see in the preview is what is sent.

Next Stepsโ€‹