Exports
The complete package of the organization's own data for its archive: every table as CSV and JSON, reports, stored files, hash-chain verification and hashes. Started by an administrator, built asynchronously, downloaded or fetched through a signed link.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/exports |
List the organization's exports | audit |
|
POST /v1/exports |
Export the organization's data | audit |
required |
GET /v1/exports/{run_id} |
Get an export's status | audit |
|
GET /v1/exports/{run_id}/download |
Download the package of an export | audit |
|
GET /v1/exports/{run_id}/link |
Get a link to download the package of an export | audit |
GET /v1/exports
List the organization's exports
The organization's exports, newest first, with their status. Pages of limit: pass the
last id as after for the next one. Needs the admin and audit scopes.
| Required scope | audit (admin implies every scope) |
| Operation id | list_exports_v1_exports_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | limit |
integer | Most exports returned, 1 to 100. (min 1; max 100; default 25) |
|
| query | after |
string (uuid) or null | Continue after this export (its id, the last of the previous page); omit for the first page. |
Example request
curl "$ERP_URL/v1/exports" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of ExportOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "expired",
"via": "platform",
"language": "de",
"note": "string",
"requested_by": {},
"requested_at": "2026-09-21T09:30:00Z",
"started_at": "2026-09-21T09:30:00Z",
"finished_at": "2026-09-21T09:30:00Z",
"stage": "string",
"progress": {},
"error": "string",
"cutoff": "2026-09-21T09:30:00Z",
"file_name": "string",
"size_bytes": 1,
"sha256": "string",
"manifest_sha256": "string",
"file_count": 1,
"row_count": 1,
"complete": true,
"statement": {},
"expires_at": "2026-09-21T09:30:00Z"
}
]
POST /v1/exports
Export the organization's data
Ask for the complete package of the organization's data, for its own archive: every
table of every entity and year as CSV (UTF-8, documented schema) and JSON, the reports of
every entity and fiscal year as PDF and CSV, every stored file checked against its SHA-256,
the verification of every entity's hash chain, a manifest with the hash of every file
(manifest.sha256, for sha256sum -c), the index.xml of the German Beschreibungsstandard
where an entity is German, a program that verifies the package, and a README in the
requested language with the legal references. Nothing secret is in it (no API key hash, no
session token).
The export is built asynchronously from one consistent snapshot of the database, so it can
be large: the answer is 202 with the export in status queued; follow it with
GET /v1/exports/{id} until it is succeeded, then download it
(GET /v1/exports/{id}/download, or a link with GET /v1/exports/{id}/link). The package
is kept for 30 days. One export at a time: 409 export_in_progress while another is
queued or running. Allowed also while the organization is suspended (it changes none of the
books). Needs the admin and audit scopes and a person's key, not an agent's. Starting is
an audit event (export.start).
A retry with the same Idempotency-Key returns the first answer and starts nothing more.
| Required scope | audit (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | start_export_v1_exports_post |
Request body
application/json (required): ExportRequest
| Field | Type | Required | Description |
|---|---|---|---|
language |
"de" | "en" | "fr" | "it" | "sv" or null |
The language of the package's README: en, de, fr, it or sv. Default: the language of the person who asks, else the country of the organization's first entity (de for CH and DE, fr for FR, it for IT, sv for SE), else en. |
|
note |
string or null | Why the export is made, up to 500 characters; kept with the request and in the audit trail. (max length 500) |
Example request
curl -X POST "$ERP_URL/v1/exports" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"language": "de",
"note": "Archive copy at contract end"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 202 | Successful Response | ExportOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "expired",
"via": "platform",
"language": "de",
"note": "string",
"requested_by": {},
"requested_at": "2026-09-21T09:30:00Z",
"started_at": "2026-09-21T09:30:00Z",
"finished_at": "2026-09-21T09:30:00Z",
"stage": "string",
"progress": {},
"error": "string",
"cutoff": "2026-09-21T09:30:00Z",
"file_name": "string",
"size_bytes": 1,
"sha256": "string",
"manifest_sha256": "string",
"file_count": 1,
"row_count": 1,
"complete": true,
"statement": {},
"expires_at": "2026-09-21T09:30:00Z"
}
GET /v1/exports/{run_id}
Get an export's status
One export: queued, running (with the stage it is in and its progress), succeeded
(with the file's name, size and SHA-256, the hash of the package's own checksum list, the
number of files and rows, whether it is complete, and the hand-over statement), failed
(with the reason), or expired (the package was deleted after its 30 days). 404 for an
unknown export or one of another organization. Needs the admin and audit scopes.
| Required scope | audit (admin implies every scope) |
| Operation id | get_export_v1_exports__run_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | run_id |
string (uuid) | yes | Id of an export (GET /v1/exports). |
Example request
curl "$ERP_URL/v1/exports/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ExportOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"org_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "expired",
"via": "platform",
"language": "de",
"note": "string",
"requested_by": {},
"requested_at": "2026-09-21T09:30:00Z",
"started_at": "2026-09-21T09:30:00Z",
"finished_at": "2026-09-21T09:30:00Z",
"stage": "string",
"progress": {},
"error": "string",
"cutoff": "2026-09-21T09:30:00Z",
"file_name": "string",
"size_bytes": 1,
"sha256": "string",
"manifest_sha256": "string",
"file_count": 1,
"row_count": 1,
"complete": true,
"statement": {},
"expires_at": "2026-09-21T09:30:00Z"
}
GET /v1/exports/{run_id}/download
Download the package of an export
The package of a succeeded export, as a ZIP, streamed (it can be many gigabytes: use a
client that writes to a file, and compare the SHA-256 with sha256 of the export or with
the X-Content-SHA256 header). For a large package, GET /v1/exports/{id}/link gives a
link that downloads straight from the store. Every download is an audit event
(export.download). 409 export_not_ready while the export has not succeeded or after it
has expired, 404 for an unknown export or one of another organization. Needs the admin
and audit scopes.
| Required scope | audit (admin implies every scope) |
| Operation id | download_export_v1_exports__run_id__download_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | run_id |
string (uuid) | yes | Id of an export (GET /v1/exports). |
Example request
curl "$ERP_URL/v1/exports/3fa85f64-5717-4562-b3fc-2c963f66afa6/download" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | The package: a ZIP, streamed (SHA-256 in X-Content-SHA256). |
application/zip |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
GET /v1/exports/{run_id}/link
Get a link to download the package of an export
A signed link that downloads the package straight from the store, without credentials,
for seven days at most (and never beyond the package's own expiry). Anyone who holds the link
can download until it ends: treat it like a password. Giving the link is an audit event
(export.link). 409 export_not_ready while the export has not succeeded or after it has
expired, and 409 no_signed_link on a deployment that cannot sign links (use /download).
Needs the admin and audit scopes.
| Required scope | audit (admin implies every scope) |
| Operation id | export_link_v1_exports__run_id__link_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | run_id |
string (uuid) | yes | Id of an export (GET /v1/exports). |
Example request
curl "$ERP_URL/v1/exports/3fa85f64-5717-4562-b3fc-2c963f66afa6/link" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ExportLink |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"url": "string",
"expires_at": "2026-09-21T09:30:00Z",
"file_name": "string",
"size_bytes": 1,
"sha256": "string"
}