# External API V1 Documentation

Technical documentation package for approved closed-beta partner IT review and feedback. This page summarizes the externally exposed API contract and access constraints.

## Overview

Technical documentation package for approved closed-beta partner IT review and feedback. This page summarizes the externally exposed API contract and access constraints.

The API accepts multipart uploads, returns JSON responses, and uses standard HTTP status codes and Bearer authentication. Submit a PDF invoice, wait for asynchronous processing, and then fetch the generated result.

Send a PDF invoice to a single endpoint and Invoice-Converter handles extraction, validation, and output generation. The result endpoint returns the generated file only after validation gates pass; otherwise it returns 422 VALIDATION_FAILED.

> **Status: closed beta**: Base path: /api/v1. Last synchronized 2026-05-14.

## Key capabilities

- Single endpoint for PDF-to-XML conversion
- AI-powered invoice data extraction
- Automated EN 16931 and KoSIT validation
- XRechnung, ZUGFeRD, UBL, and CII output formats
- Async processing with polling - small invoices around 30 seconds, larger invoices up to 1-2 minutes
- Idempotent writes for safe retries

## 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)
- [API Playground](/developer-api/playground)

## Quickstart

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

### POST /api/v1/invoices:convert (In development)
Convert PDF to structured e-invoice

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

### GET /api/v1/tasks/{task_id}/result (In development)
Download conversion result

## Request closed beta access

- API access is limited to approved closed-beta partners.
- Request access by email with company name, use case, target integration date, expected monthly volume, and required formats.
- The team confirms fit, provisions test credentials, and shares onboarding support contacts.
- Live credentials are issued after the test workflow and commercial approval are complete.

## Base URL and environments

- Production base URL: `https://invoice-converter.com/api/v1`.
- Test and live keys use the same host; key prefixes (`icp_test_*`, `icp_live_*`) identify the environment.
- Use `icp_test_*` keys for onboarding and validation runs before switching to live keys.
- 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 receiving closed-beta credentials.

- Upload: `POST /api/v1/invoices:convert` with `Authorization`, `Idempotency-Key`, `file=@invoice.pdf`, and `format=XRECHNUNG`.
- Poll every `2-5 seconds`: `GET /api/v1/tasks/{task_id}` until status is `completed` or `failed`.
- Download: `GET /api/v1/tasks/{task_id}/result?download=xml` and store `X-Correlation-ID` plus `X-Validation-Proof-Id` when present.
- For ZUGFeRD PDF output, request `format=ZUGFERD` plus `profile=ZUGFERD_FACTURX_EN16931` on convert and `download=pdf` on result.

## Common payload examples

- `XRECHNUNG`: send `format=XRECHNUNG`; this is the default when format is omitted.
- `ZUGFERD`: send `format=ZUGFERD` plus `profile=ZUGFERD_FACTURX_EN16931`; use `download=pdf` on result for the hybrid PDF/A-3 output.
- `UBL`: send `format=UBL` plus an explicit `profile` such as `PEPPOL`, `XRECHNUNG`, or `EN16931`.
- `CII`: send `format=CII` plus an explicit `profile` such as `EN16931`.

## Required request headers

- Authorization: Bearer <api_key>

## Auth rules

API access is currently in closed beta. Approved partners use an API key as a Bearer token; keys are provisioned during onboarding.

- Keys are tenant-scoped and environment-scoped (`live` / `test`). Key prefix determines environment.
- Keys are issued to approved closed-beta partners during onboarding; contact support for provisioning or rotation.
- 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`.
- Idempotency keys expire after `24 hours`.

## 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 (In development)
Upload a PDF invoice and start asynchronous conversion. Returns a task_id for polling. Request: multipart/form-data. Response: 202 Accepted.

### GET /api/v1/tasks/{task_id} (In development)
Poll the current status of a conversion task. Returns pending, processing, completed, or failed. When failed, the response includes an error field with the failure reason. Request: none (GET). Response: 200 OK.

### GET /api/v1/tasks/{task_id}/result (In development)
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. Repeated downloads may be served from cached generated artifacts. If blocking validation issues remain, this endpoint returns 422 VALIDATION_FAILED and no file body. Request: none (GET). 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.4 / Factur-X 1.08 | 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 |

## Changelog

Recent externally visible API changes.

### 2026-05-08
Added prepaid External API credits for non-Enterprise tenants. Documented 402 INSUFFICIENT_API_CREDITS for tenants without Enterprise billing or prepaid credits. Confirmed idempotent replays do not consume additional API credits. Documented gpt-5-nano as an extraction_preference option for low-cost sandbox tests.

### 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.

### 2026-02-20
Simplified status responses to status-focused output. Strengthened IndexNow submission validation and authentication checks. Hardened API access controls and request-limiting behavior in production.

## 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 |
| INSUFFICIENT_API_CREDITS | 402 | No | Buy at least 10 prepaid API credits or use Enterprise billing |
| IDEMPOTENCY_KEY_REQUIRED | 400 | No | Write endpoint called without Idempotency-Key |
| INVALID_IDEMPOTENCY_KEY | 400 | No | Idempotency key has invalid format |
| IDEMPOTENCY_CONFLICT | 409 | No | Same key used with different request hash |
| IDEMPOTENCY_IN_PROGRESS | 409 | Yes | Safe to retry later with same key/payload |
| AUTH_SERVICE_UNAVAILABLE | 503 | Yes | Auth backend unavailable |
| RATE_LIMIT_SERVICE_UNAVAILABLE | 503 | Yes | Rate-limit backend unavailable |
| RATE_LIMITED | 429 | Yes | Respect Retry-After and quota headers |
| BAD_REQUEST | 400 | No | Invalid JSON or invalid UUID path parameter |
| PAYLOAD_TOO_LARGE | 413 | No | Over upload size limit |
| INVALID_UPLOAD | 400 | No | Upload read/parsing failure |
| UPLOAD_FAILED | 4xx/5xx | Conditional | Fix invalid request options; retry only for transient 5xx cases |
| TASK_NOT_READY | 202 | Yes | Poll again for async completion |
| TASK_STATUS_FAILED | 4xx/5xx | Conditional | Retry if transient service condition |
| TASK_RESULT_FAILED | 4xx/5xx | Conditional | Retry if transient service condition |
| XML_GENERATION_FAILED | 500 | Yes | Transient XML generation failure or timeout |
| PDF_GENERATION_FAILED | 500 | Yes | Transient PDF generation failure or timeout |
| OUTPUT_PROFILE_REQUIRED | 422 | No | Explicit profile missing for ZUGFERD, UBL, or CII output |
| OUTPUT_PROFILE_CONFLICT | 422 | No | Profile contradicts the selected output format or explicit variant |
| PROXY_ERROR | 502/504 | Yes | Temporary connectivity failure (504 for timeout) |

## Common errors and what to do

- Retry with backoff: `429`, `500`, `502`, `503`, `504`.
- Fix request or source data: `400`, `409`, `413`, `422`.
- Fix access or credentials: `401`, `403`.
- Keep polling later: `202 TASK_NOT_READY`.
- For `422 VALIDATION_FAILED`, show the returned field, rule ID, and remediation to a human reviewer before retrying with corrected invoice data.

## Rate & payload limits

Per-key rate limits and payload size constraints are enforced for all API calls. Denied requests do not consume quota.

- Endpoint-aware quotas are cost-weighted. Convert uses the baseline plan quota (default `30/min` and `500/hour`), while polling endpoints use lower weighted quotas.
- Read effective limits from `X-RateLimit-Limit-Minute` and `X-RateLimit-Limit-Hour` on responses.
- PDF upload max size: `20 MB`
- JSON payload max size: `1 MB`
- Rate-limit responses include `Retry-After`, `X-RateLimit-Limit-Minute`, and `X-RateLimit-Limit-Hour`.

## Retry guidance

- Use exponential backoff with jitter.
- Retry only transient classes (`429`, `500`, `502`, `503`, `504`) using the same payload and idempotency key where possible.
- Do not blindly retry validation or contract errors (`400`, `401`, `403`, `409`, `413`).

## Task lifecycle & retention

- Completed and failed tasks remain available for `10 minutes` after reaching terminal state.
- Processing times out after `5 minutes` — stuck tasks are automatically marked `failed`.
- Idempotency keys expire after `24 hours`.
- Rate-limit counters reset on a rolling window.

## Support model

- Business-hours support.
- Target first response: 1 business day.

## 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)%2520%0D%0A2)%2520%0D%0A3)%2520%0D%0A%0D%0ARegards%2C)

## Use Postman and OpenAPI

- Import the Postman collection and set `baseUrl`, `apiKey`, and a new `idempotencyKey` variable.
- 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.
