Exports and handover packs
Three routes, all of them answering with a ZIP file rather than JSON. Two are data portability: everything Syllevo holds, in a form another system can read. The third is the month-end handover pack, the one click that replaces an accountant's monthly gather-and-rename.
Routes
| Method | Path | Needs | Answers |
|---|---|---|---|
| GET | /v1/exports/organization |
Admin |
application/zip |
| GET | /v1/exports/profiles/{id} |
View |
application/zip |
| GET | /v1/profiles/{profileId}/handover-pack |
View |
application/zip |
How these responses behave
All three are built as they are sent, which changes three things a client normally relies on.
- No
Content-Length. The size is unknowable before the last byte is compressed, and inventing one would be a lie your HTTP library would catch. The response is chunked, and a browser shows an indeterminate download. - The file name is in
Content-Disposition. Read it from there rather than making one up. - A failure can only happen before the first byte. Everything that can refuse, permissions
included, runs while the response is still empty. Once bytes are flowing the status line is
already
200, so treat a truncated archive as a failed download and ask again.
There is no job to create and no job to poll. One request, one file, however large. A firm with three hundred clients gets one ZIP.
Exports are never gated
An export answers 200 with a full archive for every organization in every subscription state: on
any package, during grace, after a refund, and at cancellation. Portability is not something a
subscription buys.
It lasts as long as the organization does. After a subscription ends, that is 90 days: then the
organization is deleted, with its documents. GET /v1/billing/closure has the date. See
Billing.
What a package changes is one file inside the ZIP. invoices-saga.csv, the ledger handoff, comes
with the 10-client package and up. When an organization is not entitled to it, the file is left out,
manifest.json says so in plain language, and the rest of the archive is unaffected. The export
still runs to completion. It is never a status code and never a refusal.
The generic invoices.csv always ships. Every invoice in the Saga file is also in invoices.json
and invoices.csv, so nothing is withheld, only the convenience layout.
GET /v1/exports/organization
Everything the organization holds. Admin and above.
File name: syllevo-export-organization-{organizationId}-{yyyy-MM-dd}.zip.
Inside:
| Entry | What it holds |
|---|---|
README.txt |
What this archive is, in plain language |
manifest.json |
What the export covers, counts, and any original that could not be fetched |
profiles.json |
The client companies covered |
profiles/{id}/ |
One folder per client |
invoices.csv |
Every purchase invoice, one row each |
sales-invoices.csv |
Every imported sales invoice, one row each |
invoices-saga.csv |
The same purchases in an accounting package's layout. Entitlement only |
originals/ |
The original files, named by content hash |
Each profiles/{id}/ folder holds suppliers.json, documents.json, invoices.json,
sales-invoices.json, reminders.json, and contacts.json.
Purchases and sales are in separate files, so a customer never lands in a supplier column.
sales-invoices.json and sales-invoices.csv carry the customer's name, tax id, and country where
invoices.json carries the supplier. A sale's original files are in originals/ and listed in
documents.json like any other. manifest.json counts sales in counts.salesInvoices and gives
the CSV's version in salesInvoicesCsvFormatVersion. manifestFormatVersion is 3.
The Saga file and the handover pack stay purchases only. They are the purchase ledger handoff.
Credit notes are negative in every file, the Saga file included. invoices.json and
sales-invoices.json carry documentTypeCode, correctedInvoiceNumber, and
correctedInvoiceIssueDate on each invoice. invoices.csv and sales-invoices.csv add the same
three as their last columns, document_type_code, corrected_invoice_number, and
corrected_invoice_issue_date, which is why invoicesCsvFormatVersion and
salesInvoicesCsvFormatVersion are 2. The earlier columns did not move. The Saga file's columns
did not change.
Read manifest.json first. It is written last, it carries the true counts, and it names any
original the export could not fetch from storage. A missing original is never a crash and never a
silent gap: the archive says what it could not get.
Originals are deduplicated by content. Two clients who received the same file share one copy in
originals/, so there are often fewer files there than rows in documents.json. Follow the
originalPath field on each document to find its file. An empty originalPath means the file could
not be fetched, and the manifest says why.
GET /v1/exports/profiles/{id}
The same archive, narrowed to one client company. View on that client.
File name: syllevo-export-profile-{profileId}-{yyyy-MM-dd}.zip.
Same layout, same manifest rules, same Saga-file behavior. Use this one when a client leaves the firm: the export is the deliverable, and it is complete without anybody's permission but the caller's.
GET /v1/profiles/{profileId}/handover-pack
One client's month, or several months, in the shape an accountant actually files. View on the
client.
| Parameter | Type | Means |
|---|---|---|
month |
string | One month as "2026-08". Omitted means the month running now |
months |
string, repeatable | Several months: ?months=2026-07&months=2026-09 |
source |
DocumentSourceKind |
0 e-Factura arrivals only, 1 everything else. Omitted means all |
Sending month and months together answers 400. At most twelve months, none repeated.
File name: syllevo-handover-{profileId}-2026-08.zip for one month, and
syllevo-handover-{profileId}-2026-07_2026-09.zip for several, naming the first and the last. The
months in between are not spelled out in the name; manifest.json lists every month the pack
actually holds.
Inside a one-month pack:
| Entry | What it holds |
|---|---|
manifest.json |
What the pack covers, counts, and anything that could not be fetched |
summary.pdf |
One page a person reads: the month's invoices as a table, with totals |
documents/ |
Every document evidencing an invoice issued in the month |
unmatched/ |
Documents that arrived in the month with no invoice to belong to |
invoices-saga.csv |
The month in the ledger's layout. Entitlement only, same rule as exports |
A pack covering several months puts each one in a folder named after it, 2026-07/documents/...,
and adds a root manifest.json listing the months. A one-month pack has no month folder, on
purpose: the folder exists to tell two months apart, and every pack an accountant has already filed
keeps the shape it had.
Documents are renamed on the way in, which is the point of the pack. A matched document is
2026-08_Supplier_InvoiceNumber.pdf, and a second document evidencing the same invoice gets a
counter, ..._F123_2.pdf. An unmatched one is named by arrival date plus the name it had when it
arrived, because that is all that is known about it. Names are sanitized so the archive extracts
cleanly on Windows as well as anywhere else, and the extension comes from the actual content type
rather than from whatever the original name claimed.
unmatched/ is in the pack rather than left out. Nothing missing at month-end includes the files
nobody has read yet.
An empty month answers 200, with a pack that says nothing was received. It is not a 404. An
accountant asking for a quiet month should get the evidence that it was quiet.
GET /v1/profiles/22222222-2222-4222-8222-222222222222/handover-pack?month=2026-08 HTTP/1.1
Host: api.syllevo.com
X-Api-Key: syl_live_examplekeynotreal000000000000
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename=syllevo-handover-22222222-2222-4222-8222-222222222222-2026-08.zip
Rate limits
None of these three carry a named rate limit policy. They are governed by the global limiter's organization partition, 1200 requests a minute for the whole organization. See Conventions.
That is a generous ceiling for a large file, so the practical limit is bandwidth rather than the limiter. Do not run exports in a loop across a portfolio in parallel; run them in sequence.