Recommended flow

How to put issuing and sending an invoice together with the Agent API: create, optionally check with one call, send, and track the status.

Order of calls

StepCallNote
1. CreatePOST /v1/agent/invoicesStore the invoice id.
2. Preflight (optional)POST /v1/agent/invoices/{id}/preflightKeep it out of the end-user click path.
3. SendPOST /v1/agent/peppol/send/{id}Answers 202, sending is asynchronous.
4. Statuswebhook peppol.document.* or a status GETA webhook is better than polling.

Preflight is a convenience. The send validates the document again, so you do not have to call preflight before every send.

Invoice preflight

POST /v1/agent/invoices/{id}/preflight (scope invoice:read) validates the exact document that will be sent (attachments and Peppol IDs included) and checks the recipient derived from the buyer DIČ. It never writes and never charges.

{
  "data": {
    "send_ready": true,
    "validator_unavailable": false,
    "validation": { "ran": true, "valid": true, "error_count": 0, "warning_count": 1 },
    "repair": [
      { "field": "…", "code": "…", "message": "…", "severity": "warning" }
    ],
    "recipient": { "peppol_id": "0245:2010101010", "found": true },
    "assembly": { "attachments_bytes": 20480 }
  }
}
SituationResult
Buyer without DIČ / IČ DPH200 with send_ready: false and a repair item with code RECEIVER_UNRESOLVED.
The send would be refused (organisation on the other Peppol network than the key, inactive Peppol account)200 with send_ready: false and a repair item SENDER_WORLD_MISMATCH or PEPPOL_ACCOUNT_INACTIVE.
Attachments over 10 MB422

XML preview before sending

Before sending, GET /v1/agent/invoices/{id}/xml returns exactly the document that will be sent, attachments and the sender and receiver Peppol IDs included (header X-Efaktura-Ubl-Source: preview). If the buyer data is incomplete, the preview is still returned, just without the receiver EndpointID. An attachment problem returns 422.

Exception: a foreign-currency invoice without a stored exchange rate is previewed with the cached ECB rate (or without BT-111 if none is cached), and the send may use a newer rate.

Server-Timing

Every /v1/agent/* response carries the header Server-Timing: total;dur=<ms>. The two preflight endpoints add validate and recipient, and the invoice preflight also assemble (all in ms). The header is exposed to browsers via CORS.

Timeouts and retries

Set a client timeout of 10 s. Retry only with the same Idempotency-Key. On a non-2xx response, read our JSON error body.

<?php
$ch = curl_init('https://api.efaktura.sk/v1/agent/invoices/' . $id . '/preflight');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: ' . $apiKey,
        'X-Organization-Id: ' . $orgId,
        'Content-Type: application/json',
    ],
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($raw === false) {
    // network error or timeout: retry with the same Idempotency-Key
}
$body = json_decode($raw, true); // 2xx and error responses are both JSON
if ($status >= 200 && $status < 300) {
    $sendReady = $body['data']['send_ready'];
} else {
    // $body holds our error description
}
On a non-2xx response, file_get_contents without 'ignore_errors' => true returns false and hides our error body. Use cURL.