eFakturuj / API Docs
eFakturuj Guides

Partner API

A partner account manages many client companies. Its API key acts for any of them, under /api/v1/partner/. This guide is the map for an integrator building on that surface: which paths the key can call, how a document moves through create, validate and send, which fields track the two things that happen after a send, and how to get the original files back.

The key and its paths

A partner API key is created in the partner console (Connect → API keys) and sent as X-API-Key: efk_... or Authorization: Bearer efk_.... It works on /api/v1/partner/* only. Any other path, including /api/v1/companies/{id}, answers 403 partner_api_key_not_allowed: the per-company routes are for the company's own users and keys.

Scopes on the key:

ScopeAllows
invoices:readClients, invoices, detail, downloads, exports, events, webhook listing
invoices:writeCreating invoices and customers, registering webhooks
invoices:sendSending
validate:onlyValidation without any other write
apps:manageInstalling a client's SAPI-SK connector

A call without the needed scope answers 403 insufficient_scope with the scope it wanted.

Clients

GET /api/v1/partner/clients
GET /api/v1/partner/clients/{company_id}

A company joins your portfolio when it is activated under your partner account (the activation code from the Finančná správa portal, entered in the console, or POST /companies/dev-enroll on the sandbox). The company_id in every path below is that client's id from the list.

Create, validate, send

POST /api/v1/partner/clients/{company_id}/invoices
POST /api/v1/partner/clients/{company_id}/invoices/{invoice_id}/validate
POST /api/v1/partner/clients/{company_id}/invoices/{invoice_id}/send

The body of the create call is the same InvoiceCreate the company API uses (see Sending invoices). Send your own invoice_number: a retry with the same number answers 409 invoice_number_conflict instead of creating a second draft, and GET /api/v1/partner/documents?client={company_id}&q={invoice_number} finds the one that exists.

Validate and send carry the invoice's revision in If-Match, taken from the version field of the create or detail response, for example If-Match: "3". Validate answers the new version and status; use that version on the send. A missing header is 428 precondition_required, a stale one 412. Send accepts an invoice in draft, validated or approved; any other status is 409 invalid_status, so a repeated send never transmits twice. Send answers 202 and the work continues in the background; 402 means the client workspace's monthly quota is used up.

Two things happen after a send

The buyer's access point confirms delivery over AS4 and answers with a Peppol MLS. Separately, eFakturuj reports the invoice to the Finančná správa (the SK TaxData Document, TDD) and the tax authority answers with its own MLS. GET /api/v1/partner/invoices/{invoice_id} tracks them apart.

Peppol delivery (AS4 and MLS)

  • status is the document's overall state (sent_peppol, delivered, rejected, failed, ...); peppol_message_id and peppol_error are the transmission's id and last error.
  • events entries with category: "peppol" are the transport timeline. An outbound invoice is peppol.outbound.invoice.<state> (a credit note peppol.outbound.credit_note.<state>): transmitted when AS4 accepted it, acknowledged when the buyer's MLS arrived. The receipt itself is peppol.inbound.mls.<state> with detail.mls_response_code (AP, AB or RE) and detail.anchor_kind: invoice for the buyer's receipt, tdd for the tax authority's answer to our report.

FS reporting (TDD)

  • c5 is the report's own state: state (pending, transmitted, acknowledged, rejected, failed), the tax authority's mls_response_code and mls_reasons, tdd_uuid, transmission_id and the timestamps. It is null while nothing has been filed.

Webhooks follow the same split: invoice.delivered and invoice.rejected (a negative MLS from the buyer) for delivery, invoice.fs_acknowledged and invoice.fs_rejected for the report, invoice.received for documents that arrive for the client. See Webhooks below.

The files

The detail's files list names every downloadable file of the invoice with a partner API path and whether a download will answer:

[
  {"kind": "ubl", "label": "invoice.ubl.xml", "href": "/api/v1/partner/invoices/{id}/ubl", "available": true},
  {"kind": "pdf", "label": "invoice.pdf", "href": "/api/v1/partner/invoices/{id}/pdf", "available": true},
  {"kind": "peppol_message", "label": "mls-receipt.xml", "href": "/api/v1/partner/invoices/{id}/peppol-messages/{message_id}/payload", "available": true},
  {"kind": "peppol_message", "label": "tax-data-document.xml", "href": "/api/v1/partner/invoices/{id}/peppol-messages/{message_id}/payload", "available": true}
]

GET /api/v1/partner/invoices/{invoice_id}/ubl returns the document as it exists: for an invoice that went out over Peppol the file that was sent, for a received invoice the supplier's document as it arrived, for a draft XML generated from the invoice. The X-UBL-Source header says which (original or generated). /json and /pdf sit beside it.

GET /api/v1/partner/invoices/{invoice_id}/peppol-messages/{message_id}/payload returns a stored Peppol message: a received envelope (received-invoice.xml, received-credit-note.xml), the buyer's receipt (mls-receipt.xml), the tax authority's receipt (fs-mls-receipt.xml) or the TaxData Document as filed (tax-data-document.xml). Envelopes received before copies were kept show available: false and answer 404; outbound envelopes other than the TDD are not kept.

For a month at a time, the export bundles the originals:

POST /api/v1/partner/clients/{company_id}/exports   {"period_from": "2026-09-01", "period_to": "2026-09-30"}
GET  /api/v1/partner/clients/{company_id}/exports/{export_id}

The poll answers parts with signed download_urls valid for 7 days; each ZIP holds the sent files under ubl/ next to a CSV and JSON summary.

Received documents

GET /api/v1/partner/clients/{company_id}/invoices?direction=received

lists what arrived for a client, and the detail and /ubl work the same way for those. A client that consumes documents through SAPI-SK instead gets its own credentials from POST /api/v1/partner/clients/{company_id}/apps/sapi-sk/install (scope apps:manage); the invoice.received webhook carries provider_document_id, the handle GET /sapi/document/receive/{documentId} takes. See SAPI-SK.

Webhooks

POST /api/v1/partner/clients/{company_id}/webhooks   {"url": "https://...", "events": "invoice.delivered,invoice.rejected,invoice.fs_acknowledged,invoice.fs_rejected,invoice.received"}
GET  /api/v1/partner/clients/{company_id}/webhooks
GET  /api/v1/partner/clients/{company_id}/webhooks/{webhook_id}/deliveries
POST /api/v1/partner/clients/{company_id}/webhooks/{webhook_id}/test

The default events list does not include invoice.received; add it when the client receives documents. Signing, retries and payloads are the same as for a company's own webhooks (Webhooks).

Retries and idempotency

The partner API does not read an Idempotency-Key header; only the SAPI-SK send does. What makes retries safe here is the invoice number on create and If-Match plus the status rule on send, described above. The Idempotency guide has the client-side pattern, and Rate limits the per-minute budget and the Retry-After header on 429.

See also