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.
- 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โ
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/invoices/import/csv/template | Download a sample file to fill in |
GET | /api/v1/invoices/import/csv/columns | List every column name and alias the importer accepts |
POST | /api/v1/invoices/import/csv/preview | Parse and fully validate without sending |
POST | /api/v1/invoices/import/csv/validation-report | Turn preview results into a downloadable error report |
POST | /api/v1/invoices/import/csv | Import and send, asynchronously |
GET | /api/v1/settings/company/import-defaults | Read the default buyer and default currency the import uses โ see the import default buyer |
PUT | /api/v1/settings/company/import-defaults | Set 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.
The recommended sequenceโ
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.
- Download the template so your column headers match.
- Preview the file. Fix what comes back.
- Import the file. This returns immediately with a job ID.
- 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.multiproduces a multi-line layout, where several rows sharing aninvoice_numberbecome one invoice with several lines.filetypeโcsv(default), orxlsxfor 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:
| Column | Meaning |
|---|---|
invoice_number | Unique invoice identifier, for example INV-2026-001 |
buyer_peppol_id | Customer's Peppol participant ID, as scheme:value |
buyer_name | Customer's company name |
line_description | Description 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.
| Field | Required | Format |
|---|---|---|
currency | No | Three capital letters, for example GBP |
buyer.peppol_id | Yes, if buyer is given | scheme:value: four digits, a colon, then up to 100 letters, digits, dots, hyphens or underscores |
buyer.name | Yes, if buyer is given | 1 to 300 characters |
buyer.country | Yes, if buyer is given | Two capital letters, for example GB |
buyer.vat_number | No | Up to 50 characters |
buyer.street | No | Up to 500 characters |
buyer.city | No | Up to 200 characters |
buyer.postal_code | No | Up 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".
| Column | Meaning |
|---|---|
issue_date | Date the invoice was issued; YYYY-MM-DD is preferred |
due_date | Payment due date |
currency | Three-letter currency code |
buyer_reference | The 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_reference | The customer's purchase order number (BT-13). This is where a PO belongs |
order_line_reference | The line number on the customer's purchase order (BT-132) |
line_accounting_cost | The customer's accounting string for the line, such as a general-ledger code or cost centre (BT-133) |
notes | Additional notes to appear on the invoice |
seller_peppol_id, seller_name, seller_vat, seller_country | Your own details |
buyer_vat, buyer_country, buyer_street, buyer_city | More customer details |
line_quantity | Quantity; defaults to 1 |
line_unit | Unit of measure, for example EA, HUR, DAY |
line_unit_price | Price per unit |
line_tax_rate | Tax rate as a percentage, for example 25 for 25% |
line_tax_category | Tax category for the line โ see Zero-rated and exempt lines |
total_payable | Total amount due; calculated if you leave it out |
payment_iban, payment_bic, payment_reference | Accepted without error, but not put on the invoice (as of 8 October 2026). See the warning below and issue #1584 |
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_referencewrites the header purchase order tocac:OrderReference/cbc:ID(BT-13). This is what a buyer such as DHL matches on. Put the PO number here.buyer_referencewrites 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:
| Column | Also accepted as |
|---|---|
order_reference | po_number, purchase_order, po, order_number |
order_line_reference | po_line_number, po_line |
line_accounting_cost | accounting_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 column | Read as |
|---|---|
Invoice.Number | invoice_number |
Invoice.Date | issue_date |
Invoice.DatePaymentDue | due_date |
Invoice.CustomerOrderNumber | order_reference โ the customer's purchase order number |
InvoiceItem.Description | line_description |
InvoiceItem.ItemDescription | line_description |
InvoiceItem.Quantity | line_quantity |
InvoiceItem.UnitPrice | line_unit_price |
InvoiceItem.VATRate | line_tax_rate |
InvoiceItem.AmountNet | line_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.
| Column | Also accepted as | What it is for |
|---|---|---|
line_tax_category | tax_category, vat_category | Says outright which category the line is |
line_tax_exemption_reason_code | vat_exemption_reason_code, tax_exemption_reason_code, exemption_code | The official code that justifies the zero rate or the exemption |
line_tax_exemption_reason | vat_exemption_reason, tax_exemption_reason, exemption_reason | The 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:
- If
line_tax_categoryhas a value, that value wins โ whatever the rate says. - Otherwise, if the rate is zero or missing, the row's own reason code decides:
VATEX-EU-ICmakes the line an intra-community supply, any other code beginningVATEXmakes it exempt, anything else makes it zero-rated. An intra-community supply also needs the buyer's VAT number, an invoicing period and adelivery_countryโ see the Belgium guide. - 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 itIntra-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
0248render 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.
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_errorsandtotal_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_mappingandunmapped_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โ
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โ
| Condition | What happens |
|---|---|
| File larger than 5 MB | 413, with the maximum stated in the message |
| Empty file | 400 |
Filename does not end in .xlsx | 400. CSV goes to POST /api/v1/import-jobs/csv, which is the same mechanism for a comma-separated file |
| Authentication | Ordinary 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โ
| Field | Meaning |
|---|---|
job | The 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 |
accepted | Invoices turned into drafts |
rejected | Rows refused, for example a missing required value |
duplicates | Invoice numbers your organisation already holds. These are skipped, not overwritten |
already_sent | Accepted 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_rows | Invoices found in the file after rows sharing an invoice number are grouped, so this counts invoices rather than spreadsheet lines |
errors | Up 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.
| Call | What you get |
|---|---|
GET /api/v1/import-jobs/{job_id}/errors | The 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.json | The same rejected rows as JSON, for showing in your own screen instead of downloading |
GET /api/v1/import-jobs/{job_id}/invoices.csv | The invoice drafts this one import created, as a CSV download |
GET /api/v1/import-jobs/export.csv | Every 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.
total, not with the length of errorstotal 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.
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โ
- Saved Parties โ store customers you invoice repeatedly instead of retyping them in every file
- Batch Sending โ the other bulk mechanism
- Validation โ what the validation layers check
- Tracking โ following documents after they are sent
- Error Handling