# External API V1 Documentation

Convert invoices into validated e-invoices from your own systems: upload a PDF, DOCX, or TXT document — or structured ERP data — and download XRechnung, ZUGFeRD, EN 16931, UBL, or CII output once validation has passed. This page is the complete integration contract: access model, endpoints, error catalog, and limits.

> Five REST endpoints turn PDF, DOCX, or TXT invoices — or structured ERP data — into validated XRechnung, ZUGFeRD, EN 16931, UBL, and CII e-invoices. Active Enterprise subscribers create keys directly; 100 shared Email/API conversions are included each month, then each conversion uses €0.40–0.50 in prepaid credits.

## Overview

The API accepts multipart uploads, returns JSON responses, and uses standard HTTP status codes with Bearer authentication. Every conversion runs asynchronously: submit the document, poll the task, then download the result. A file is delivered only after validation has passed — there is no unvalidated output.

Send a PDF, DOCX, or TXT invoice document or structured invoice data to a conversion endpoint. Invoice-Converter starts one async task for extraction, validation, and artifact generation. The result endpoint returns a file only when the requested artifact is validated, checked, and ready; while processing it returns 202 TASK_NOT_READY, and blocking validation issues return 422 VALIDATION_FAILED.

> **Status: Enterprise access**: Base path: /api/v1. Last synchronized 2026-09-08.

## Key capabilities

- Upload endpoints for PDF invoices and structured invoice data
- Invoice data extraction with source-field review
- Automated EN 16931 and KoSIT validation
- XRechnung, ZUGFeRD, EN16931, UBL, and CII output formats
- Async processing with polling; small invoices often take around 30 seconds, larger invoices up to 1-2 minutes
- Idempotent writes for safe retries

## Start Enterprise API access

Every active Enterprise subscriber can create production API keys directly in the profile.

1. Create an account and start Enterprise from the pricing page: €35/month with annual billing (€420/year); month-to-month: €50/month.
2. Use the 100 shared Email/API conversions included each month; additional conversions use prepaid credits at €0.40–0.50 each.
3. Create a live API key from the API access section in your profile.
4. Run the first request with server-side credentials, then monitor usage and rotate keys from your profile.

## Start Enterprise

- [Enterprise plan and pricing](/pricing)

## Quickstart

Three API calls complete a conversion. The convert endpoint is served at /api/v1 and requires authentication.

### POST /api/v1/invoices:convert (Live)
Convert invoice document to structured e-invoice

### POST /api/v1/invoices:convert-structured (Live)
Convert structured data

### GET /api/v1/tasks/{task_id} (Live)
Poll task status

## Quickstart with curl

Replace $API_KEY with your live key and $TASK_ID with the task_id from the first response. The same three calls work for every output format.

### 1. Start the conversion
```
curl -X POST "https://www.invoice-converter.com/api/v1/invoices:convert" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: inv-2026-0001" \
  -F "file=@invoice.pdf" \
  -F "format=XRECHNUNG"
```

### 2. Poll the task until completed
```
curl "https://www.invoice-converter.com/api/v1/tasks/$TASK_ID" \
  -H "Authorization: Bearer $API_KEY"
```

### 3. Download the validated file
```
curl -o invoice.xml \
  "https://www.invoice-converter.com/api/v1/tasks/$TASK_ID/result?download=xml" \
  -H "Authorization: Bearer $API_KEY"
```

## Base URL and API keys

- Production base URL: `https://www.invoice-converter.com/api/v1`.
- Live keys use the production host and the `icp_...` prefix.
- Run onboarding and validation requests with your live key before sending production volume.
- Treat keys as server-side secrets. Do not embed them in browser or mobile clients.

## First successful request

Use this sequence as the minimum happy path after creating an API key.

- Upload: `POST /api/v1/invoices:convert` with `Authorization`, `Idempotency-Key`, `file=@invoice.pdf` (or `.docx`/`.txt`), and `format=XRECHNUNG`.
- Poll with backoff: wait about `20 seconds` after the `202`, then call `GET /api/v1/tasks/{task_id}` on 20s, 30s, 45s, 60s, and 60s intervals until status is `completed` or `failed`. Stay inside the `10/min` and `120/hour` status quota, and give up after about 16 minutes.
- Download: `GET /api/v1/tasks/{task_id}/result?download=xml` and store `X-Correlation-ID` for support tracing.
- For ZUGFeRD PDF output, request `format=ZUGFERD` on convert and `download=pdf` on result; hybrid PDF output requires a PDF source upload.
- For structured input, call `POST /api/v1/invoices:convert-structured` with `pdf_file=@invoice.pdf`, `data_file=@invoice-data.json`, and the target `format`.
- Optionally send `client_reference` or `external_invoice_id` and `source_system` for ERP reconciliation.
- For split ERP exports of one invoice, repeat `data_file`; for multiple invoices, start one task per invoice with its own idempotency key.
- Store `result_artifacts` from the status response to see whether XML/PDF artifacts are validated, cached, or still unavailable because of dependencies.

## Common payload examples

- `XRECHNUNG`: send `format=XRECHNUNG`.
- `ZUGFERD`: send `format=ZUGFERD`; use `download=pdf` on result for the hybrid PDF/A-3 output.
- `Structured input`: send `pdf_file` plus one or more `data_file` parts; accepted data formats are CSV, JSON, XML, XLSX, and TXT, with any supported target format. The data_file parts must contain all mandatory data; the PDF does not fill missing fields.
- `Multiple invoices`: submit separate convert requests and track each returned `task_id`; repeated `data_file` parts are only for split exports of the same invoice.
- `UBL`: send `format=UBL`; accepted profiles are `XRECHNUNG`, `PEPPOL`, and `EN16931`, defaulting to `EN16931`.
- `CII`: send `format=CII`; accepted profiles are `XRECHNUNG`, `EN16931`, `ZUGFERD_EN16931`, `ZUGFERD_FACTURX_EXTENDED`, and `ZUGFERD_XRECHNUNG`, defaulting to `EN16931`.
- `format` x `profile` is a closed table: `XRECHNUNG` accepts `[XRECHNUNG]` (default `XRECHNUNG`), `EN16931` accepts `[EN16931]` (default `EN16931`), `UBL` accepts `[XRECHNUNG, PEPPOL, EN16931]` (default `EN16931`), `CII` accepts `[XRECHNUNG, EN16931, ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG]` (default `EN16931`), and `ZUGFERD` accepts `[ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG]` (default `ZUGFERD_EN16931`). Profiles are matched case-insensitively; `ZUGFERD`, `FACTURX`, `FACTUR-X`, and `FACTUR_X` alias `ZUGFERD_EN16931`, and `ZUGFERD-XRECHNUNG` aliases `ZUGFERD_XRECHNUNG`.

## Required request headers

- Authorization: Bearer <api_key>

## Auth rules

Every active Enterprise subscriber can create API keys from the profile and use them as Bearer tokens. The shared Email/API allowance includes 100 conversions per month; additional conversions use prepaid credits at €0.40–0.50 each.

- Keys are tenant-scoped live credentials for active Enterprise subscriptions. The current production prefix is `icp_...`.
- Create, rotate, and revoke API keys from your profile while Enterprise is active. Copy new keys immediately because plaintext keys are shown only once.
- Missing or invalid key returns `401`.
- Calls to `/api/v1` receive an `X-Correlation-ID` automatically when omitted.
- Write calls require `Idempotency-Key`; keep this value stable across retries.
- Use server-to-server integration from your backend. Browser-origin access is restricted in production.

## Idempotency contract

- Send an `Idempotency-Key` on every write call.
- Idempotency keys must match `[A-Za-z0-9._:-]+` and be at most 200 chars.
- If you provide your own key, the same key + identical payload returns the cached response.
- Same key + different payload returns `409 IDEMPOTENCY_CONFLICT`, which is not retryable; use a new key for a new payload.
- A second request with the same key while the first is still processing returns `409 IDEMPOTENCY_IN_PROGRESS`; retry the same key after a short delay. A stuck in-progress claim is reclaimed after `15 minutes`.
- Once the original task is past its 24-hour retention, a replay returns `409 IDEMPOTENCY_REPLAY_EXPIRED`; start a new conversion with a new key.
- Idempotency records live for `24 hours`, matching task retention.

## Endpoint Reference

All endpoints are available under /api/v1. Timeouts surface as 504 and other temporary connectivity failures as 502; correlation IDs help support trace requests end-to-end.

### POST /api/v1/invoices:convert (Live)
Upload a PDF, DOCX, or TXT invoice document and start asynchronous conversion. Returns a task_id for polling. ZUGFeRD/Factur-X hybrid PDF downloads require a PDF source upload; DOCX/TXT sources should request XML results. Embedded invoice XML is ignored by default; set use_embedded_xml=true only when your integration trusts it as the primary extraction source. Request: multipart/form-data; file (binary, required) — PDF, DOCX, or TXT invoice source document; legacy DOC/RTF, images, and other files are rejected; format (string, required) — target output format; see format matrix below; profile (string, optional, recommended for deterministic integrations) — explicit compliance profile, matched case-insensitively. Each format has a closed accepted set and one default: XRECHNUNG → [XRECHNUNG] (default XRECHNUNG); EN16931 → [EN16931] (default EN16931); UBL → [XRECHNUNG, PEPPOL, EN16931] (default EN16931); CII → [XRECHNUNG, EN16931, ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG] (default EN16931); ZUGFERD → [ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG] (default ZUGFERD_EN16931). ZUGFERD, FACTURX, FACTUR-X, and FACTUR_X alias ZUGFERD_EN16931; ZUGFERD-XRECHNUNG aliases ZUGFERD_XRECHNUNG. A value outside the accepted set returns 422 OUTPUT_PROFILE_CONFLICT; an unrecognized profile name returns 422 INVALID_PROFILE with details.allowed_profiles; jurisdiction (string, optional) — explicit ISO 3166-1 alpha-2 jurisdiction context used for validation/advisory checks; does not override profile; transaction_scope (string, optional) — explicit transaction scope context, such as B2G; applied to the queued task; delivery_channel (string, optional) — one of PEPPOL, DIRECT_XML, PORTAL, EMAIL_PDF, UNKNOWN; applied to the queued task; client_reference or external_invoice_id (string, optional) — tenant-side invoice/job reference returned on accepted uploads and task status responses; source_system (string, optional) — upstream ERP or billing system label returned on accepted uploads and task status responses; email_input (free text, optional, PDF sources only) — customer instructions in any language, up to 10,000 characters, supplied without email block markers; all AI extraction and correction steps receive them separately from the invoice source text; cannot be combined with use_embedded_xml=true, is part of the request identity used for idempotency, and is not supported by invoices:convert-structured; standard invoice validation still applies; use_seller_master_data (boolean, optional) — when omitted, the tenant profile default applies; false ignores saved seller defaults for this request, true provides/uses seller defaults; seller_master_data (JSON object string, optional) — request-scoped seller defaults used only when use_seller_master_data=true; supported keys include business_name, address, tax, electronic address, contact, and payment fields (payment_means_code 30/42/58, payment_iban, payment_bic, payment_account_name, payment_terms_note); every supplied profile value replaces the corresponding extracted value, absent profile fields remain unchanged, and differences create non-blocking review warnings; electronic_address and electronic_address_scheme must be supplied together or both omitted; use_embedded_xml (boolean, optional, default false) — embedded Factur-X, ZUGFeRD, or XRechnung XML is ignored unless explicitly set to true; use this only when the integration trusts the embedded XML as the primary extraction source. Response: 202 Accepted.

### POST /api/v1/invoices:convert-structured (Live)
Upload a carrier PDF plus CSV, JSON, XML, XLSX, or TXT invoice data and start asynchronous structured-data conversion. The data_file parts are the only semantic source; the PDF does not fill missing invoice fields. For ZUGFeRD/Factur-X it is used as the carrier PDF, and for XML-oriented outputs it is retained as the submitted PDF artifact. Use one conversion request per invoice; repeat data_file only for split ERP exports that describe the same invoice. Request: multipart/form-data; pdf_file (binary, required) — carrier PDF used for ZUGFeRD/Factur-X embedding and retained for XML-oriented outputs; data_file (binary, required, repeatable) — CSV, JSON, XML, XLSX, or TXT invoice data used as the only semantic source; .xls, PDFs, and image files are rejected as data_file; repeat for split header/line exports of the same invoice; aliases data_files and data_files[] are accepted; structured data total size — max 2 MB across all data_file parts; format (string, required) — target output format; supports XRECHNUNG, ZUGFERD, EN16931, UBL, and CII; profile (string, optional, recommended for deterministic integrations) — explicit compliance profile, matched case-insensitively. Each format has a closed accepted set and one default: XRECHNUNG → [XRECHNUNG] (default XRECHNUNG); EN16931 → [EN16931] (default EN16931); UBL → [XRECHNUNG, PEPPOL, EN16931] (default EN16931); CII → [XRECHNUNG, EN16931, ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG] (default EN16931); ZUGFERD → [ZUGFERD_EN16931, ZUGFERD_FACTURX_EXTENDED, ZUGFERD_XRECHNUNG] (default ZUGFERD_EN16931). A value outside the accepted set returns 422 OUTPUT_PROFILE_CONFLICT; an unrecognized profile name returns 422 INVALID_PROFILE with details.allowed_profiles; jurisdiction (string, optional) — explicit ISO 3166-1 alpha-2 jurisdiction context used for validation/advisory checks; does not override profile; transaction_scope (string, optional) — explicit transaction scope context, such as B2G; applied to the queued task; delivery_channel (string, optional) — one of PEPPOL, DIRECT_XML, PORTAL, EMAIL_PDF, UNKNOWN; applied to the queued task; client_reference or external_invoice_id (string, optional) — tenant-side invoice/job reference returned on accepted uploads and task status responses; source_system (string, optional) — upstream ERP or billing system label returned on accepted uploads and task status responses; use_seller_master_data (boolean, optional) — when omitted, the tenant profile default applies; false ignores saved seller defaults for this request, true provides/uses seller defaults; seller_master_data (JSON object string, optional) — request-scoped seller defaults used only when use_seller_master_data=true; supported keys include business_name, address, tax, electronic address, contact, and payment fields (payment_means_code 30/42/58, payment_iban, payment_bic, payment_account_name, payment_terms_note); every supplied profile value replaces the corresponding extracted value, absent profile fields remain unchanged, and differences create non-blocking review warnings; electronic_address and electronic_address_scheme must be supplied together or both omitted. Response: 202 Accepted.

### GET /api/v1/tasks/{task_id} (Live)
Poll the current status of a conversion task. Returns pending (accepted and queued, not started yet), processing, completed, or failed. Rate limit 10/min and 120/hour, which is the binding constraint for polling: wait about 20 seconds after the accepted 202 before the first call, then back off (20s, 30s, 45s, 60s, and 60s from there on) and stop on completed or failed. Completed tasks include `result_artifacts` diagnostics so clients can see which XML/PDF artifacts are available, cached, and validation-proven. Completed task payloads may include additive `_processing_warnings` and `_validation_warnings` entries with SOURCE_CONTEXT_* rule IDs when source evidence was unavailable, suspect, or truncated; treat them as review signals, not failures. When failed, the response includes an error field with the failure reason. Request: none (GET); task_id (path, required) — UUID returned by the convert endpoint; include_validation_report_html (query, optional) — true or false (default false); when true, the status response inlines the sanitized validation report HTML for the current strict artifact when available. Response: 200 OK.

### GET /api/v1/tasks/{task_id}/result (Live)
Download the generated file (XML or PDF). Result syntax matches the original task format: XRECHNUNG/EN16931/UBL return UBL XML, CII/ZUGFERD return CII XML, and ZUGFERD + download=pdf returns a hybrid PDF/A-3. For other formats, download=pdf can return a rendered PDF; on a completed task download=xml is the expected-available artifact, not a guaranteed one. Repeated downloads may be served from cached generated artifacts when validation proof is still current. While processing, this endpoint returns a 202 carrying the standard error envelope ({"code":"TASK_NOT_READY","message":"Strict conversion is still processing. No validated artifact is available yet.","correlation_id":"<uuid>"}); blocking validation issues return 422 VALIDATION_FAILED, retryable dependency gaps return 503, terminal conversion failures return 500 TASK_FAILED with the reason in details.code, and artifact invariant failures return 500 INTERNAL_ARTIFACT_INVARIANT_FAILED, all with no file body. Successful downloads carry X-Correlation-ID, Content-Disposition, Cache-Control: no-store, X-Artifact-Sha256, X-Validation-Proof-Id, X-Artifact-Proof-Id, X-Artifact-State, X-Validation-State, X-Proof-Status, and X-Validator-Bundle-Id; X-Task-Id is not set on this endpoint. Rate limit 10/min and about 134/hour. Request: none (GET); task_id (path, required) — UUID returned by the convert endpoint; download (query, required) — xml or pdf. Response: 200 OK.

### GET /api/v1/tasks/{task_id}/validation-report (Live)
Download the report for the current validated result artifact. The report is available only after strict conversion stores an artifact with current proof; otherwise this endpoint returns 202 or 404. Response headers identify the artifact and report proof. X-Artifact-Sha256 identifies the result artifact, not the report file. Rate limit: 10/min and 120/hour. Request: none (GET); task_id (path, required) — UUID returned by a conversion endpoint; download (query, optional) — html or xml. Response: 200 OK.

## Output format matrix

| Format | Syntax | Version / Profile | Content-Type | Extension |
| --- | --- | --- | --- | --- |
| XRECHNUNG | UBL 2.1 XML | XRechnung 3.0.2 | application/xml | .xml |
| ZUGFERD | CII XML (download=xml) / hybrid PDF/A-3 (download=pdf) | ZUGFeRD 2.5 / Factur-X 1.09 | application/xml or application/pdf | .xml / .pdf |
| EN16931 | UBL 2.1 XML | EN 16931 | application/xml | .xml |
| UBL | UBL 2.1 XML | OASIS UBL 2.1 | application/xml | .xml |
| CII | UN/CEFACT CII XML | D16B | application/xml | .xml |

## Error Contract

| Code | HTTP | Retryable | Notes |
| --- | --- | --- | --- |
| AUTHENTICATION_REQUIRED | 401 | No | Missing/empty bearer token |
| INVALID_API_KEY | 401 | No | API key not found/revoked/expired |
| API_NOT_ENABLED_FOR_TENANT | 403 | No | The key is valid, but External API access is disabled for this account; contact support |
| INSUFFICIENT_API_CREDITS | 402 | No | Included monthly allowance plus prepaid API credits could not cover the request. Two details shapes: prepaid (remaining, minimum_purchase 100) and included allowance (included_remaining, credit_remaining, shortfall, minimum_purchase 100). Parse on code and read whichever keys are present |
| IDEMPOTENCY_KEY_REQUIRED | 400 | No | Write endpoint called without Idempotency-Key |
| INVALID_IDEMPOTENCY_KEY | 400 | No | Idempotency key must match [A-Za-z0-9._:-]+ and be at most 200 characters |
| IDEMPOTENCY_CONFLICT | 409 | No | The key was already used with a different payload, or the idempotent claim could not be started; use a new key for a new payload |
| IDEMPOTENCY_IN_PROGRESS | 409 | Yes | The first request with this key is still processing; retry the SAME key after a short delay. A stuck in-progress claim is reclaimed after 15 minutes |
| IDEMPOTENCY_REPLAY_EXPIRED | 409 | No | The original task is past its 24-hour retention; start a new conversion with a new key |
| FORMAT_REQUIRED | 400 | No | Conversion request missing required format |
| INVALID_FORMAT | 422 | No | Unsupported conversion format |
| CLIENT_REFERENCE_CONFLICT | 400 | No | client_reference and external_invoice_id differ |
| INVALID_CLIENT_METADATA | 400 | No | client_reference, external_invoice_id, or source_system exceeds its length limit or contains control characters |
| INVALID_EMAIL_INPUT | 400 | No | email_input is sent more than once, exceeds 10,000 characters, is combined with a non-PDF source or use_embedded_xml=true, or is sent to invoices:convert-structured |
| INVALID_EMBEDDED_XML_POLICY | 400 | No | use_embedded_xml must be true or false |
| INVALID_SELLER_MASTER_DATA | 400 | No | use_seller_master_data or seller_master_data is not parseable or fails field validation; electronic_address and electronic_address_scheme must be supplied together or both omitted |
| METHOD_NOT_ALLOWED | 405 | No | Conversion paths accept POST only and task paths accept GET only; the response includes Allow: POST, OPTIONS (conversion) or Allow: GET, OPTIONS (task) |
| DOWNLOAD_FORMAT_REQUIRED | 400 | No | Task result request missing required download query |
| INVALID_DOWNLOAD_FORMAT | 400 | No | Task result download query must be xml or pdf |
| AUTH_SERVICE_UNAVAILABLE | 503 | Yes | Auth backend unavailable |
| RATE_LIMIT_SERVICE_UNAVAILABLE | 503 | Yes | The rate-limit service could not be reached; retry with backoff |
| PLAN_TIER_CHECK_FAILED | 503 | Yes | The service could not verify plan or API access; retry with backoff |
| API_CREDIT_SERVICE_UNAVAILABLE | 503 | Yes | Prepaid API credit or channel allowance verification is temporarily unavailable on conversion uploads |
| RATE_LIMITED | 429 | Yes | Respect Retry-After. Retry-After, X-RateLimit-Limit-Minute, and X-RateLimit-Limit-Hour are returned on 429 responses only; the body carries details.minute_count, details.hour_count, details.limit_minute, and details.limit_hour |
| BAD_REQUEST | 400 | No | Invalid JSON or invalid UUID path parameter |
| INVALID_QUERY_PARAMETER | 400 | No | include_validation_report_html must be true or false |
| PAYLOAD_TOO_LARGE | 413 | No | Over upload size limit |
| INVALID_UPLOAD | 400 | No | Upload read/parsing failure |
| UPLOAD_FAILED | 422 | No | An optional context field (jurisdiction, transaction_scope, delivery_channel) held an unrecognized value; the allowed values are in the message |
| INVALID_PROFILE | 422 | No | Unknown profile name; details.allowed_profiles lists the accepted values |
| TASK_NOT_READY | 202 | Yes | Poll again for async completion |
| TASK_NOT_FOUND | 404 | No | The task is unknown, not owned by the tenant, or past its 24-hour retention after reaching a terminal state |
| VALIDATION_FAILED | 422 | No | Blocking validation issues remain, including strict ZUGFeRD prerequisite failures and unresolved blocking_source_conflict items; correct invoice data before retrying |
| AUTHORITATIVE_VALIDATION_UNAVAILABLE | 503 | Yes | Authoritative validation, proof persistence, or hybrid-generation dependency unavailable; retry later |
| TASK_STATUS_FAILED | 4xx/5xx | Conditional | Retry if transient service condition |
| TASK_RESULT_FAILED | 4xx/5xx | Conditional | Retry if transient service condition |
| TASK_FAILED | 500 | Conditional | Conversion failure on the result endpoint. Read details.code and details.retryable: MULTIPLE_INVOICES_IN_DOCUMENT, NO_INVOICE_DETECTED, INSUFFICIENT_INVOICE_SIGNAL, SCHEMA_PARSE_FAILED, and ARTIFACT_PARITY_FAILED are terminal; PROVIDER_ERROR and any unrecognized details.code follow details.retryable, and details.retryable=true means start a new conversion with a new Idempotency-Key instead of re-polling the same task. The failed conversion does not consume a billing unit |
| MULTIPLE_INVOICES_IN_DOCUMENT | 500 (details code) | No | Terminal: the source contains more than one invoice. Split it into one file per invoice and start separate conversions |
| NO_INVOICE_DETECTED | 500 (details code) | No | Terminal: the document does not appear to be an invoice; send it for human handling |
| INSUFFICIENT_INVOICE_SIGNAL | 500 (details code) | No | Terminal: the source lacks enough invoice data; provide a better source or use structured conversion |
| SCHEMA_PARSE_FAILED | 500 (details code) | No | Terminal for this input: extracted data failed schema parsing. Start a new conversion; escalate if the same document fails again |
| PROVIDER_ERROR | 500 (details code) | Conditional | Extraction provider failed. Follow details.retryable; for provider_context_too_large, use a smaller source document |
| XML_GENERATION_FAILED | 500 | Yes | Transient XML generation failure or timeout |
| PDF_GENERATION_FAILED | 500 | Yes | Transient PDF generation failure or timeout |
| ARTIFACT_GENERATION_RERUN_REQUIRED | 503 | No | Strict artifact generation failed after server-side retries; start a new conversion after the dependency recovers |
| EXTRACTION_INCOMPLETE_GROUP_FAILURE | 503 | No | One or more extraction groups failed. The task cannot recover; start a new conversion and inspect details.failed_groups |
| ARTIFACT_GENERATION_FAILED | 503 (details code) | No | Recorded on failed tasks for retryable strict issuance failures; result downloads surface 503 ARTIFACT_GENERATION_RERUN_REQUIRED with this code in details |
| ARTIFACT_PARITY_FAILED | 500 (details code) | No | Reported in the details of 500 TASK_FAILED when the strict artifact does not match the final reviewed invoice data; escalate with the correlation ID |
| INTERNAL_ARTIFACT_INVARIANT_FAILED | 500 | No | Completed strict task has no safe stored artifact for the requested download; escalate with the correlation ID |
| PROFILE_MISMATCH | 422 | No | Requested profile does not match the CustomizationID of the stored result on result download |
| ZUGFERD_SOURCE_PDF_INCOMPATIBLE | 422 | No | Strict hybrid PDF generation cannot embed XML into the uploaded source PDF |
| ZUGFERD_SOURCE_PDF_REQUIRED | 422 | No | download=pdf for ZUGFERD requires a PDF source upload (DOCX/TXT sources cannot carry the hybrid PDF); request download=xml instead |
| VALIDATION_REPORT_NOT_FOUND | 404 | No | No validation report is bound to the current delivered artifact proof |
| VALIDATION_REPORT_FAILED | 4xx/5xx | Conditional | Validation report retrieval failed; retry only transient 5xx cases |
| OUTPUT_PROFILE_REQUIRED | 422 | No | A generic output contract requires an explicit profile when no unambiguous default can be determined |
| OUTPUT_PROFILE_CONFLICT | 422 | No | Profile contradicts the selected output format or explicit variant |
| PROXY_ERROR | 502/504 | Yes | Transport failure rather than a conversion outcome: proxy/upstream failure (504 for timeout). Retry with backoff and the same idempotency key |

## Common errors and what to do

- Retry with backoff: `429`, `502`, `504`, `503` with a retryable code, and transient `500`s that are not `TASK_FAILED` or `INTERNAL_ARTIFACT_INVARIANT_FAILED`. `500 TASK_FAILED` is retryable only when `details.retryable` is `true`, and then only as a new conversion.
- Do not retry: `400`, `401`, `402`, `403`, `404`, `405`, `413`, `422`, `409 IDEMPOTENCY_CONFLICT`, `409 IDEMPOTENCY_REPLAY_EXPIRED`, `500 TASK_FAILED` when `details.retryable` is not `true`, and `500 INTERNAL_ARTIFACT_INVARIANT_FAILED`.
- Fix request or source data: `400`, `413`, `422`.
- Fix access or credentials: `401 INVALID_API_KEY`. `403 API_NOT_ENABLED_FOR_TENANT` means the key is valid but External API access is not enabled for the account — contact support.
- Confirm the included monthly allowance or buy a prepaid API-credit package: `402 INSUFFICIENT_API_CREDITS`. Read whichever `details` keys are present (`remaining` for prepaid accounts, or `included_remaining`/`credit_remaining`/`shortfall` when an included allowance is enforced).
- Keep polling later: `202 TASK_NOT_READY`.
- For `500 TASK_FAILED`, read `details.code` and `details.retryable`. `MULTIPLE_INVOICES_IN_DOCUMENT`, `NO_INVOICE_DETECTED`, `INSUFFICIENT_INVOICE_SIGNAL`, `SCHEMA_PARSE_FAILED`, and `ARTIFACT_PARITY_FAILED` are terminal; `PROVIDER_ERROR` and any unrecognized code follow `details.retryable`, and `true` means start a NEW conversion instead of re-polling the same task. A failed conversion does not consume a billing unit.
- For `422 VALIDATION_FAILED`, show the returned field, rule ID, and remediation to a human reviewer before retrying with corrected invoice data.
- For `503 AUTHORITATIVE_VALIDATION_UNAVAILABLE`, fetch the same task result later; no unverified artifact was delivered. For `503 ARTIFACT_GENERATION_RERUN_REQUIRED` and `503 EXTRACTION_INCOMPLETE_GROUP_FAILURE`, start a new conversion instead.
- `502` and `504 PROXY_ERROR` are transport failures rather than conversion outcomes; retry with backoff and the same idempotency key.

## Rate & payload limits

Per-key rate limits and payload size constraints are enforced for all API calls. Rejected conversions do not consume prepaid API credits; rate limits are calculated separately per endpoint.

- Endpoint-aware limits are cost-weighted, and each endpoint has its own bucket so polling cannot starve conversion throughput. Defaults per API key: `POST /invoices:convert` and `POST /invoices:convert-structured` `30/min` and `500/hour`; `GET /tasks/{task_id}` `10/min` and `120/hour`; `GET /tasks/{task_id}/result` `10/min` and about `134/hour`; `GET /tasks/{task_id}/validation-report` `10/min` and `120/hour`.
- Quota headers are returned on `429 RATE_LIMITED` responses only. Successful responses do not carry quota headers, so treat the table above as the working contract and read exact effective values from a `429`.
- The status bucket is the binding constraint for polling: wait about `20 seconds` after the accepted `202` before the first status call, then back off (20s, 30s, 45s, 60s, and 60s from there on) and stop on `completed` or `failed`. Do not poll every 10 seconds; a single task polled that way burns its whole hourly budget in 20 minutes.
- Source document upload max size: `20 MB` for PDF, DOCX, or TXT files.
- Structured data upload max size: `2 MB` total across all `data_file` parts.
- JSON payload max size: `1 MB`
- `429` responses include `Retry-After`, `X-RateLimit-Limit-Minute`, and `X-RateLimit-Limit-Hour`, plus `details.minute_count`, `details.hour_count`, `details.limit_minute`, and `details.limit_hour`.

## Retry guidance

- Use exponential backoff with jitter, and reuse the same `Idempotency-Key` on every retry of a write request.
- Branch on the machine-readable `code` — and on `details.code` plus `details.retryable` for `500 TASK_FAILED` — never on the HTTP status alone. A `500` is not automatically retryable in this API.
- Safe to retry: `429`, `502`, `504`, `503` with a retryable code, transient `500`s that are NOT `TASK_FAILED` or `INTERNAL_ARTIFACT_INVARIANT_FAILED`, and `500 TASK_FAILED` when `details.retryable` is `true` (transient provider failures: rate limiting, timeout, transport error) — retry that case as a NEW conversion with a new `Idempotency-Key`, not by re-polling the same task.
- Never retry: `400`, `401`, `402`, `403`, `404`, `405`, `413`, `422`, `409 IDEMPOTENCY_CONFLICT`, `409 IDEMPOTENCY_REPLAY_EXPIRED`, `500 TASK_FAILED` when `details.retryable` is not `true`, and `500 INTERNAL_ARTIFACT_INVARIANT_FAILED`. A terminally failed conversion does not consume a billing unit.
- `409 IDEMPOTENCY_IN_PROGRESS` is retryable with the SAME key after a short delay; a stuck in-progress claim is reclaimed after 15 minutes.
- `503 ARTIFACT_GENERATION_RERUN_REQUIRED` and `503 EXTRACTION_INCOMPLETE_GROUP_FAILURE` need a NEW conversion rather than a retry of the same task.

## Task lifecycle & retention

- A task and its stored artifacts are retained for `24 hours` after the task reaches a terminal state (`completed` or `failed`), then purged. After purge, status, result, and validation-report requests return `404 TASK_NOT_FOUND`.
- There is no fixed conversion timeout. A task is failed after a `5 minute` stall window with no stage or progress update, or once total processing exceeds the absolute `15 minute` hard cap.
- Set your client-side timeout to about `16 minutes` from the accepted `202`. Most conversions finish in well under two minutes.
- Idempotency records live for `24 hours`, matching task retention. A request stuck in progress is reclaimed after `15 minutes`.
- Rate-limit counters reset on a rolling window.

## Support model

- Business-hours support on commercially reasonable efforts.
- No formal SLA, service credit, or response-time commitment unless agreed in an order form.

## Changelog

Recent externally visible API changes.

### 2026-09-08
seller_master_data now treats electronic_address and electronic_address_scheme as one optional pair. Supply both fields or omit both fields; an incomplete pair returns 400 INVALID_SELLER_MASTER_DATA.

### 2026-09-07
Pricing documentation correction: 1,000 prepaid credits cost EUR 400 (EUR 0.40 each). The 100-, 200-, and 500-credit packages remain EUR 50, EUR 100, and EUR 250. Runtime prices and existing purchases are unchanged.

### 2026-08-24
Document conversion now ignores embedded invoice XML by default. Set use_embedded_xml=true only when the integration explicitly trusts the embedded XML as the primary extraction source. Email Import always ignores embedded invoice XML. Changing use_embedded_xml changes the idempotency request hash; use a new Idempotency-Key when you change this option.

### 2026-08-20
When seller master data fills a field, leftover extractor flags on that field stay visible but no longer block strict Email Import or External API issuance. Buyer, lines, tax, delivery, due date, Skonto, remittance PaymentID, unfilled profile fields, and invalid profile values stay blocking.

### 2026-08-07
Enterprise became available by direct checkout for EUR 50/month or EUR 420/year. Enterprise includes 100 shared Email/API conversions per month; additional conversions use prepaid credits at EUR 0.40–0.50 each. API keys no longer require manual approval.

### 2026-07-29
Published the current error catalog and code-specific retry rules. A 500 is not automatically retryable; inspect code, details.code, and details.retryable. Documented both 402 INSUFFICIENT_API_CREDITS detail shapes and the different 409 idempotency recovery paths. Published validation-report proof headers, 24-hour retention, task timeouts, and endpoint-specific rate limits. Published the closed format/profile table and corrected download, METHOD_NOT_ALLOWED, and PROFILE_MISMATCH guidance.

### 2026-07-28
API conversion now rejects a source that is confirmed to contain several invoices with terminal MULTIPLE_INVOICES_IN_DOCUMENT. Split confirmed multi-invoice files. An uncertain multiple-invoice signal instead returns 422 VALIDATION_FAILED for review.

### 2026-07-26
Enabled seller master data now replaces matching extracted seller or payment values. Missing profile fields leave extracted values unchanged; differences remain non-blocking review warnings.

### 2026-07-25
Superseded by 2026-07-26: seller master data now replaces matching extracted values instead of filling only missing values.

### 2026-07-10
Documentation backfill; no runtime behavior change. The error catalog now documents the previously undocumented runtime error codes, including API_CREDIT_SERVICE_UNAVAILABLE, TASK_NOT_FOUND, INVALID_CLIENT_METADATA, INVALID_SELLER_MASTER_DATA, PROFILE_MISMATCH, ZUGFERD_SOURCE_PDF_REQUIRED, VALIDATION_REPORT_NOT_FOUND, VALIDATION_REPORT_FAILED, INVALID_QUERY_PARAMETER, and METHOD_NOT_ALLOWED. Clients that parse error responses by the machine-readable code field need no changes; clients that switch on a fixed list of codes should add the newly documented values. Corrected changelog dates: DOCX/TXT source support shipped on 2026-06-30, not 2026-07-06.

### 2026-07-06
Completed task payloads may include additive _processing_warnings and _validation_warnings entries with SOURCE_CONTEXT_* rule IDs when source evidence was unavailable, suspect, or truncated before extraction. Treat SOURCE_CONTEXT_* entries as review signals for customer-side exception handling; strict artifact downloads remain governed by validation proof and artifact checks.

### 2026-07-03
Strict ZUGFeRD prerequisite failures (missing mandatory fields for hybrid generation) now fail as 422 VALIDATION_FAILED with the blocking rule IDs instead of a retryable 503; route these to a data-correction flow, not a retry loop. For XML-only formats (XRECHNUNG, EN16931, UBL, CII), the PDF rendering is now a best-effort convenience artifact: download=xml stays authoritative on completed tasks while download=pdf can be unavailable if the rendering failed after XML issuance. Conversions with unresolved blocking source conflicts now fail as 422 VALIDATION_FAILED with blocking_source_conflict issue entries instead of issuing an artifact.

### 2026-06-30
POST /api/v1/invoices:convert now accepts PDF, DOCX, and TXT invoice source documents in the file field. Legacy DOC, RTF, image, and other unsupported source files are rejected before conversion starts. ZUGFeRD/Factur-X hybrid PDF downloads still require a PDF source upload; use XML downloads for DOCX/TXT source conversions. Added optional include_validation_report_html=true on GET /api/v1/tasks/{task_id} to inline the sanitized validation report HTML when available. Conversion uploads now accept optional use_seller_master_data and seller_master_data fields on both endpoints so approved tenants can opt into stored or request-scoped seller defaults.

### 2026-06-29
Added GET /api/v1/tasks/{task_id}/validation-report?download=html|xml to retrieve the validation report tied to the current strict result artifact proof. Validation report responses expose task ID, artifact SHA-256, validation proof ID, report proof ID, report content type, and correlation ID headers.

### 2026-06-10
Submitted invoice data is now the source of truth for hybrid ZUGFeRD output; deterministic cleanup and tax normalization still apply. Tasks fail on stalled progress or the 15-minute hard cap, not on a fixed five-minute processing timeout.

### 2026-06-09
Usage-tracking persistence failures no longer block a ready validated response or charge an extra credit; failed events are queued for reconciliation.

### 2026-06-02
External API access is now documented as approved gated access rather than ungated key creation. Clarified that no formal SLA, service credit, or contractual penalty applies unless agreed in an order form. format is now required on both conversion endpoints; missing values return 400 FORMAT_REQUIRED and unsupported values return 422 INVALID_FORMAT. download is now required on task-result requests; missing values return 400 DOWNLOAD_FORMAT_REQUIRED and unsupported values return 400 INVALID_DOWNLOAD_FORMAT. Conversion uploads now accept client_reference/external_invoice_id and source_system for customer-side reconciliation. Accepted conversion and task-status responses now include status_url, primary_result_format, primary_result_url, and supplied reconciliation fields.

### 2026-06-01
Structured conversion now accepts all public output formats: XRECHNUNG, ZUGFeRD, EN16931, UBL, and CII. Structured conversion now accepts repeatable data_file parts plus data_files and data_files[] aliases for split ERP exports. Structured multi-file bundles must describe exactly one invoice and fail fast on conflicting or missing bundle invoice IDs. Clarified that multiple invoice documents should be submitted as separate conversion tasks, each with its own idempotency key.

### 2026-05-27
Added POST /api/v1/invoices:convert-structured for carrier-PDF plus CSV/JSON/XML/XLSX/TXT structured-data conversion across supported output formats. Documented that structured data is the only semantic source on this endpoint; the PDF is used for hybrid embedding. Updated OpenAPI and Postman artifacts for structured conversion.

### 2026-05-26
Strict XML and hybrid PDF artifacts gained internal parity metadata; use result_artifacts for readiness and validation state. The documented production base URL changed to https://www.invoice-converter.com/api/v1.

### 2026-05-19
GET /api/v1/tasks/{task_id}/result became retrieval-only; it does not generate, repair, or validate files. Strict tasks complete only after a validated artifact is stored; missing current proof fails closed. External API conversion is fixed to strict issuance, with no draft or warning override. Task status gained result_artifacts diagnostics and documented delivery_channel values.

### 2026-05-08
Added prepaid External API credits for non-Enterprise tenants. Documented 402 INSUFFICIENT_API_CREDITS for approved tenants without Enterprise order-form billing or prepaid credits. Confirmed idempotent replays do not consume additional API credits. Clarified that External API V1 model routing is managed server-side while profile and delivery context stay caller-controlled.

### 2026-03-28
Task status gained XML/PDF artifact-readiness diagnostics. Strict result downloads return files only after server-side artifact checks pass.

### 2026-03-26
Successful result downloads gained server-side validation proof for the returned artifact. Missing validation or proof dependencies return 503 AUTHORITATIVE_VALIDATION_UNAVAILABLE. Cached downloads are reused only while their persisted validation proof remains current.

### 2026-03-06
Made task-result downloads format-faithful for CII and ZUGFERD outputs. Added cached result artifact reuse for repeated XML/PDF downloads of the same task. Aligned polling quotas with endpoint-scoped weighted rate-limit buckets.

### 2026-02-23
Added clearer, consistent API error responses across all endpoints. Expanded convert options and documented XML/PDF download behavior for task results. Improved retry safety with stricter idempotency requirements and validation. Updated OpenAPI/Postman artifacts to match current API behavior.

## Delivery Artifacts

Download machine-readable integration artifacts for the Developer API.

- [OpenAPI JSON](/developer-api/v1/openapi.json)
- [Postman collection](/developer-api/v1/postman.json)

## Use Postman and OpenAPI

- Import the Postman collection and set the `base_url`, `api_key`, and `idempotency_key` collection variables.
- Run the collection in order: convert, poll status, then fetch result.
- Use the OpenAPI JSON to generate typed clients, but keep file upload, polling, and binary result handling covered by integration tests.
- Record `X-Correlation-ID` in logs so support can trace requests end-to-end.

## Send Technical Feedback

Share implementation questions, risks, and required contract changes with our team.

- [Email technical feedback](mailto:contact@invoice-converter.com?subject=External%20API%20V1%20technical%20review%20feedback&body=Hello%20Invoice-Converter%20team%2C%0D%0A%0D%0AWe%20reviewed%20the%20External%20API%20V1%20documentation%20and%20have%20the%20following%20feedback%3A%0D%0A%0D%0A1)%20%0D%0A2)%20%0D%0A3)%20%0D%0A%0D%0ARegards%2C)
