Invoices
Issue an invoice via POST /v1/agent/invoices. The seller is derived from the organization (X-Organization-Id), the buyer from the customer field or an existing customer_id. Numbering, VAT calculation, PDF, and QR code generation all happen server-side, the same as for manual issuance.
Endpoint
| Method | Path | Scope | Headers |
|---|---|---|---|
| POST | /v1/agent/invoices | invoice:create | X-API-Key, X-Organization-Id |
This endpoint has a per-org rate limit of 30 requests / minute (on top of the key's global limit). The invoice currency is always EUR.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
type | invoice | self_invoice | final_invoice | credit_note | debit_note | No | Document type, defaults to invoice. Each type has its own number series. self_invoice goes over Peppol under the BIS Self-Billing 3.0 profile; credit_note = credit note, debit_note = debit note, final_invoice = final settlement invoice. |
customer_id | UUID | Conditionally | ID of an existing customer. Either customer_id OR customer is required. |
customer | object | Conditionally | Inline customer (creates a contact). See the table below. |
items | array of objects | Yes | At least 1 item. See the items table below. |
vehicle_id | UUID | No | ID of an existing vehicle (fleet module). |
vehicle_plate | string (≤20) | No | Vehicle license plate: the vehicle is looked up (normalized to uppercase without spaces). |
payment_method | bank_transfer | cash | card | offset | No | Defaults to bank_transfer (mapped internally to "bank"). offset = mutual set-off. |
due_days | int 0–365 | No | Payment term in days from the issue date. Defaults to 14. Note: if the organization has defaultDueDays configured, that's used instead. |
note | string (≤2000) | No | Note on the invoice (visible to the customer). |
internal_note | string (≤2000) | No | Internal note (not on the PDF). |
issue_date | YYYY-MM-DD | No | Issue date. Defaults to today. |
delivery_date | YYYY-MM-DD | No | Delivery date. Defaults to issue_date. |
order_number | string (≤100) | No | Order number (orderReference). |
already_paid | boolean | No | Defaults to false. When true, the invoice is issued with status "paid" and full payment as of the issue date. |
attachments | array (≤10) | No | Attachments (PDF/JPG/PNG, base64). When sent via Peppol they're embedded directly in the document → they reach the recipient even on other systems (e.g. Doklado). |
{ filename, mime_code, base64_content } (optionally description). Allowed types: PDF, JPG, PNG; 10 MB total max (Peppol limit). An attachment can also be added later to an existing invoice via POST /v1/agent/invoices/{id}/attachments and managed via GET / DELETE on the same path.Customer object
Maps to the contacts table. For an inline customer, a new contact of type customer is created.
| Field | Type | Required | Description |
|---|---|---|---|
name | string 1–300 | Yes | Company name or customer name. |
ico | string (8 digits) | No | IČO: exactly 8 digits. |
dic | string | No | DIČ: 10 digits (SK) or a country code + 8–10 digits (e.g. CZ12345678). Normalized to uppercase without spaces. |
ic_dph | string (SK + 10 digits) | No | VAT ID in the format SK2120035951. |
address | string | object | No | Either a plain string, or an object { street, city, postalCode, country }. For Peppol, a full address object is required. |
email | email (≤254) | No | An empty string is treated as not provided. |
phone | string E.164 (≤16) | No | E.164 format, e.g. +421900123456. Separators and the 00 prefix are normalized (0042... → +42...). An invalid number returns 400 VALIDATION_ERROR. An empty string is treated as not provided. |
address as an object requires non-empty street, city, postalCode, and a 2-letter country. If you send the address as a plain string, the string is stored in the street field and country is set to "SK"; city and postalCode remain empty: insufficient for Peppol (see below). For Peppol sending, always send a full address object.Line item object (items[])
| Field | Type | Required | Description |
|---|---|---|---|
description | string 1–500 | Yes | Item name / description. |
quantity | number > 0 | Yes | Quantity (a numeric string is also accepted). |
unit | enum | No | Defaults to ks. Allowed: ks, hod, l, m, km, súbor. |
unit_price | number ≥ 0 | Yes | Unit price excluding VAT. |
vat_rate | int | Yes | VAT rate in percent. Allowed: 0, 5, 19, 23. |
ic_dph nor isVatPayer), no item may have vat_rate> 0; otherwise the request fails with VALIDATION_ERROR("Not a VAT payer"). The invoice gets the exemption "Supplier is not a VAT payer".Complete example
POST /v1/agent/invoices
X-API-Key: efk_pk_test_...
X-Organization-Id: 0a2c1f3e-…
Idempotency-Key: 7f0c2a4e-order-2026-042
Content-Type: application/json
{
"customer": {
"name": "Customer s.r.o.",
"ico": "87654321",
"dic": "2010101010",
"ic_dph": "SK2010101010",
"address": {
"street": "Obchodná 5",
"city": "Košice",
"postalCode": "04001",
"country": "SK"
},
"email": "fakturacia@odberatel.sk",
"phone": "+421900123456"
},
"items": [
{
"description": "Service work",
"quantity": 2,
"unit": "hod",
"unit_price": 50,
"vat_rate": 23
},
{
"description": "Spare part",
"quantity": 1,
"unit": "ks",
"unit_price": 80,
"vat_rate": 23
}
],
"payment_method": "bank_transfer",
"due_days": 14,
"note": "Thank you for your business.",
"order_number": "OBJ-2026-042"
}Response
HTTP/1.1 201 Created
{
"id": "b1f0d7a2-…",
"invoice_number": "2026001",
"status": "issued",
"customer": { "id": "…", "name": "Customer s.r.o." },
"items": [ … ],
"subtotal": "180.00",
"vat_total": "41.40",
"total": "221.40",
"currency": "EUR",
"issue_date": "2026-06-22",
"due_date": "2026-07-06",
"pdf_url": "https://…",
"created_at": "2026-06-22T10:00:00.000Z"
}The id value is used both when sending via Peppol (POST /v1/agent/peppol/send/{id}) and when checking status.
Idempotency (retry without duplicates)
POST /v1/agent/invoices supports an optional Idempotency-Key header: the primary protection mechanism against duplicate invoices on automatic retries. The same key with the same body returns the stored response(the invoice isn't created twice and the number series isn't consumed); the same key with a different body, or a concurrent request with the same key, returns 409. Without the header, behavior is unchanged.
POST /v1/agent/expenses. As an extra measure, store the returned id after a successful response.Downloading PDF / XML
You can download an issued invoice as PDF or as UBL XML. Both operations require the scopeinvoice:download and return the file as an attachment (Content-Disposition: attachment).
| Method | Path | Scope | Content-Type |
|---|---|---|---|
| GET | /v1/agent/invoices/{id}/pdf | invoice:download | application/pdf |
| GET | /v1/agent/invoices/{id}/xml | invoice:download | application/xml |
The XML is the exact UBL that was handed to the Access Point when sending via Peppol (kept for statutory e-invoice archiving). If the invoice hasn't been sent via Peppol yet, the XML isn't available and the endpoint returns 404 ("XML for this invoice is not available."). The file name is the invoice number ({numberPrefix}{number}.xml).
GET /v1/agent/invoices/b1f0d7a2-…/xml
X-API-Key: efk_pk_test_...
X-Organization-Id: 0a2c1f3e-…
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Disposition: attachment; filename="OF2026001.xml"
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">…</Invoice>Completeness requirements for Peppol
An invoice issued via this endpoint is fine as a regular document, but for it to be sendable via Peppol, the resulting UBL XML must pass EN 16931 + Peppol BIS Billing 3.0, plus the Slovak FS overlay. The SK overlay applies when the seller's country is SK, and requires:
| Rule (BT) | Field | Source in the API |
|---|---|---|
SK-BT-35 | Seller street | address.street of the organization |
SK-BT-37 | Seller city | address.city of the organization |
SK-BT-38 | Seller postal code | address.postalCode of the organization |
SK-BT-50 | Buyer street | customer.address.street |
SK-BT-52 | Buyer city | customer.address.city |
SK-BT-53 | Buyer postal code | customer.address.postalCode |
SK-BT-30 | schemeID for the seller's registration ID | derived from the organization's IČO/DIČ |
SK-BT-47 | schemeID for the buyer's registration ID | derived from the buyer's IČO/DIČ |
SK-BT-35-REQUIRED). So when creating an organization and when creating a customer, always send a full address (street + city + postalCode + country).Recipient routing in Peppol goes via the buyer's DIČ (sandbox: 9915:<DIČ>, production: 0245:<DIČ>). For correct VAT categorization (BR‑S/BR‑E), both the seller and buyer should have ic_dph. Before sending, you can pre-validate the UBL XML: see Validation.
You send an invoice issued by this endpoint via POST /v1/agent/peppol/send/{id}; right before sending, you can check it with a dry run via Preflight : that's validation from the Agent API (no session), with no DB writes and no credit charge. If instead you assemble the UBL/CII yourself and don't want to go through /agent/invoices, you can validate, reserve credit, and send in a single call via Connector.