Getting Started

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.

Base URL: 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.

1
Submit

Send a POST /invoices request with your invoice data. Receive a referenceId and initial clearanceStatus.

2
Poll or listen

Call GET /invoices/status on a schedule, or configure a webhook to receive push notifications on every status transition.

3
Handle the outcome

A terminal status (ACCEPTED, REJECTED, DUPLICATE) ends the lifecycle. Non-terminal statuses require continued polling.

Getting Started

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
Never expose your API key in client-side code or public repositories. Use environment variables. Test keys begin with ck_test_; live keys begin with ck_live_.
HTTP
x-api-key: ck_live_••••••••••••
x-idempotency-key: a3f9c2d1-e847-4b6a-9c12-d3f0e1a2b7c8
Content-Type: application/json
Getting Started

Quickstart

Submit your first invoice in under two minutes. This example sends an Italian B2B invoice to the SDI.

cURL
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:

JSON Response
{
  "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.

Core API

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.

Endpoint: POST https://api.clearvo.io/v1/invoices

Node.js example

JavaScript
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

FieldTypeDescription
okbooleanAlways true on 2xx.
countrystringISO country code the invoice was routed to.
referenceIdstringClearvo-assigned identifier for this invoice submission. Use with GET /invoices/status.
clearanceStatusstringInitial status — always PENDING for async countries; ACCEPTED immediately for synchronous flows (DE B2B).
terminalbooleantrue when the status will not change again. Stop polling when true.
submittedAtstring (ISO 8601)UTC timestamp of submission.
nextPollAfterstring (ISO 8601)Earliest time to poll for an update. Respect this to avoid rate limiting.
Core API

GET /invoices/status

Retrieve the current clearance status and full event history for a submitted invoice.

Endpoint: GET https://api.clearvo.io/v1/invoices/status?country=IT&id=<referenceId>

Query parameters

ParameterTypeDescription
countrystringISO 3166-1 alpha-2 country code. Required
idstringThe referenceId returned by POST /invoices. Required

Polling loop (JavaScript)

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

FieldTypeDescription
clearanceStatusstringCurrent status. See clearance status values below.
terminalbooleanStop polling when true.
eventsarrayOrdered array of status transitions. Each entry has status, at (ISO 8601), and optional message.
clearedAtstring?Timestamp of authority acceptance. Present only when ACCEPTED.
rejectedAtstring?Timestamp of rejection. Present only when REJECTED.
errorobject?Authority rejection detail. Present only when REJECTED. See error object.
mcDeadlinestring?Italy only. The SDI 10-day delivery deadline in ISO 8601. Present only when UNDELIVERED.
nextPollAfterstring?Earliest recommended next poll time. Absent when terminal.

Clearance status values

StatusTerminal?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.
Core API

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
Additional countries are added quarterly. Enterprise customers can request priority support for a specific country via support@clearvo.io.
Invoice Schema

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.

FieldTypeDescription
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
Invoice Schema

Party object

Used for both supplier and buyer.

FieldTypeDescription
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
JSON — Party example
{
  "name": "Acme SRL",
  "taxId": "01234567890",
  "taxIdCountry": "IT",
  "address": {
    "street": "Via Roma 1",
    "city": "Milano",
    "postalCode": "20121",
    "country": "IT"
  }
}
Invoice Schema

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.

FieldTypeDescription
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
Invoice Schema

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.

CodeCategoryRates (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).
Using 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.
Invoice Schema

Payment object

Optional payment details embedded in the invoice. Included in the generated XML where the authority format supports it (FatturaPA, UBL, XRechnung).

FieldTypeDescription
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
Invoice Schema

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

FieldTypeDescription
countryData.it.codiceFiscalestringBuyer's Italian fiscal code (for individuals). Optional
countryData.it.codiceDestinatariostringSDI recipient code (7-char). Provide if known; Clearvo looks it up via SMP if omitted. Optional
countryData.it.pecDestinatariostringBuyer PEC email address (fallback if no codice). Optional

Germany

FieldTypeDescription
countryData.de.buyerReferencestringLeitweg-ID (required for all public-sector B2G invoices). Optional for B2B

Poland

FieldTypeDescription
countryData.pl.kSeFTokenstringClearvo handles KSeF token acquisition automatically. You do not need to supply this. Managed by Clearvo
Error Reference

HTTP errors

Clearvo uses standard HTTP status codes. All error responses include a JSON body with ok: false.

StatusMeaningCommon 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.
Error Reference

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.detail for 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 Reference

Error object

All error responses — both HTTP errors and authority rejections — return a consistent error envelope.

JSON — HTTP error (400)
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body validation failed",
    "violations": [
      { "field": "lines[0].taxCode", "message": "must be a valid EN16931 tax code" }
    ]
  }
}
JSON — Clearance rejection (from status poll)
{
  "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"
  }
}
JSON — Internal error (500)
{
  "ok": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred",
    "requestId": "req_01J9XKZ4MFBVQH7PRTD8CNWY5"
  }
}
Guides

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

1
Generate the key before the request

Use crypto.randomUUID() (Node 19+) or a UUID library. Store it alongside your pending invoice record so you can replay it on retry.

2
Retry on network failure

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.

3
Never reuse a key for a different invoice

Each unique invoice (different invoiceNumber or any field change) must use a fresh UUID.

JavaScript — Idempotent submit with retry
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)));
    }
  }
}
Guides

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

PENDING
ACCEPTED
PENDING
REJECTED
PENDING
UNDELIVERED
ACCEPTED (after 10-day window)

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.
Do not void and reissue an 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.

Guides

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

PENDING
ACCEPTED
PENDING
REJECTED

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.

Both supplier and buyer NIP numbers are validated against a Luhn-style checksum before submission. An invalid NIP returns HTTP 422 immediately — you do not need to wait for KSeF.
Guides

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

From 2025, all German businesses must be able to receive XRechnung invoices. Peppol coverage among large buyers is growing rapidly — check events[].peppolDelivered to confirm network delivery.
Guides

Webhooks

Coming soon — Q3 2026 Webhook push notifications are on our roadmap. When released, you will be able to register an endpoint URL and receive a signed JSON payload for every clearance status transition — eliminating the need to poll. Until then, use the GET /invoices/status polling approach with the nextPollAfter hint.

In the meantime, contact us to be notified when webhook support is released.