Clearvo API Reference
Clearvo is a single REST API for submitting, tracking, and managing B2B e-invoices across Europe. One integration handles format generation, authority submission, clearance polling, and status webhooks for every supported country.
https://api.clearvo.io/v1 — All requests must be made over HTTPS. HTTP requests are rejected with 301 Moved Permanently.How it works
You send one JSON payload to POST /invoices. Clearvo transforms it into the correct country format (FatturaPA, FA(3), XRechnung, etc.), submits it to the relevant authority, and returns a referenceId. You then poll GET /invoices/status or receive a webhook when the clearance status changes.
Send a POST /invoices request with your invoice data. Receive a referenceId and initial clearanceStatus.
Call GET /invoices/status on a schedule, or configure a webhook to receive push notifications on every status transition.
A terminal status (ACCEPTED, REJECTED, DUPLICATE) ends the lifecycle. Non-terminal statuses require continued polling.
Authentication
Every request to the Clearvo API requires an API key passed in the x-api-key header. Keys are scoped to an organisation and environment (live vs. test).
| Header | Type | Description |
|---|---|---|
| x-api-key | string | Your API key. Required on every request. |
| x-idempotency-key | string (UUID) | Required on all POST requests. Replaying the same key within 24 h returns the cached response without re-submitting to the authority. Required on POST |
| x-taxually-entity-id | string | Optional. Multi-tenant entity attribution — used to scope invoice records to a sub-tenant in your platform. Optional |
ck_test_; live keys begin with ck_live_.x-api-key: ck_live_••••••••••••
x-idempotency-key: a3f9c2d1-e847-4b6a-9c12-d3f0e1a2b7c8
Content-Type: application/json
Quickstart
Submit your first invoice in under two minutes. This example sends an Italian B2B invoice to the SDI.
curl https://api.clearvo.io/v1/invoices \
-X POST \
-H "x-api-key: ck_live_••••••••••••" \
-H "x-idempotency-key: a3f9c2d1-e847-4b6a-9c12-d3f0e1a2b7c8" \
-H "Content-Type: application/json" \
-d '{
"documentType": "invoice",
"invoiceNumber": "INV-2024-001",
"issueDate": "2024-01-15",
"currency": "EUR",
"country": "IT",
"supplier": {
"name": "Acme SRL",
"taxId": "01234567890",
"taxIdCountry": "IT",
"address": { "street": "Via Roma 1", "city": "Milano", "postalCode": "20121", "country": "IT" }
},
"buyer": {
"name": "Cliente SpA",
"taxId": "09876543210",
"taxIdCountry": "IT",
"address": { "street": "Via Veneto 10", "city": "Roma", "postalCode": "00187", "country": "IT" }
},
"lines": [
{ "description": "Software licence Q1", "quantity": 1, "unitPrice": 1000.00, "taxCode": "S" },
{ "description": "Support hours", "quantity": 8, "unitPrice": 125.00, "taxCode": "S" }
]
}'
A successful response returns:
{
"ok": true,
"country": "IT",
"referenceId": "IT-20240115-INV-001",
"clearanceStatus": "PENDING",
"terminal": false,
"submittedAt": "2024-01-15T10:30:00Z",
"nextPollAfter": "2024-01-15T10:35:00Z"
}
Store the referenceId — you'll use it to poll for the final clearance decision.
POST /invoices
Submit a new invoice for clearance. Clearvo validates the payload, generates the country-specific XML format, and forwards it to the relevant tax authority.
POST https://api.clearvo.io/v1/invoicesNode.js example
const response = await fetch('https://api.clearvo.io/v1/invoices', {
method: 'POST',
headers: {
'x-api-key': process.env.CLEARVO_API_KEY,
'x-idempotency-key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
documentType: 'invoice',
invoiceNumber: 'INV-2024-001',
issueDate: '2024-01-15',
currency: 'EUR',
country: 'IT',
supplier: {
name: 'Acme SRL',
taxId: '01234567890',
taxIdCountry: 'IT',
address: { street: 'Via Roma 1', city: 'Milano', postalCode: '20121', country: 'IT' },
},
buyer: {
name: 'Cliente SpA',
taxId: '09876543210',
taxIdCountry: 'IT',
address: { street: 'Via Veneto 10', city: 'Roma', postalCode: '00187', country: 'IT' },
},
lines: [
{ description: 'Software licence Q1', quantity: 1, unitPrice: 1000.00, taxCode: 'S' },
{ description: 'Support hours', quantity: 8, unitPrice: 125.00, taxCode: 'S' },
],
}),
});
const invoice = await response.json();
console.log(invoice.referenceId); // "IT-20240115-INV-001"
Response fields
| Field | Type | Description |
|---|---|---|
| ok | boolean | Always true on 2xx. |
| country | string | ISO country code the invoice was routed to. |
| referenceId | string | Clearvo-assigned identifier for this invoice submission. Use with GET /invoices/status. |
| clearanceStatus | string | Initial status — always PENDING for async countries; ACCEPTED immediately for synchronous flows (DE B2B). |
| terminal | boolean | true when the status will not change again. Stop polling when true. |
| submittedAt | string (ISO 8601) | UTC timestamp of submission. |
| nextPollAfter | string (ISO 8601) | Earliest time to poll for an update. Respect this to avoid rate limiting. |
GET /invoices/status
Retrieve the current clearance status and full event history for a submitted invoice.
GET https://api.clearvo.io/v1/invoices/status?country=IT&id=<referenceId>Query parameters
| Parameter | Type | Description |
|---|---|---|
| country | string | ISO 3166-1 alpha-2 country code. Required |
| id | string | The referenceId returned by POST /invoices. Required |
Polling loop (JavaScript)
async function waitForClearance(referenceId, country) {
const MAX_POLLS = 48; // 4 hours at 5-min cadence
let polls = 0;
while (polls++ < MAX_POLLS) {
const res = await fetch(
`https://api.clearvo.io/v1/invoices/status?country=${country}&id=${referenceId}`,
{ headers: { 'x-api-key': process.env.CLEARVO_API_KEY } }
);
const data = await res.json();
if (data.terminal) {
return data; // ACCEPTED | REJECTED | DUPLICATE
}
// Honour nextPollAfter — don't hammer the API
const wait = new Date(data.nextPollAfter) - Date.now();
if (wait > 0) await new Promise(r => setTimeout(r, wait));
}
throw new Error('Max polls reached — check Italy UNDELIVERED / mcDeadline');
}
Status response fields
| Field | Type | Description |
|---|---|---|
| clearanceStatus | string | Current status. See clearance status values below. |
| terminal | boolean | Stop polling when true. |
| events | array | Ordered array of status transitions. Each entry has status, at (ISO 8601), and optional message. |
| clearedAt | string? | Timestamp of authority acceptance. Present only when ACCEPTED. |
| rejectedAt | string? | Timestamp of rejection. Present only when REJECTED. |
| error | object? | Authority rejection detail. Present only when REJECTED. See error object. |
| mcDeadline | string? | Italy only. The SDI 10-day delivery deadline in ISO 8601. Present only when UNDELIVERED. |
| nextPollAfter | string? | Earliest recommended next poll time. Absent when terminal. |
Clearance status values
| Status | Terminal? | Description |
|---|---|---|
| PENDING | No | Submitted to the authority; awaiting response. Continue polling. |
| ACCEPTED | Yes | Cleared by the authority. Invoice is legally valid. |
| REJECTED | Yes | Authority rejected the invoice. Check the error block for reason codes. |
| DUPLICATE | Yes | Authority already has this invoice number. No action needed if intentional. |
| UNDELIVERED | No | Italy (SDI) only. SDI cannot reach the recipient's inbox. SDI retries for 10 days. After that deadline the invoice may still be legally valid — see the Italy guide. |
Supported Countries
Clearvo supports the following countries. Each country maps to a specific authority and XML format. Pass the two-letter code in the country field of your request.
| Country | Authority | Format | Mandate |
|---|---|---|---|
| 🇮🇹 IT | SDI | FatturaPA 1.2 | Mandatory (all businesses) |
| 🇵🇱 PL | KSeF | FA(3) | Mandatory 2025 |
| 🇪🇸 ES | AEAT | VerifactuREC | 2025 |
| 🇵🇹 PT | AT | SAFT-PT | Mandatory |
| 🇫🇷 FR | DGFiP | Factur-X / UBL | 2026 (PDP) |
| 🇩🇪 DE | No central authority | XRechnung 3.0 / ZUGFeRD | B2G mandatory · B2B 2025 |
| 🇷🇴 RO | ANAF | e-Factura CIUS-RO | Mandatory |
| 🇭🇺 HU | NAV | Online Számla v3.0 | Mandatory |
Request body
All fields are sent as a flat JSON object to POST /invoices. Fields marked Required must be present; Optional fields are silently ignored if absent.
| Field | Type | Description |
|---|---|---|
| documentType | enum | "invoice" | "credit_note" | "debit_note". Required |
| invoiceNumber | string | Seller-assigned invoice number. Must be unique per supplier. Required |
| issueDate | string | Invoice issue date in YYYY-MM-DD. Required |
| taxPointDate | string | VAT point date in YYYY-MM-DD, if different from issue date. Optional |
| dueDate | string | Payment due date in YYYY-MM-DD. Optional |
| currency | string | ISO 4217 currency code: EUR, PLN, GBP, USD. Required |
| country | string | ISO 3166-1 alpha-2 destination country: IT, PL, ES, PT, FR, DE, RO, HU. Required |
| supplier | Party | The invoice issuer. Must match your registered VAT identity. Required |
| buyer | Party | The invoice recipient. Required |
| lines | LineItem[] | Array of line items. Minimum 1. Required |
| payment | Payment | Payment details. Optional |
| originalInvoiceRef | string | Reference to the original invoice number. Required for credit_note and debit_note. Optional |
| customerReference | string | Stored in Clearvo records only — not forwarded to the authority. Useful for correlating to your internal order IDs. Optional |
Party object
Used for both supplier and buyer.
| Field | Type | Description |
|---|---|---|
| name | string | Legal entity name. Required |
| taxId | string | VAT/tax registration number — no country prefix, no spaces. IT: 11-digit Partita IVA. PL: 10-digit NIP. DE: without the "DE" prefix. Optional |
| taxIdCountry | string | ISO country code the taxId is registered in. Required when taxId is provided. Optional |
| address.street | string | Street address line. Optional |
| address.city | string | City. Required |
| address.postalCode | string | Postal/ZIP code. Optional |
| address.country | string | ISO 3166-1 alpha-2 country of the address. Required |
{
"name": "Acme SRL",
"taxId": "01234567890",
"taxIdCountry": "IT",
"address": {
"street": "Via Roma 1",
"city": "Milano",
"postalCode": "20121",
"country": "IT"
}
}
Line items
Each entry in the lines array describes one invoiced item. Amounts are net (excluding VAT). Clearvo computes VAT amounts from the taxCode and the country's current rate table.
| Field | Type | Description |
|---|---|---|
| description | string | Item description. Appears on the XML invoice. Required |
| quantity | number | Quantity of units. Decimal precision up to 6 dp. Required |
| unitPrice | number | Net unit price excluding VAT, in the invoice currency. Required |
| taxCode | string | EN16931 VAT category code. See Tax codes section. Required |
| unit | string | UN/CEFACT unit of measure code. Defaults to "EA" (each). Common values: HUR (hour), DAY, KGM (kilogram). Optional |
Tax codes
Tax codes follow the EN16931 VAT category framework. Clearvo maps each code to the correct authority-specific code in the generated XML. Always use the code that describes the legal reason for the rate, not just the rate percentage.
| Code | Category | Rates (by country) |
|---|---|---|
| S | Standard rate | IT 22% · PL 23% · DE 19% · FR 20% · ES 21% |
| AA | Reduced rate | IT 10% · PL 8% · DE 7% |
| AB | Second reduced rate | IT 5% · PL 5% |
| AC | Super-reduced rate | IT 4% (essential goods) |
| AE | Reverse charge | Cross-border B2B services under Art. 196 VAT Directive. Buyer accounts for VAT. |
| K | Intra-EU zero rate | Goods dispatched to VAT-registered buyer in another EU member state (Art. 138). |
| G | Export / zero rate | Goods or services exported outside the EU. |
| E | Exempt | Legally exempt from VAT. Include exemptionReason in line item where required by country. |
| O | Out of scope | Outside the scope of VAT entirely (e.g. statutory transfers). |
| Z | Zero rated (domestic) | Domestic zero-rated supplies (distinct from intra-EU zero rate). |
AE (reverse charge) requires both parties to have a valid taxId. Clearvo validates this and returns HTTP 422 if the buyer taxId is missing on cross-border reverse-charge invoices.Payment object
Optional payment details embedded in the invoice. Included in the generated XML where the authority format supports it (FatturaPA, UBL, XRechnung).
| Field | Type | Description |
|---|---|---|
| iban | string | IBAN of the payee account. No spaces. Optional |
| bic | string | BIC/SWIFT code of the payee bank. Optional |
| paymentMeans | enum | "SEPA" | "CREDIT_TRANSFER" | "CASH". Defaults to "CREDIT_TRANSFER". Optional |
| paymentReference | string | Structured creditor reference (e.g. RF-reference or invoice number). Optional |
Country-specific fields
Some countries require additional fields that have no EN16931 equivalent. These are passed as a countryData object alongside the standard payload.
Italy
| Field | Type | Description |
|---|---|---|
| countryData.it.codiceFiscale | string | Buyer's Italian fiscal code (for individuals). Optional |
| countryData.it.codiceDestinatario | string | SDI recipient code (7-char). Provide if known; Clearvo looks it up via SMP if omitted. Optional |
| countryData.it.pecDestinatario | string | Buyer PEC email address (fallback if no codice). Optional |
Germany
| Field | Type | Description |
|---|---|---|
| countryData.de.buyerReference | string | Leitweg-ID (required for all public-sector B2G invoices). Optional for B2B |
Poland
| Field | Type | Description |
|---|---|---|
| countryData.pl.kSeFToken | string | Clearvo handles KSeF token acquisition automatically. You do not need to supply this. Managed by Clearvo |
HTTP errors
Clearvo uses standard HTTP status codes. All error responses include a JSON body with ok: false.
| Status | Meaning | Common cause |
|---|---|---|
| 401 | Unauthorized | Missing or invalid x-api-key. Check the header name (lowercase) and that the key is active. |
| 400 | Bad Request | Payload validation failed. The response includes field and message for each violation. |
| 409 | Conflict | Idempotency conflict — a different payload was submitted with the same x-idempotency-key. Generate a new key. |
| 422 | Unprocessable Entity | Country-specific validation failure (e.g. missing buyer taxId for reverse charge, invalid Italian Partita IVA check digit). |
| 500 | Internal Server Error | Unexpected Clearvo error. The response includes a requestId — include this when contacting support. |
Clearance errors
These are not HTTP errors — the initial POST /invoices returns 200, but a subsequent status poll may reveal that the authority rejected the invoice. Check clearanceStatus === "REJECTED" and inspect the error block.
Common authority rejection reasons by country:
Italy (SDI)
- 00001 — Invalid file structure (XML schema violation).
- 00002 — Duplicate invoice number from the same sender.
- 00102 — Supplier Partita IVA not found in Revenue Agency registry.
- 00201 — Buyer Codice Destinatario or PEC not found or inactive.
Poland (KSeF)
- InvalidNip — NIP number checksum invalid.
- DuplicateInvoice — Invoice FA number already submitted in this session.
- SchemaValidationError — FA(3) schema violation; check
error.detailfor XPath.
Germany (XRechnung)
Germany has no central clearance authority for B2B. Clearance is immediate on valid XML. Peppol-based rejections from the recipient's Access Point appear as REJECTED with error.source: "PEPPOL_AP".
Error object
All error responses — both HTTP errors and authority rejections — return a consistent error envelope.
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request body validation failed",
"violations": [
{ "field": "lines[0].taxCode", "message": "must be a valid EN16931 tax code" }
]
}
}
{
"clearanceStatus": "REJECTED",
"terminal": true,
"rejectedAt": "2024-01-15T10:47:12Z",
"error": {
"source": "SDI",
"code": "00102",
"message": "Partita IVA fornitore non trovata",
"detail": "Supplier tax ID 01234567890 is not registered with the Italian Revenue Agency"
}
}
{
"ok": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"requestId": "req_01J9XKZ4MFBVQH7PRTD8CNWY5"
}
}
Idempotency
Every POST /invoices request must include an x-idempotency-key header containing a UUID v4. This protects against duplicate submissions caused by network timeouts or retries.
How it works
- On the first request with a given key, Clearvo processes the invoice and caches the response.
- On any subsequent request with the same key and identical payload, the cached response is returned immediately — the invoice is not re-submitted to the authority.
- If you submit a different payload with the same key, Clearvo returns HTTP 409 Conflict. Generate a new UUID.
- Keys expire after 24 hours on Starter, 72 hours on Growth, and configurable on Enterprise.
Best practice
Use crypto.randomUUID() (Node 19+) or a UUID library. Store it alongside your pending invoice record so you can replay it on retry.
If the request times out or you receive a 5xx, retry with the same idempotency key. You will get the cached 200 response if Clearvo already processed the invoice.
Each unique invoice (different invoiceNumber or any field change) must use a fresh UUID.
async function submitWithRetry(payload, idempotencyKey) {
const MAX_RETRIES = 3;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const res = await fetch('https://api.clearvo.io/v1/invoices', {
method: 'POST',
headers: {
'x-api-key': process.env.CLEARVO_API_KEY,
'x-idempotency-key': idempotencyKey, // same key on every retry
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (res.status === 409) throw new Error('Key reused with different payload');
if (res.ok) return res.json();
if (res.status < 500) throw new Error(`Client error: ${res.status}`);
} catch (err) {
if (attempt === MAX_RETRIES - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
}
Italy — SDI lifecycle
Italy's Sistema di Interscambio (SDI) is an asynchronous clearance system. Invoices are accepted or rejected typically within minutes, but the SDI can take up to 5 days for a final decision in exceptional cases.
Status lifecycle
UNDELIVERED and mcDeadline
The UNDELIVERED status means the SDI successfully received your invoice but could not deliver it to the buyer's inbox (invalid Codice Destinatario, full mailbox, etc.). The SDI continues retrying for 10 days. This window is exposed as mcDeadline in the status response.
After the 10-day window expires:
- If the buyer eventually received it — the invoice is legally valid.
- If it was truly undeliverable — the invoice is still legally valid under Italian law (D.Lgs. 127/2015), but you should notify the buyer by other means and keep proof of the SDI notification.
UNDELIVERED invoice unless you are certain the original was never received. Duplicate invoice numbers are rejected by the SDI.Polling cadence
Poll every 5 minutes while PENDING. Clearvo returns a nextPollAfter timestamp — always honour this rather than hardcoding intervals. Polling more frequently will not speed up SDI processing and may trigger rate limiting.
Poland — KSeF
KSeF (Krajowy System e-Faktur) is Poland's national e-invoicing platform. All B2B invoices between VAT-registered Polish entities will be mandatory through KSeF from 2025.
Token-based authentication — managed by Clearvo
KSeF uses a session-token system: before submitting invoices, a session must be opened using the supplier's qualified electronic seal. Clearvo manages this entirely — you do not need to handle KSeF tokens, session lifetimes, or cryptographic signing. Simply submit your invoice payload and Clearvo handles the rest.
Status lifecycle
On acceptance, KSeF assigns a unique KSeF invoice number (kSeFNumber) which appears in the events array and can be used as a legal reference.
Polling cadence
KSeF processing is typically faster than Italian SDI — decisions usually arrive within 30 seconds. Poll every 60 seconds while PENDING. As always, honour the nextPollAfter field.
Germany — XRechnung
Germany currently has no central B2B invoice clearance authority. Clearvo generates a valid XRechnung 3.0 document (or ZUGFeRD 2.3 hybrid PDF for B2B), delivers it via Peppol where the buyer has a Peppol ID, and returns ACCEPTED immediately on successful XML validation.
No asynchronous clearance
Unlike Italy or Poland, German B2B invoices do not go through a tax authority clearance step. Clearvo validates your payload against the XRechnung 3.0 Schematron rules, generates the XML, and marks the invoice ACCEPTED as soon as the document passes validation. The terminal flag is true on the initial response — no polling required.
BuyerReference (Leitweg-ID)
For public sector (B2G) invoices, the countryData.de.buyerReference field containing the Leitweg-ID is mandatory. The Leitweg-ID is assigned by the contracting authority and identifies the recipient routing within German public administration (Xrechnung EN16931 BT-10).
For B2B invoices, buyerReference is optional but recommended when the buyer requests it for internal purchase-order matching.
Peppol delivery
If the buyer has a registered Peppol ID (e.g. 0088:4047035000007), Clearvo automatically routes the XRechnung via the Peppol network. If the buyer is not on Peppol, the invoice is made available for download from the Clearvo portal and you are notified via webhook. A rejection from the buyer's Access Point is surfaced as REJECTED with error.source: "PEPPOL_AP".
events[].peppolDelivered to confirm network delivery.Webhooks
GET /invoices/status polling approach with the nextPollAfter hint.
In the meantime, contact us to be notified when webhook support is released.