openapi: 3.1.0 info: title: efakt ERP API version: 1.0.0 license: name: Proprietary identifier: LicenseRef-Proprietary description: "Cut of the efakt portal API for ERP/accounting programs (Caris and compatible): `/erp/*` only. Generated from packages/contracts/openapi.yaml by docs/erp/build-erp-openapi.mjs - do not edit by hand. Guide: docs/ERP-INTEGRATION-GUIDE.md." servers: - url: https://efakt.example/api/v1 description: Replace with your portal address (HTTPS only) tags: - name: ERP description: ERP API (Caris a kompatibilní účetní programy) a správa ERP integrací paths: /erp/test-runs/{testRunId}: get: tags: - ERP operationId: erpGetTestRun summary: Výsledek testovacího odeslání pro ERP (platí 24 hodin; scope `send` nebo `status`) description: Totéž co `GET /test-runs/{id}`, ale s ERP údaji (ty se přijímají jen na `/erp/*`). security: - erpBearer: [] - erpBasic: [] parameters: - name: testRunId in: path required: true schema: type: string pattern: ^test_[0-9a-f-]{36}$ responses: "200": description: Testovací běh headers: X-Test-Mode: $ref: "#/components/headers/XTestMode" content: application/json: schema: $ref: "#/components/schemas/TestRun" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "410": description: Test je starší než 24 hodin (TEST_RUN_EXPIRED) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/invoices/validate: post: tags: - ERP - Validation operationId: erpValidateInvoice summary: Dry-run předodesílací validace pro ERP (scope `send`; NIC se neukládá ani neodesílá) description: | Totéž co `POST /invoices/validate` pro integraci ERP. `Idempotency-Key` se nevyžaduje. Rate limit 20 dry-runů/min na integraci (dry-run poštáře sdílí limit adaptéru). Tělo `application/xml` (spojení z `?connection_id`, příznaky z hlaviček `X-Confidential-Supply`) nebo JSON. security: - erpBearer: [] - erpBasic: [] parameters: - name: connection_id in: query schema: type: string format: uuid - name: Accept-Language in: header schema: type: string - name: X-Confidential-Supply in: header schema: type: string enum: - "true" - "false" - name: X-Check-Routing in: header schema: type: string enum: - "1" - "true" - name: check_routing in: query schema: type: boolean requestBody: required: true content: application/xml: schema: type: string application/json: schema: $ref: "#/components/schemas/ValidateInvoiceRequest" responses: "200": description: Report validace. `verdict=blocked` (i doklad, kterému chybí povinná pole, např. `REQUIRED_FIELD_MISSING`) se vrací jako 200 s nálezy; 422 zůstává pro nevalidní XML/kódování, cizího odesílatele aj. (stejné kódy jako odeslání). content: application/json: schema: $ref: "#/components/schemas/ValidationReport" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/validation/routing: get: tags: - ERP - Validation operationId: erpValidationRouting summary: Ověření příjemce v Peppolu pro ERP integraci (stejný výsledek jako `GET /validation/routing`) description: | Výslovná žádost (SMP / poštář výchozího odchozího spojení). Výpadek nebo vypnutý zdroj = `registered=null` + `reason` (fail-open, vždy 200). Autentizace ERP (Bearer `erp_…` / Basic), scope `send`. Limit 60 dotazů/min na integraci. security: - erpBearer: [] - erpBasic: [] parameters: - name: dic in: query required: true schema: type: string pattern: ^[0-9]{10}$ - name: connection_id in: query schema: type: string format: uuid responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/ValidationRouting" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/validation/party: get: tags: - ERP operationId: erpValidateParty summary: Ověření subjektu pro ERP integraci (stejný výsledek jako `GET /validation/party`) description: Autentizace ERP (Bearer `erp_…` / Basic), scope `send`. Limit 60 dotazů/min na integraci. security: - erpBearer: [] - erpBasic: [] parameters: - name: ico in: query schema: type: string maxLength: 64 - name: dic in: query schema: type: string maxLength: 64 - name: icdph in: query schema: type: string maxLength: 64 - name: country in: query schema: type: string enum: - SK - name: include in: query schema: type: string maxLength: 64 responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/PartyValidation" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "409": $ref: "#/components/responses/Conflict" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/auth/token: post: tags: - ERP operationId: erpAuthToken summary: Výměna jména a hesla technického účtu za krátkodobý Bearer token description: | Veřejný endpoint (bez `Authorization`; alternativně lze údaje poslat jako HTTP Basic). Rate limit **před** drahým ověřením hesla (per IP a per uživatelské jméno), po opakovaných chybách zámek účtu (`429 ACCOUNT_LOCKED` + `Retry-After`). Token má platnost `expires_in` sekund a stejné scopes jako integrace; zrušení hesla nebo integrace ho zneplatní okamžitě. security: [] requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/ErpTokenRequest" responses: "200": description: Token vydán content: application/json: schema: $ref: "#/components/schemas/ErpTokenResponse" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/me: get: tags: - ERP operationId: erpMe summary: Kdo jsem – tenant, integrace, scopes, dostupná spojení s poštáři a limity security: - erpBearer: [] - erpBasic: [] responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/ErpMe" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/invoices: post: tags: - ERP operationId: erpSubmitInvoice summary: Odeslání jedné faktury přes poštáře (scope `send`, vždy asynchronní) description: | `Content-Type: application/xml` (UBL 2.1 Peppol BIS 3.0, bajty beze změny; volitelně `X-Client-Reference`, `?connection_id=`) **nebo** `application/json` obálka `{ xml_base64, connection_id?, tag?, client_reference? }`. Povinný `Idempotency-Key` (klíče jsou nezávislé per integrace). Platí všechna pravidla `POST /invoices` (seller = tenant, datum vystavení = den odeslání v Europe/Bratislava, retry po `failed_permanent` novým klíčem, `RECIPIENT_NOT_REGISTERED` = `delivery_failed`). Poštář: `connection_id` z požadavku, jinak výchozí poštář integrace, jinak výchozí odchozí poštář tenanta. Chyby 422 s `findings` jako u `POST /invoices`. security: - erpBearer: [] - erpBasic: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" - name: connection_id in: query schema: type: string format: uuid - name: X-Client-Reference in: header schema: type: string maxLength: 200 - $ref: "#/components/parameters/TestQuery" - $ref: "#/components/parameters/TestModeHeader" - $ref: "#/components/parameters/SimulateQuery" - $ref: "#/components/parameters/SimulateWebhooksQuery" requestBody: required: true content: application/xml: schema: type: string format: binary application/json: schema: $ref: "#/components/schemas/ErpInvoiceEnvelope" responses: "200": description: Jen v testovacím režimu (`test=true` nebo integrace `test_only`) - nic se neodeslalo ani neuložilo. Viz `TestSubmitResponse`. headers: X-Test-Mode: $ref: "#/components/headers/XTestMode" X-Would-Have-Sent: $ref: "#/components/headers/XWouldHaveSent" Idempotent-Replayed: description: "`true` při přehrání původní testovací odpovědi" schema: type: string enum: - "true" content: application/json: schema: $ref: "#/components/schemas/TestSubmitResponse" "202": description: "Přijato k odeslání (opakování se stejným klíčem vrací původní odpověď s hlavičkou `Idempotent-Replayed: true`)." headers: Location: description: Adresa stavu faktury schema: type: string Idempotent-Replayed: description: true při opakování se stejným Idempotency-Key schema: type: string content: application/json: schema: $ref: "#/components/schemas/ErpInvoiceAccepted" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "409": $ref: "#/components/responses/Conflict" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/invoices/batch: post: tags: - ERP operationId: erpSubmitInvoiceBatch summary: Dávkové odeslání (scope `send`), částečný úspěch = 207 description: | Až `max_batch_items` (výchozí 50, viz `GET /erp/me`) položek `{ client_reference, xml_base64, idempotency_key, connection_id? }`. Každá položka je zpracována a idempotentní **samostatně** (stejně jako `POST /erp/invoices`); odpověď `207` nese výsledek po položkách (`status` accepted|error, `http_status`, u chyby `error` jako problem+json). Opakování celé dávky je bezpečné. security: - erpBearer: [] - erpBasic: [] parameters: - $ref: "#/components/parameters/TestQuery" - $ref: "#/components/parameters/TestModeHeader" - $ref: "#/components/parameters/SimulateQuery" - $ref: "#/components/parameters/SimulateWebhooksQuery" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ErpBatchRequest" responses: "207": description: Výsledky po položkách (pořadí jako ve vstupu) content: application/json: schema: $ref: "#/components/schemas/ErpBatchResponse" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/invoices/{invoiceId}: get: tags: - ERP operationId: erpGetInvoice summary: Stav a události odeslané faktury (scope `send` nebo `status`) description: | Stejný model jako `GET /invoices/{id}`; jen odchozí faktury vlastního tenanta (jinak `404`). security: - erpBearer: [] - erpBasic: [] parameters: - $ref: "#/components/parameters/InvoiceId" responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/InvoiceDetail" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/outbox/changes: get: tags: - ERP operationId: erpOutboxChanges summary: Proud změn stavů odeslaných faktur (scope `status`) description: | Změny stavů faktur odeslaných touto integrací (`scope=tenant` = všech odchozích faktur tenanta) od kurzoru. **Vždy si ulož `next_cursor`** (i při prázdné stránce; je neprůhledný a stabilní). Změny se zveřejňují se zpožděním `ERP_FEED_LAG_SECONDS` (výchozí 30 s), aby souběžné transakce nemohly způsobit ztrátu události. At-least-once, řazení podle času změny a id. Bez kurzoru se začíná od `since` (jinak od začátku historie). security: - erpBearer: [] - erpBasic: [] parameters: - name: cursor in: query schema: type: string maxLength: 2048 - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 100 - name: since in: query description: "Jen bez kurzoru: ISO 8601 datum nebo datum a čas" schema: type: string maxLength: 40 - name: scope in: query schema: type: string enum: - own - tenant default: own responses: "200": description: Stránka změn content: application/json: schema: $ref: "#/components/schemas/ErpOutboxPage" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/pending: get: tags: - ERP operationId: erpInboxPending summary: Přijaté faktury, které tato integrace ještě nepotvrdila (scope `receive`) description: | Příchozí faktury tenanta seřazené podle `received_at` vzestupně (stabilně: `received_at`, `id`), které **tato integrace** nepotvrdila (ACK) – po založení integrace celá dostupná historie. Bez ACK se nabídnou znovu (at-least-once). Stav je per integrace (víc ERP u jednoho klienta si nepřebíjí převzetí). `received_invalid` a `received_without_xml` jsou vidět s příznakem (`status`, `xml_missing`); karanténní (po N NACK) se nenabízejí. Čtení **nemění** stav. security: - erpBearer: [] - erpBasic: [] parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 100 - name: cursor in: query schema: type: string maxLength: 2048 - name: include in: query description: "`xml` = vložit XML jako base64 (do limitu velikosti odpovědi, jinak odkaz `xml_url`)" schema: type: string enum: - xml - name: charset in: query description: "S `include=xml`: vložit odvozenou kopii XML v cílovém kódování (viz `GET /erp/me` capabilities.charsets); položka pak nese `xml_encoding` a `xml_derived_from_sha256`; nepřevoditelná položka má `xml_base64: null` a `xml_encoding_error` (nikdy tichá náhrada)." schema: type: string maxLength: 40 responses: "200": description: Stránka content: application/json: schema: $ref: "#/components/schemas/ErpInboxPage" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/by-period: get: tags: - ERP operationId: erpInboxByPeriod summary: Přijaté faktury za zadané období (scope `receive`) – bez změny stavu převzetí description: | Dohledání a opakované stažení (např. celý měsíc). `from`/`to`: datum `YYYY-MM-DD` (den v Europe/Bratislava, `to` včetně) nebo ISO 8601 datum a čas (obě meze včetně). `date_field` = `received_at` (výchozí) | `issue_date`. `state` = `all` (výchozí) | `pending` | `acknowledged` | `conflict` (faktury s odlišnou duplicitou). Maximální rozsah `max_period_days` (výchozí 366; viz `GET /erp/me`), `limit` ≤ 500, řazení vzestupně podle zvoleného pole a `id`, stabilní kurzor. **Stažení neoznačuje faktury jako převzaté.** Volitelně `ack=true` = „stáhni a potvrď“: ACK se provede až PO předání celé odpovědi do spojení (server počká na `finish` odpovědi; při chybě nebo přerušeném spojení se neprovede; odpověď má hlavičku `X-Erp-Ack: deferred`; garance je „odesláno“, ne „klient zpracoval“), a JEN u položek ve stavu `pending`, které odpověď nese i s XML (`include=xml`, `xml_base64` ≠ null). Nikdy nepotvrdí položky v karanténě (`quarantined`), pronajaté (`leased`), v prodlevě po NACK (`nack_backoff`), „bez XML“ (`xml_missing`) ani bez vloženého XML (`xml_not_inline`) – ty jsou v odpovědi v poli `ack_skipped` s důvodem. Potvrzení nese `erp_reference.id = auto:by_period` a audit `erp.inbox.ack` s `delivery_confirmed: false`. Doporučený a bezpečný postup zůstává samostatné `POST /erp/inbox/ack` až po zpracování. security: - erpBearer: [] - erpBasic: [] parameters: - name: from in: query required: true schema: type: string maxLength: 40 - name: to in: query required: true schema: type: string maxLength: 40 - name: date_field in: query schema: type: string enum: - received_at - issue_date default: received_at - name: state in: query schema: type: string enum: - all - pending - acknowledged - conflict default: all - name: ack in: query schema: type: boolean default: false - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 100 - name: cursor in: query schema: type: string maxLength: 2048 - name: include in: query description: "`xml` = vložit XML jako base64 (do limitu velikosti odpovědi, jinak odkaz `xml_url`)" schema: type: string enum: - xml - name: charset in: query description: "S `include=xml`: vložit odvozenou kopii XML v cílovém kódování (viz `GET /erp/me` capabilities.charsets); položka pak nese `xml_encoding` a `xml_derived_from_sha256`; nepřevoditelná položka má `xml_base64: null` a `xml_encoding_error` (nikdy tichá náhrada)." schema: type: string maxLength: 40 responses: "200": description: Stránka content: application/json: schema: $ref: "#/components/schemas/ErpInboxPage" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/stats: get: tags: - ERP operationId: erpInboxStats summary: Počty pending / leased / acknowledged / nack / quarantined (scope `receive`) security: - erpBearer: [] - erpBasic: [] responses: "200": description: OK content: application/json: schema: $ref: "#/components/schemas/ErpInboxStats" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/claim: post: tags: - ERP operationId: erpInboxClaim summary: Převzetí dávky pending faktur s lease (volitelné, scope `receive`) description: | Vrátí až `limit` pending faktur a na `lease_seconds` je zamkne pro tuto integraci, aby paralelní workery ERP nestahovaly totéž. Po vypršení leasu se faktura nabídne znovu. Lease není ACK: převzetí se potvrzuje až `POST /erp/inbox/ack`. security: - erpBearer: [] - erpBasic: [] parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 500 default: 50 - name: lease_seconds in: query schema: type: integer minimum: 10 maximum: 3600 default: 300 - name: include in: query schema: type: string enum: - xml - name: charset in: query description: "S `include=xml`: vložit odvozenou kopii XML v cílovém kódování (viz `GET /erp/me` capabilities.charsets); položka pak nese `xml_encoding` a `xml_derived_from_sha256`; nepřevoditelná položka má `xml_base64: null` a `xml_encoding_error` (nikdy tichá náhrada)." schema: type: string maxLength: 40 responses: "200": description: Zamčené položky (může být méně než `limit`, i žádná) content: application/json: schema: $ref: "#/components/schemas/ErpClaimResult" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/ack: post: tags: - ERP operationId: erpInboxAck summary: Potvrzení převzetí (ACK), idempotentní (scope `receive`) description: | Potvrdí převzetí faktur touto integrací. Odpověď rozděluje `acknowledged` (nově potvrzeno), `already_acknowledged` (beze změny, datum zůstává původní) a `not_found` (neexistuje, cizí tenant nebo není příchozí); faktura „bez XML“ bez dodatečného XML je v `rejected` (`xml_missing`, není co převzít). ACK nikdy nemění ani nemaže uložená XML; je konečný. Datum a čas ACK se zobrazí u faktury v portálu jako „převzato do ERP“. security: - erpBearer: [] - erpBasic: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ErpAckRequest" responses: "200": description: Výsledek po položkách content: application/json: schema: $ref: "#/components/schemas/ErpAckResult" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/nack: post: tags: - ERP operationId: erpInboxNack summary: ERP doklad nedokázalo zpracovat (NACK, scope `receive`) description: | Faktura zůstává pending (znovu se nabídne po exponenciální prodlevě), zvýší se počítadlo pokusů a důvod vidí admin v portálu. Po `nack_quarantine_after` pokusech (výchozí 5) přejde do karantény (nenabízí se) a tenantu se odešle upozornění. security: - erpBearer: [] - erpBasic: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ErpNackRequest" responses: "200": description: Stav po NACK content: application/json: schema: $ref: "#/components/schemas/ErpNackResult" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "409": $ref: "#/components/responses/Conflict" "413": $ref: "#/components/responses/PayloadTooLarge" "415": $ref: "#/components/responses/UnsupportedMediaType" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/{invoiceId}/xml: get: tags: - ERP operationId: erpInboxXml summary: Původní XML příchozí faktury (přesné bajty, ETag = SHA-256; scope `receive`) description: | Nemění stav převzetí. `If-None-Match` s hodnotou SHA-256 vrací `304`. Doklad bez XML (`received_without_xml`) = `409 XML_NOT_AVAILABLE`, dokud nedorazí dodatečné XML (pak se vrací ono). `duplicate_id` = konkrétní dodatek / odlišná duplicita z pole `duplicates` položky. security: - erpBearer: [] - erpBasic: [] parameters: - $ref: "#/components/parameters/InvoiceId" - name: duplicate_id in: query schema: type: string format: uuid - name: charset in: query description: | Vrátit **odvozenou kopii** v cílovém kódování (`windows-1250`, `iso-8859-2`, `windows-1252`, `cp852`, `utf-16le`, `utf-16be`, `utf-8`; viz `GET /erp/me` → `capabilities.charsets`). Deklarace `encoding=` se přepíše, hlavičky `X-Derived-From-SHA256` (SHA-256 UTF-8 verze) a `X-Source-Encoding`; `ETag` je SHA-256 odvozených bajtů. Znak, který cílové kódování nemá => `422 ENCODING_NOT_REPRESENTABLE` se seznamem znaků (nikdy tichá náhrada). Odvozená kopie NENÍ doklad k archivaci. schema: type: string maxLength: 40 responses: "200": description: XML beze změny (UTF-8) nebo odvozená kopie při `charset` headers: ETag: description: SHA-256 XML v uvozovkách schema: type: string X-Derived-From-SHA256: description: "Jen s `charset`: SHA-256 UTF-8 verze, ze které je kopie odvozena" schema: type: string X-Source-Encoding: description: "Jen s `charset`: kódování zdroje, ze kterého je kopie odvozena (vždy `utf-8`; cílové kódování je v Content-Type)" schema: type: string content: application/xml: schema: type: string format: binary "304": description: Beze změny (ETag souhlasí) "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "409": $ref: "#/components/responses/Conflict" "422": $ref: "#/components/responses/Unprocessable" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" /erp/inbox/{invoiceId}/attachments/{attachmentId}: get: tags: - ERP operationId: erpInboxAttachment summary: Příloha příchozí faktury (scope `receive`) security: - erpBearer: [] - erpBasic: [] parameters: - $ref: "#/components/parameters/InvoiceId" - name: attachmentId in: path required: true schema: type: string format: uuid responses: "200": description: Obsah přílohy s původním Content-Type (dokumentováno jako octet-stream) content: application/octet-stream: schema: type: string format: binary "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": $ref: "#/components/responses/NotFound" "429": $ref: "#/components/responses/TooManyRequests" "503": $ref: "#/components/responses/ServiceUnavailable" components: securitySchemes: erpBasic: type: http scheme: basic description: "HTTP Basic: jméno + heslo technického účtu ERP integrace (Argon2id, rate limit před hashem, zámek po chybách). Jen pro `/erp/*`; povinně HTTPS." erpBearer: type: http scheme: bearer bearerFormat: erp_* description: Token ERP integrace (`erp__`, ≥ 256 bit) nebo krátkodobý token z `POST /erp/auth/token`. Jen pro `/erp/*`. schemas: Address: type: object required: - street - city - postal_code - country_code properties: street: type: - string - "null" city: type: - string - "null" postal_code: type: - string - "null" country_code: type: - string - "null" description: ISO 3166-1 alpha-2 AttachmentMeta: type: object required: - id - filename - mime_type - size_bytes - sha256 - download_url properties: id: type: string format: uuid filename: type: string mime_type: type: string size_bytes: type: integer sha256: type: string pattern: ^[0-9a-f]{64}$ download_url: type: string ConflictingDuplicate: type: object required: - id - reason - source - received_at - xml_sha256 - xml_size_bytes - download_url properties: id: type: string format: uuid reason: type: string enum: - different_content - xml_supplement source: type: string received_at: type: string format: date-time xml_sha256: type: string pattern: ^[0-9a-f]{64}$ xml_size_bytes: type: integer download_url: type: string DeliveryEvent: type: object description: Neměnný záznam stavu/události (tabulka `invoice_events`), včetně MLS informací z poštáře. required: - id - at - type - source - from_status - to_status - reason_code - message - details properties: id: type: string format: uuid at: type: string format: date-time type: $ref: "#/components/schemas/DeliveryEventType" source: $ref: "#/components/schemas/DeliveryEventSource" from_status: oneOf: - $ref: "#/components/schemas/InvoiceStatus" - type: "null" to_status: oneOf: - $ref: "#/components/schemas/InvoiceStatus" - type: "null" reason_code: type: - string - "null" description: Např. `RECIPIENT_NOT_REGISTERED`, `PROVIDER_REJECTED` message: type: - string - "null" details: type: - object - "null" additionalProperties: true description: Prostá data z poštáře (bez tajemství) DeliveryEventSource: type: string enum: - portal - worker - provider DeliveryEventType: type: string enum: - status_changed - provider_message - warning - retry_scheduled - note Direction: type: string enum: - inbound - outbound DocumentTypeCode: type: string description: | UNCL1001 – 380 faktura, 381 dobropis, 383 debet, 384 opravná, 388 faktura k přijaté platbě, 389 samofakturace, 393 factoring, 261 samofakturační dobropis. `unknown` = příchozí doklad, jehož typ nelze určit nebo ho portál nepodporuje (M-03; původní kód viz `unparsed_reason`). Odesílání: 384 se **nepřijímá** (Peppol BIS 3.0.20 ho odmítá pravidlem PEPPOL-EN16931-P0112) – použijte dobropis 381 + novou fakturu 380; 389/261 jen s `TenantSettings.allow_self_billing`. enum: - "380" - "381" - "383" - "384" - "388" - "389" - "393" - "261" - unknown EncodingInfo: type: object description: | Jak portál určil kódování přijatého XML a co s ním udělal (docs/CODEPAGE-INPUT-SPEC.md). Odeslaná/uložená verze (`xml_sha256`) je vždy UTF-8; při `transcoded=true` se původní bajty uchovávají beze změny jako nemazatelný zdrojový dokument (`GET /invoices/{id}/source-xml`). required: - source - origin - transcoded - bom_stripped - warnings properties: source: type: string enum: - utf-8 - windows-1250 - iso-8859-2 - windows-1252 - cp852 - utf-16le - utf-16be description: Kódování původních bajtů origin: type: string enum: - param - envelope - bom - declaration - tenant_default - detected_utf8 description: "Čím bylo kódování určeno: `param` (charset v Content-Type), `envelope` (pole `encoding`), BOM, deklarace XML, výchozí kódování tenanta, platnost UTF-8." declared: type: - string - "null" description: Hodnota `encoding=` v XML deklaraci originálu, jak byla napsána. transcoded: type: boolean description: Uložené bajty se liší od původních (převod kódování, odstranění BOM, přepis deklarace). bom_stripped: type: boolean source_sha256: type: string pattern: ^[0-9a-f]{64}$ result_sha256: type: string pattern: ^[0-9a-f]{64}$ description: SHA-256 UTF-8 verze (= `xml_sha256`). transcoder_version: type: string warnings: type: array description: "`ENCODING_TRANSCODED`, `ENCODING_DECLARATION_MISMATCH`, `ENCODING_SUSPICIOUS`." items: $ref: "#/components/schemas/ValidationFinding" EncodingUnrepresentableChar: type: object required: - char - code_point - count - first_offset properties: char: type: string code_point: type: string example: U+03A9 count: type: integer first_offset: type: integer description: Pozice prvního výskytu v textu dokumentu (kódové jednotky UTF-16) ErpAckRequest: type: object required: - ids properties: ids: type: array minItems: 1 maxItems: 500 items: type: string format: uuid erp_reference: type: object required: - id properties: id: type: string minLength: 1 maxLength: 200 ErpAckResult: type: object required: - acknowledged - already_acknowledged - not_found - acknowledged_at properties: acknowledged: type: array items: type: string format: uuid already_acknowledged: type: array items: type: string format: uuid not_found: type: array items: type: string format: uuid acknowledged_at: type: string format: date-time rejected: type: array description: Faktury „bez XML“ bez dodatečného XML – nelze potvrdit. items: type: object required: - id - reason properties: id: type: string format: uuid reason: type: string enum: - xml_missing ErpBatchItem: type: object required: - client_reference - xml_base64 - idempotency_key properties: client_reference: type: string minLength: 1 maxLength: 200 xml_base64: type: string idempotency_key: type: string minLength: 1 maxLength: 200 pattern: ^[A-Za-z0-9._:-]+$ connection_id: type: string format: uuid tag: type: string maxLength: 64 encoding: type: string maxLength: 40 description: Kódování bajtů této položky (per položka; viz `InvoiceSubmitEnvelope.encoding`). test: type: boolean description: "Testovací režim jen pro tuto položku (globální `test` má přednost: true vyhrává)." simulate: $ref: "#/components/schemas/TestSimulate" simulate_webhooks: type: boolean ErpBatchItemResult: type: object required: - client_reference - status - http_status - replayed - id - invoice_status - accepted - error properties: client_reference: type: string status: type: string enum: - accepted - error - test_accepted http_status: type: integer replayed: type: boolean id: type: - string - "null" format: uuid invoice_status: oneOf: - $ref: "#/components/schemas/OutboundStatus" - type: "null" accepted: oneOf: - $ref: "#/components/schemas/ErpInvoiceAccepted" - type: "null" error: oneOf: - $ref: "#/components/schemas/Problem" - type: "null" test: type: boolean description: Jen u testovací položky; `id` je null, výsledek je v `test_result`. test_result: oneOf: - $ref: "#/components/schemas/TestSubmitResponse" - type: "null" ErpBatchRequest: type: object description: Skutečný limit je `max_batch_items` z `GET /erp/me` (výchozí 50); nad limit `400`. required: - items properties: items: type: array minItems: 1 maxItems: 500 items: $ref: "#/components/schemas/ErpBatchItem" test: type: boolean description: Globální testovací režim pro celou dávku (také `?test=true` / `X-Test-Mode`). simulate: $ref: "#/components/schemas/TestSimulate" simulate_webhooks: type: boolean ErpBatchResponse: type: object required: - items properties: items: type: array items: $ref: "#/components/schemas/ErpBatchItemResult" ErpClaimResult: type: object required: - items - lease_seconds - lease_expires_at properties: items: type: array items: $ref: "#/components/schemas/ErpInboxItem" lease_seconds: type: integer lease_expires_at: type: - string - "null" format: date-time ErpDelivery: type: object description: "Příznak „převzato do ERP“: integrace a datum a čas potvrzení (UTC; UI zobrazuje Europe/Bratislava). Nemění XML ani hash." required: - integration_id - integration_name - acknowledged_at properties: integration_id: type: string format: uuid integration_name: type: string acknowledged_at: type: string format: date-time ErpInboxItem: type: object required: - id - invoice_number - doc_type_code - status - validation_status - issue_date - due_date - currency - seller - buyer - total_net - total_vat - total_gross - received_at - xml_missing - xml_sha256 - xml_size_bytes - xml_url - xml_base64 - attachments - erp_state - acknowledged_at - attempts properties: id: type: string format: uuid invoice_number: type: string doc_type_code: $ref: "#/components/schemas/DocumentTypeCode" status: type: string enum: - received - received_invalid - received_without_xml validation_status: $ref: "#/components/schemas/ValidationStatus" issue_date: type: string format: date due_date: type: - string - "null" format: date currency: type: string pattern: ^[A-Z]{3}$ seller: $ref: "#/components/schemas/ErpParty" buyer: $ref: "#/components/schemas/ErpParty" total_net: $ref: "#/components/schemas/Money" total_vat: $ref: "#/components/schemas/Money" total_gross: $ref: "#/components/schemas/Money" received_at: type: string format: date-time xml_missing: type: boolean xml_sha256: type: string pattern: ^[0-9a-f]{64}$ xml_size_bytes: type: integer xml_url: type: - string - "null" xml_base64: type: - string - "null" description: Jen s include=xml a do limitu velikosti odpovědi; jinak null (stáhněte přes xml_url). xml_encoding: type: string description: "Jen s `charset`: kódování odvozené kopie v `xml_base64` (jinak je XML UTF-8)." xml_derived_from_sha256: type: string pattern: ^[0-9a-f]{64}$ description: "Jen s `charset`: SHA-256 UTF-8 verze, ze které je kopie odvozena (= `xml_sha256`)." xml_encoding_error: type: object description: "Jen s `charset`: kopii nelze vytvořit (`ENCODING_NOT_REPRESENTABLE`); `xml_base64` je null." required: - code - detail properties: code: type: string detail: type: string characters: type: array items: $ref: "#/components/schemas/EncodingUnrepresentableChar" attachments: type: array items: $ref: "#/components/schemas/AttachmentMeta" erp_state: $ref: "#/components/schemas/ErpInboxState" acknowledged_at: type: - string - "null" format: date-time attempts: type: integer duplicates: type: array description: Dodatečná XML a odlišné duplicity téže faktury; stahují se přes `xml_url`. items: type: object required: - id - reason - received_at - xml_sha256 - xml_size_bytes - xml_url properties: id: type: string format: uuid reason: type: string enum: - different_content - xml_supplement received_at: type: string format: date-time xml_sha256: type: string pattern: ^[0-9a-f]{64}$ xml_size_bytes: type: integer xml_url: type: string ErpInboxPage: type: object required: - items - next_cursor properties: items: type: array items: $ref: "#/components/schemas/ErpInboxItem" next_cursor: type: - string - "null" ack_skipped: type: array description: "Jen `by-period?ack=true`: položky, které se NEpotvrdí, a proč." items: type: object required: - id - reason properties: id: type: string format: uuid reason: type: string enum: - quarantined - leased - nack_backoff - xml_missing - xml_not_inline ErpInboxState: type: string enum: - pending - leased - acknowledged - nack - quarantined ErpInboxStats: type: object required: - pending - leased - acknowledged - nack - quarantined - total_eligible - oldest_pending_received_at - last_ack_at properties: pending: type: integer minimum: 0 leased: type: integer minimum: 0 acknowledged: type: integer minimum: 0 nack: type: integer minimum: 0 quarantined: type: integer minimum: 0 total_eligible: type: integer minimum: 0 oldest_pending_received_at: type: - string - "null" format: date-time last_ack_at: type: - string - "null" format: date-time withheld_xml_missing: type: integer minimum: 0 description: Faktury „bez XML“ bez dodatečného XML (nenabízejí se, nejsou v `pending`). conflicts: type: integer minimum: 0 description: Faktury s odlišnou duplicitou (`by-period?state=conflict`). ErpInvoiceAccepted: allOf: - $ref: "#/components/schemas/InvoiceAccepted" - type: object required: - client_reference - location properties: client_reference: type: - string - "null" location: type: string ErpInvoiceEnvelope: type: object required: - xml_base64 properties: xml_base64: type: string connection_id: type: string format: uuid tag: type: string maxLength: 64 client_reference: type: string maxLength: 200 encoding: type: string maxLength: 40 description: Kódování bajtů v `xml_base64` (viz `InvoiceSubmitEnvelope.encoding`). test: type: boolean simulate: $ref: "#/components/schemas/TestSimulate" simulate_webhooks: type: boolean ErpMe: type: object required: - tenant - integration - connections - limits - server_time properties: tenant: type: object required: - id - name - dic - peppol_id properties: id: type: string format: uuid name: type: string dic: type: string peppol_id: type: string integration: type: object required: - id - name - scopes - default_connection_id - auth_via - ip_allowlist properties: id: type: string format: uuid name: type: string scopes: type: array items: $ref: "#/components/schemas/ErpScope" default_connection_id: type: - string - "null" format: uuid auth_via: type: string enum: - token - basic ip_allowlist: type: array items: type: string test_only: type: boolean connections: type: array items: $ref: "#/components/schemas/ErpMeConnection" limits: type: object required: - max_batch_items - max_page_size - max_period_days - max_xml_bytes - lease_seconds_default - inline_xml_max_bytes properties: max_batch_items: type: integer max_page_size: type: integer max_period_days: type: integer max_xml_bytes: type: integer lease_seconds_default: type: integer inline_xml_max_bytes: type: integer server_time: type: string format: date-time capabilities: type: object description: Schopnosti serveru; `charsets` = kódování, která server na vstupu převádí do UTF-8 a umí vrátit jako odvozenou kopii (VFP klient podle toho posílá bajty beze změny s `charset`). required: - charsets properties: charsets: type: array items: type: string bom_policy: type: string enum: - strip - keep source_xml: type: boolean test_mode: type: boolean description: Server umí testovací režim (`test`, `simulate`, `GET /test-runs/{id}`). test_mode: type: object required: - available - test_only - sandbox_tenant - rate_limit_per_minute - result_ttl_hours properties: available: type: boolean test_only: type: boolean description: "Pověření je `test_only`: ostrá odeslání jsou zakázána (403 TEST_ONLY_CREDENTIAL)." sandbox_tenant: type: boolean rate_limit_per_minute: type: integer result_ttl_hours: type: integer ErpMeConnection: type: object required: - id - name - provider_type - capabilities - receive_mode - is_default_outbound - is_integration_default - enabled properties: id: type: string format: uuid name: type: string provider_type: type: string capabilities: type: array items: type: string enum: - inbound - outbound receive_mode: type: - string - "null" is_default_outbound: type: boolean is_integration_default: type: boolean enabled: type: boolean ErpNackRequest: type: object required: - id - reason properties: id: type: string format: uuid reason: type: string minLength: 1 maxLength: 500 ErpNackResult: type: object required: - id - state - attempts - retry_at properties: id: type: string format: uuid state: type: string enum: - nack - quarantined attempts: type: integer retry_at: type: - string - "null" format: date-time ErpOutboxChange: type: object description: Změna stavu odeslané faktury (`id` = id faktury). required: - event_id - id - client_reference - invoice_number - status - previous_status - at - reason - message properties: event_id: type: string id: type: string format: uuid client_reference: type: - string - "null" invoice_number: type: string status: $ref: "#/components/schemas/InvoiceStatus" previous_status: oneOf: - $ref: "#/components/schemas/InvoiceStatus" - type: "null" at: type: string format: date-time reason: type: - string - "null" message: type: - string - "null" ErpOutboxPage: type: object required: - items - next_cursor - has_more properties: items: type: array items: $ref: "#/components/schemas/ErpOutboxChange" next_cursor: type: string has_more: type: boolean ErpParty: type: object required: - name - dic - ic_dph - peppol_id properties: name: type: string dic: type: - string - "null" ic_dph: type: - string - "null" peppol_id: type: - string - "null" ErpScope: type: string enum: - send - receive - status ErpTokenRequest: type: object required: - username - password properties: grant_type: type: string enum: - password username: type: string minLength: 1 maxLength: 100 password: type: string minLength: 1 maxLength: 200 ErpTokenResponse: type: object required: - access_token - token_type - expires_in - scope properties: access_token: type: string token_type: type: string enum: - Bearer expires_in: type: integer scope: type: string ErrorCode: type: string description: Katalog kódů chyb a varování (viz docs/CONTRACTS.md pro mapování na HTTP status). enum: - BAD_REQUEST - INVALID_CURSOR - UNAUTHENTICATED - INVALID_CREDENTIALS - TOTP_INVALID - SESSION_EXPIRED - API_KEY_INVALID - API_KEY_REVOKED - FORBIDDEN - INSUFFICIENT_SCOPE - CSRF_FAILED - TENANT_SUSPENDED - TENANT_REQUIRED - NOT_FOUND - CONFLICT - INBOUND_CONNECTION_EXISTS - EMAIL_ALREADY_EXISTS - DIC_ALREADY_EXISTS - WEAK_PASSWORD - IDEMPOTENCY_KEY_REQUIRED - IDEMPOTENCY_CONFLICT - IDEMPOTENCY_IN_PROGRESS - DUPLICATE_INVOICE - PAYLOAD_TOO_LARGE - UNSUPPORTED_MEDIA_TYPE - XML_MALFORMED - UNSUPPORTED_DOCUMENT_TYPE - VALIDATION_FAILED - OBLIGATION_FORBIDDEN - SELLER_MISMATCH - BUYER_MISMATCH - ISSUE_DATE_NOT_TODAY - DELIVERY_DATE_OVER_15_DAYS - RECIPIENT_NOT_REGISTERED - NO_OUTBOUND_CONNECTION - CONNECTION_DISABLED - SSRF_BASE_URL_NOT_ALLOWED - INVALID_CREDENTIALS_FORMAT - UNKNOWN_PROVIDER_TYPE - INVALID_CONNECTION_SETTINGS - EXPORT_TOO_LARGE - EXPORT_NOT_READY - INVALID_STATE_TRANSITION - RATE_LIMITED - PROVIDER_AUTH_FAILED - PROVIDER_RATE_LIMITED - PROVIDER_UNAVAILABLE - PROVIDER_REJECTED - PROVIDER_ERROR - PROVIDER_OPERATION_NOT_SUPPORTED - WEBHOOK_SIGNATURE_INVALID - LOOKUP_NOT_SUPPORTED - VALIDATOR_UNAVAILABLE - SERVICE_UNAVAILABLE - INTERNAL_ERROR - XML_NOT_AVAILABLE - SELF_BILLING_NOT_ENABLED - INVALID_SETTING - SMTP_NOT_CONFIGURED - INVALID_TOKEN - IP_NOT_ALLOWED - ERP_CLIENT_DISABLED - ACCOUNT_LOCKED - INVALID_PROVIDER_INFO - INVALID_ASSET - AI_REFRESH_DISABLED - BACKUP_ENGINE_UNAVAILABLE - BACKUP_ENGINE_ERROR - RETENTION_BELOW_LEGAL_MINIMUM - ENCODING_UNSUPPORTED - ENCODING_AMBIGUOUS - ENCODING_INVALID - ENCODING_INVALID_CHARACTER - ENCODING_NOT_REPRESENTABLE - INVALID_TEST_FLAG - TEST_ONLY_CREDENTIAL - TEST_SEND_BLOCKED - SANDBOX_REAL_CONNECTION - TEST_RUN_EXPIRED ErrorSummary: type: object required: - code - message - at properties: code: type: string message: type: string at: type: string format: date-time FindingSeverity: type: string enum: - error - warning - info FindingSource: type: string enum: - xsd - schematron_en16931 - schematron_peppol - schematron_sk - portal - party - registry - routing - obligation - tdd_sk - provider_dryrun - external InboundStatus: type: string description: received_without_xml = poštář předal jen metadata (XML > 4 MB, push payload:null); XML je nutné stáhnout u poštáře (`provider_portal_url`) a nahrát ručním importem. enum: - received - received_invalid - received_without_xml InvoiceAccepted: type: object required: - id - status - direction - doc_type_code - invoice_number - issue_date - seller_peppol_id - buyer_peppol_id - connection_id - xml_sha256 - warnings - links properties: id: type: string format: uuid status: $ref: "#/components/schemas/OutboundStatus" direction: type: string enum: - outbound doc_type_code: $ref: "#/components/schemas/DocumentTypeCode" invoice_number: type: string issue_date: type: string format: date seller_peppol_id: type: string buyer_peppol_id: type: - string - "null" connection_id: type: string format: uuid xml_sha256: type: string pattern: ^[0-9a-f]{64}$ warnings: type: array description: Nálezy se severity `warning`/`info` (např. `RECIPIENT_NOT_REGISTERED`, `ISSUE_DATE_NOT_TODAY` při policy=warn). items: $ref: "#/components/schemas/ValidationFinding" links: $ref: "#/components/schemas/InvoiceLinks" validation_run_id: type: string format: uuid description: Záznam předodesílací validace (`validation_runs`) encoding: $ref: "#/components/schemas/EncodingInfo" InvoiceDetail: allOf: - $ref: "#/components/schemas/InvoiceListItem" - type: object required: - xml_sha256 - xml_size_bytes - remote_id - buyer_reference - delivery_date - billing_references - note - totals - lines - attachments - validation - delivery_events - last_error - links - payload_omitted_reason - provider_portal_url - unparsed_reason - conflicting_duplicates - supersedes_invoice_id - superseded_by_invoice_id properties: payload_omitted_reason: type: - string - "null" description: Proč poštář XML nepředal (např. `payload_too_large`). provider_portal_url: type: - string - "null" description: Odkaz na portál poskytovatele, kde lze XML stáhnout (jen u `xml_missing`). unparsed_reason: type: - string - "null" description: Důvod, proč metadata nejsou z XML (neznámý typ dokladu, nečitelné XML). conflicting_duplicates: type: array items: $ref: "#/components/schemas/ConflictingDuplicate" supersedes_invoice_id: type: - string - "null" format: uuid superseded_by_invoice_id: type: - string - "null" format: uuid encoding: description: Jak bylo určeno kódování přijatého XML; `null` u dokladů z poštáře a starších dokladů. oneOf: - $ref: "#/components/schemas/EncodingInfo" - type: "null" xml_sha256: type: string pattern: ^[0-9a-f]{64}$ xml_size_bytes: type: integer remote_id: type: - string - "null" description: ID u poštáře buyer_reference: type: - string - "null" description: BT-10 delivery_date: type: - string - "null" format: date billing_references: type: array description: BT-25 reference na předchozí faktury items: type: string note: type: - string - "null" totals: $ref: "#/components/schemas/Totals" lines: type: array items: $ref: "#/components/schemas/InvoiceLine" attachments: type: array items: $ref: "#/components/schemas/AttachmentMeta" validation: $ref: "#/components/schemas/ValidationResult" delivery_events: type: array description: Chronologicky vzestupně items: $ref: "#/components/schemas/DeliveryEvent" last_error: oneOf: - $ref: "#/components/schemas/ErrorSummary" - type: "null" links: $ref: "#/components/schemas/InvoiceLinks" InvoiceLine: type: object required: - line_id - name - quantity - unit_code - unit_price - line_net - vat_category - vat_rate properties: line_id: type: string name: type: string quantity: type: string unit_code: type: - string - "null" description: UN/ECE Rec 20 (BR-23) unit_price: $ref: "#/components/schemas/Money" line_net: $ref: "#/components/schemas/Money" vat_category: type: string vat_rate: type: - string - "null" InvoiceLinks: type: object required: - self - xml - html properties: self: type: string xml: type: string html: type: string InvoiceListItem: type: object required: - id - direction - source - status - validation_status - doc_type_code - invoice_number - issue_date - due_date - currency - seller - buyer - total_net - total_vat - total_gross - has_attachments - connection_id - invoice_uuid5 - external_reference - received_at - sent_at - delivered_at - created_at - xml_missing - metadata_incomplete - has_conflicting_duplicates - superseded properties: id: type: string format: uuid direction: $ref: "#/components/schemas/Direction" source: $ref: "#/components/schemas/InvoiceSource" status: $ref: "#/components/schemas/InvoiceStatus" validation_status: $ref: "#/components/schemas/ValidationStatus" doc_type_code: $ref: "#/components/schemas/DocumentTypeCode" invoice_number: type: string issue_date: type: string format: date due_date: type: - string - "null" format: date currency: type: string pattern: ^[A-Z]{3}$ seller: $ref: "#/components/schemas/Party" buyer: $ref: "#/components/schemas/Party" total_net: $ref: "#/components/schemas/Money" total_vat: $ref: "#/components/schemas/Money" total_gross: $ref: "#/components/schemas/Money" has_attachments: type: boolean connection_id: type: - string - "null" format: uuid invoice_uuid5: type: - string - "null" format: uuid description: UUID v5 dle Peppol SK architektury (ID-BDID-01); null dokud není ověřen jmenný prostor (viz OPEN-QUESTIONS) external_reference: type: - string - "null" description: Hodnota `X-External-Reference` / `metadata.external_reference` received_at: type: - string - "null" format: date-time sent_at: type: - string - "null" format: date-time delivered_at: type: - string - "null" format: date-time created_at: type: string format: date-time xml_missing: type: boolean description: "Záznam bez XML (stav `received_without_xml`): poštář XML nepředal, viz detail `payload_omitted_reason`/`provider_portal_url`." metadata_incomplete: type: boolean description: Metadata (typ, strany) se nepodařilo přečíst z XML; hodnoty jsou zástupné (`unparsed_reason`). has_conflicting_duplicates: type: boolean description: Existuje další doklad se stejným klíčem a JINÝM obsahem (původní XML zůstává beze změny). superseded: type: boolean description: Odeslání bylo nahrazeno novým pokusem (stejné číslo/datum po failed_permanent/rejected_by_provider). erp_deliveries: type: array description: "Příznak „převzato do ERP“ (ADR 015): integrace, které potvrdily převzetí příchozí faktury (ACK), s datem a časem (UTC). Prázdné = nepřevzato. Nemění XML ani hash." items: $ref: "#/components/schemas/ErpDelivery" InvoiceSource: type: string enum: - provider_poll - api_submit - manual_import InvoiceStatus: description: Sjednocení `OutboundStatus` a `InboundStatus`. oneOf: - $ref: "#/components/schemas/OutboundStatus" - $ref: "#/components/schemas/InboundStatus" Money: type: string description: Desetinné číslo jako řetězec, tečka, nejvýše 6 desetinných míst (částky 2, jednotkové ceny až 6). Záporné pro dobropisy jen tam, kde je v XML záporné. pattern: ^-?[0-9]{1,15}(\.[0-9]{1,6})?$ examples: - "1230.00" OutboundStatus: type: string enum: - queued - validating - sending - sent - delivered - delivery_failed - rejected_by_provider - failed_permanent Party: type: object required: - name - dic - ic_dph - ico - peppol_id - address properties: name: type: string dic: type: - string - "null" description: "DIČ (SK: 10 číslic)" ic_dph: type: - string - "null" description: IČ DPH, např. SK2020123457 ico: type: - string - "null" peppol_id: type: - string - "null" description: "`0245:` (z EndpointID schemeID=0245)" address: oneOf: - $ref: "#/components/schemas/Address" - type: "null" PartyFinding: type: object required: - code - severity - message properties: code: type: string description: Kód nálezu `@efakt/ids` (ID-*) severity: type: string enum: - error - warning - info message: type: string PartyPeppol: type: object required: - registered - provider - source - reason - checked_at properties: registered: type: - boolean - "null" provider: type: - string - "null" source: type: string enum: - smp - directory - cache - none reason: type: - string - "null" checked_at: type: string format: date-time PartyRegistry: type: object required: - found - dic_assigned - vat_status - regime - since - deleted_at - group_vat - cancellation_pending - person_kind - stale - degraded - as_of - sources properties: found: type: boolean dic_assigned: type: - boolean - "null" description: Je DIČ přiděleno (registr ds_dsrdp)? null = nelze určit vat_status: type: string enum: - registered - deleted - not_found - unknown regime: type: - string - "null" enum: - §4 - §4b - §5 - §7 - §7a - unknown - null since: type: - string - "null" format: date deleted_at: type: - string - "null" format: date group_vat: type: - boolean - "null" description: IČ DPH je sdílené (skupina DPH §4b) cancellation_pending: type: boolean description: Plátce je v seznamu s důvodem ke zrušení registrace (ds_dphz) person_kind: type: - string - "null" enum: - legal - natural - null stale: type: boolean description: Data jsou starší než nastavená čerstvost degraded: type: boolean description: Některý zdroj chybí / je příliš starý – "not_found" je nespolehlivé as_of: type: - string - "null" format: date-time sources: type: array items: $ref: "#/components/schemas/PartySource" PartySource: type: object required: - name - as_of - rows properties: name: type: string description: fs_ds_dphs | fs_ds_dsrdp | fs_ds_dphv | fs_ds_dphz as_of: type: string format: date-time description: Den stavu dat (DatumAktualizacieZoznamu, 00:00 UTC) rows: type: - integer - "null" PartyValidation: type: object required: - normalized - format - registry - consistency - peppol - name - address - attribution properties: normalized: type: object required: - ico - dic - ic_dph - peppol_id properties: ico: type: - string - "null" dic: type: - string - "null" ic_dph: type: - string - "null" peppol_id: type: - string - "null" description: "`0245:` (nikdy z IČ DPH)" format: type: object required: - ok - findings properties: ok: type: boolean findings: type: array items: $ref: "#/components/schemas/PartyFinding" registry: $ref: "#/components/schemas/PartyRegistry" consistency: type: object required: - icdph_vs_dic - ico_matches properties: icdph_vs_dic: type: - string - "null" enum: - match - mismatch - group - foreign_para5 - unknown - null ico_matches: type: - boolean - "null" peppol: oneOf: - $ref: "#/components/schemas/PartyPeppol" - type: "null" name: type: - string - "null" description: Jen právnická osoba a jen s include=name address: type: - string - "null" attribution: type: array items: type: string description: Povinné uvedení zdrojů / licencí Problem: type: object description: "RFC 9457 + `code`. `Content-Type: application/problem+json`." required: - type - title - status - code properties: type: type: string description: URI; konvence `https://efakt.local/problems/` title: type: string status: type: integer detail: type: string instance: type: string description: Cesta požadavku code: $ref: "#/components/schemas/ErrorCode" request_id: type: string errors: type: array description: Chyby polí (pro `BAD_REQUEST`, `INVALID_CREDENTIALS_FORMAT`, ...) items: $ref: "#/components/schemas/ProblemFieldError" findings: type: array description: Validační nálezy (pro `VALIDATION_FAILED` a portálové kontroly faktury) items: $ref: "#/components/schemas/ValidationFinding" existing_invoice_id: type: string format: uuid description: Jen u `DUPLICATE_INVOICE` a `IDEMPOTENCY_CONFLICT` retry_after_seconds: type: integer offset: type: integer minimum: 0 description: "Jen u `ENCODING_*`: bajtový offset prvního problematického bajtu/znaku ve vstupu." characters: type: array description: "Jen u `ENCODING_NOT_REPRESENTABLE`: znaky, které cílové kódování nemá (nikdy se nenahrazují)." items: $ref: "#/components/schemas/EncodingUnrepresentableChar" ProblemFieldError: type: object required: - pointer - message properties: pointer: type: string description: JSON Pointer do těla nebo `query:` / `header:` message: type: string TestRun: allOf: - $ref: "#/components/schemas/TestSubmitResponse" - type: object required: - endpoint properties: endpoint: type: string enum: - invoices - erp_invoices - erp_batch TestSimulate: type: string enum: - delivered - undeliverable - rejected - provider_error - timeout - late_issue default: delivered description: | Simulovaný průběh po (nevykonaném) odeslání: `delivered` (queued, sending, sent, delivered), `undeliverable` (delivery_failed RECIPIENT_NOT_REGISTERED), `rejected` (rejected_by_provider), `provider_error` (opakované dočasné chyby, pak failed_permanent RETRIES_EXHAUSTED), `timeout` (failed_permanent OUTCOME_UNKNOWN), `late_issue` (failed_permanent ISSUE_DATE_NOT_TODAY). TestSimulatedEvent: type: object required: - seq - at - type - from_status - to_status - reason_code - message - webhook_event properties: seq: type: integer minimum: 1 at: type: string format: date-time type: type: string enum: - status_changed - retry_scheduled - warning from_status: type: - string - "null" to_status: type: - string - "null" reason_code: type: - string - "null" message: type: string webhook_event: type: - string - "null" description: Událost webhooku, kterou by přechod vyvolal (`invoice.sent` ...). TestSubmitResponse: type: object description: Výsledek testovacího odeslání. Nikdy nevznikla faktura ani úloha; ID `test_` nelze použít u `/invoices/{id}`. required: - id - test - status - would_have_sent - forced_by - simulate - would_send - validation - encoding - simulated_events - webhooks - created_at - expires_at - links properties: id: type: string pattern: ^test_[0-9a-f-]{36}$ test: type: boolean enum: - true status: type: string enum: - test_accepted would_have_sent: type: boolean description: "`false` při blokujícím nálezu validace (ostré volání by selhalo)." forced_by: type: - string - "null" enum: - test_only_credential - null simulate: $ref: "#/components/schemas/TestSimulate" would_send: oneOf: - $ref: "#/components/schemas/TestWouldSend" - type: "null" validation: $ref: "#/components/schemas/ValidationReport" encoding: type: - object - "null" additionalProperties: true simulated_events: type: array items: $ref: "#/components/schemas/TestSimulatedEvent" webhooks: $ref: "#/components/schemas/TestWebhookResult" created_at: type: string format: date-time expires_at: type: string format: date-time links: type: object required: - self properties: self: type: string client_reference: type: - string - "null" TestWebhookResult: type: object required: - requested - endpoints - delivered - failed - completed properties: requested: type: boolean endpoints: type: integer delivered: type: integer failed: type: integer completed: type: boolean TestWouldSend: type: object required: - connection_id - provider_type - sender_peppol_id - receiver_peppol_id - route properties: connection_id: type: string format: uuid provider_type: type: string sender_peppol_id: type: string receiver_peppol_id: type: - string - "null" route: type: object required: - selected_by - environment - mock - recipient_check properties: selected_by: type: string enum: - request - integration_default - tenant_default environment: type: string enum: - sandbox - production mock: type: boolean recipient_check: type: string enum: - registered - not_registered - unknown - skipped Totals: type: object required: - currency - total_net - total_vat - total_gross - prepaid_amount - payable_amount - vat_breakdown properties: currency: type: string pattern: ^[A-Z]{3}$ total_net: $ref: "#/components/schemas/Money" total_vat: $ref: "#/components/schemas/Money" total_gross: $ref: "#/components/schemas/Money" prepaid_amount: oneOf: - $ref: "#/components/schemas/Money" - type: "null" payable_amount: $ref: "#/components/schemas/Money" vat_breakdown: type: array items: $ref: "#/components/schemas/VatBreakdownItem" ValidateInvoiceRequest: type: object required: - xml_base64 properties: xml_base64: type: string contentEncoding: base64 encoding: type: string maxLength: 40 description: Kódování bajtů v `xml_base64` (např. `windows-1250`); ekvivalent `charset` v Content-Type. Viz `EncodingInfo`. connection_id: type: string format: uuid options: type: object description: Požadavek smí nastavení jen ZPŘÍSNIT (nikdy nevypnout vrstvu ani nesnížit závažnost). properties: layers: type: array items: type: string description: Vrstvy, které se mají zapnout strict: type: boolean require_provider_dryrun: type: boolean provider_dryrun: type: string enum: - auto - manual - off - force locale: type: string enum: - sk - cs - en skip_recipient_lookup: type: boolean confidential_supply: type: boolean description: "`true` = utajované plnění => e-fakturu nelze vystavit (OBL-FORBIDDEN); `false` = potvrzeno, že není." recipient_sis_vo: type: boolean description: "`true` = příjemce je SIS / Vojenské spravodajství => zákaz; `false` = potvrzeno, že není." check_routing: type: boolean description: Výslovně ověřit příjemce v Peppolu (SMP / poštář). Bez něj se vrstva `routing` spouští jen při `routing_default=always`. ValidationDisagreement: type: object required: - layers - local - provider - rule_ids properties: layers: type: array items: type: string local: type: string enum: - ok - error provider: type: string enum: - ok - error rule_ids: type: array items: type: string ValidationFinding: type: object required: - rule_id - severity - message - location - source properties: rule_id: type: string description: Schematron pravidlo (`BR-O-02`, `BR-23`, `PEPPOL-EN16931-R001`, ...) nebo portálový kód z `ErrorCode` (`SELLER_MISMATCH`, `RECIPIENT_NOT_REGISTERED`, ...) severity: $ref: "#/components/schemas/FindingSeverity" message: type: string location: type: - string - "null" description: XPath do dokumentu source: $ref: "#/components/schemas/FindingSource" layer: type: string description: Vrstva předodesílací validace (format, sk, party_format, registry, routing, obligation, timing, tdd_sk, provider_dryrun, external, engine) messages: type: object description: Text nálezu ve všech jazycích; `message` je v jazyce `Accept-Language` (výchozí en). properties: sk: type: string cs: type: string en: type: string blocking: type: boolean description: Nález blokuje odeslání blocking_reason: type: string enum: - statutory - policy description: statutory = zákonný zákaz (nelze vypnout), policy = konfigurace/přísný režim overridable: type: boolean description: Lze závažnost přepsat konfigurací? code_class: type: string params: type: object description: Parametry nálezu; `alias` = alternativní kód (PTY-* pro ID-*, TIM-*, RTE-* pro starší kódy portálu) additionalProperties: type: - string - number - boolean - "null" ValidationLayerReport: type: object required: - name - status - duration_ms properties: name: type: string description: "Id vrstvy: `xsd_schematron`, `sk_national`, `party_format`, `registry`, `routing`, `obligation`, `timing`, `tdd_sk`, `provider_dryrun:`, `external:`" status: type: string enum: - ok - skipped - unavailable - error duration_ms: type: integer minimum: 0 as_of: type: string format: date-time description: Stáří dat vrstvy (např. registry) meta: type: object additionalProperties: true description: Důvod přeskočení (`reason`), verze artefaktů apod. ValidationObligation: type: object required: - decision - reasons properties: decision: type: string enum: - FORBIDDEN - MANDATORY - OPTIONAL - NOT_APPLICABLE - UNDETERMINED reasons: type: array items: type: string description: Kódy `OBL-*` ValidationRecipient: type: object required: - peppol_id - registered - source properties: peppol_id: type: - string - "null" registered: type: - boolean - "null" source: type: - string - "null" description: smp | adapter as_of: type: string format: date-time ValidationReport: type: object required: - verdict - can_send - mode - findings - layers - artefact_versions - disagreements - generated_at properties: verdict: type: string enum: - pass - pass_with_warnings - blocked can_send: type: boolean mode: type: string enum: - dryrun - enforce findings: type: array description: Řazeno error, warning, info. Texty podle `Accept-Language`, všechny jazyky v `messages`. items: $ref: "#/components/schemas/ValidationFinding" layers: type: array items: $ref: "#/components/schemas/ValidationLayerReport" artefact_versions: type: object additionalProperties: true description: Verze validátoru, Peppol BIS, SK pravidel, TDD, enginu obligation: $ref: "#/components/schemas/ValidationObligation" disagreements: type: array items: $ref: "#/components/schemas/ValidationDisagreement" recipient: $ref: "#/components/schemas/ValidationRecipient" encoding: $ref: "#/components/schemas/EncodingInfo" generated_at: type: string format: date-time ValidationResult: type: object required: - status - error_count - warning_count - validated_at - findings properties: status: $ref: "#/components/schemas/ValidationStatus" error_count: type: integer warning_count: type: integer validated_at: type: - string - "null" format: date-time findings: type: array items: $ref: "#/components/schemas/ValidationFinding" ValidationRouting: type: object required: - dic - peppol_id - registered - source - as_of properties: dic: type: string peppol_id: type: string registered: type: - boolean - "null" description: "`null` = nelze zjistit (SMP vypnuto a poštář bez lookup, nebo výpadek)" source: type: - string - "null" description: smp | adapter as_of: type: - string - "null" format: date-time provider: type: string description: Poštář příjemce podle SMP (je-li znám) access_point_url: type: string reason: type: string description: Proč `registered=null` (smp_disabled, unavailable, no_lookup_source, invalid_dic) ValidationStatus: type: string description: pending = ještě nevalidováno; valid; valid_with_warnings; invalid. enum: - pending - valid - valid_with_warnings - invalid VatBreakdownItem: type: object required: - category - rate - taxable_amount - tax_amount - exemption_reason_code - exemption_reason properties: category: type: string description: S, E, K, G, AE, O, Z, ... rate: type: - string - "null" description: Sazba v % jako řetězec, null u kategorií bez sazby (O) taxable_amount: $ref: "#/components/schemas/Money" tax_amount: $ref: "#/components/schemas/Money" exemption_reason_code: type: - string - "null" description: VATEX kód exemption_reason: type: - string - "null" responses: BadRequest: description: Chybný požadavek. Validace těla, dotazu i cesty = vždy `400 BAD_REQUEST` s `errors[]` (ADR 005 bod 2); dále `INVALID_CURSOR`, `IDEMPOTENCY_KEY_REQUIRED`. content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Conflict: description: Konflikt (`TENANT_REQUIRED` = není zvolen aktivní tenant, `IDEMPOTENCY_CONFLICT`, `IDEMPOTENCY_IN_PROGRESS`, `DUPLICATE_INVOICE`, `CONFLICT`, `EMAIL_ALREADY_EXISTS`, `CONNECTION_DISABLED`, `EXPORT_NOT_READY`, ...) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Forbidden: description: Nedostatečná práva (`FORBIDDEN`, `INSUFFICIENT_SCOPE`, `CSRF_FAILED`, `TENANT_SUSPENDED`) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" NotFound: description: Nenalezeno (včetně záznamů cizího tenanta) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" PayloadTooLarge: description: Tělo přesahuje limit (`PAYLOAD_TOO_LARGE`) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" ServiceUnavailable: description: Dočasně nedostupné (`VALIDATOR_UNAVAILABLE`, `SERVICE_UNAVAILABLE`); nic nebylo uloženo ani odesláno. headers: Retry-After: $ref: "#/components/headers/RetryAfter" content: application/problem+json: schema: $ref: "#/components/schemas/Problem" TooManyRequests: description: Rate limit (`RATE_LIMITED`) headers: Retry-After: $ref: "#/components/headers/RetryAfter" RateLimit-Limit: $ref: "#/components/headers/RateLimitLimit" RateLimit-Remaining: $ref: "#/components/headers/RateLimitRemaining" RateLimit-Reset: $ref: "#/components/headers/RateLimitReset" content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Unauthorized: description: Nepřihlášen / neplatný klíč (`UNAUTHENTICATED`, `INVALID_CREDENTIALS`, `TOTP_INVALID`, `API_KEY_INVALID`, ...) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" Unprocessable: description: | Sémantická chyba (syntakticky správný požadavek, který nelze provést; validace tvaru je `400`). U faktur `findings` obsahuje konkrétní pravidla (např. `BR-O-02`, `BR-23`) a `code` je `VALIDATION_FAILED`, nebo specifický portálový kód (`SELLER_MISMATCH`, `ISSUE_DATE_NOT_TODAY`, ...). content: application/problem+json: schema: $ref: "#/components/schemas/Problem" UnsupportedMediaType: description: Nepodporovaný Content-Type (`UNSUPPORTED_MEDIA_TYPE`) content: application/problem+json: schema: $ref: "#/components/schemas/Problem" parameters: IdempotencyKey: name: Idempotency-Key in: header required: true description: Klient generuje unikátní hodnotu (doporučeno UUID v4) pro každý logický pokus o odeslání. 1–255 znaků `[A-Za-z0-9._:-]`. schema: type: string minLength: 1 maxLength: 255 pattern: ^[A-Za-z0-9._:-]+$ InvoiceId: name: invoiceId in: path required: true schema: type: string format: uuid SimulateQuery: name: simulate in: query description: "Jen s `test=true`: simulovaný výsledek (výchozí `delivered`)." schema: $ref: "#/components/schemas/TestSimulate" SimulateWebhooksQuery: name: simulate_webhooks in: query description: "Jen s `test=true`: rozeslat webhookům tenanta testovací události (`test:true`, hlavička `X-Test: 1`). Výchozí vypnuto." schema: type: boolean TestModeHeader: name: X-Test-Mode in: header description: Viz parametr `test`. schema: type: string enum: - "true" - "false" - "1" - "0" TestQuery: name: test in: query description: "Testovací režim: provede se vše kromě odeslání (nic se neodešle ani neuloží). Hodnoty `true|false|1|0`, jiné = `400 INVALID_TEST_FLAG`. Totéž hlavička `X-Test-Mode` a pole `test` v JSON; při rozporu vyhrává `true`." schema: type: string enum: - "true" - "false" - "1" - "0" headers: RateLimitLimit: description: Limit požadavků v okně schema: type: integer RateLimitRemaining: description: Zbývající požadavky v okně schema: type: integer RateLimitReset: description: Sekundy do resetu okna schema: type: integer RetryAfter: description: Sekundy, po kterých lze zkusit znovu schema: type: integer XTestMode: description: Vždy `true` u testovací odpovědi schema: type: string enum: - "true" XWouldHaveSent: description: "`true`, pokud by ostré volání prošlo; `false` při blokujícím nálezu" schema: type: string enum: - "true" - "false" security: - erpBearer: [] - erpBasic: []