πŸ“–
KSeF API 2.0

KSeF API 2.0 Guide

Complete technical reference for integration with the Polish Ministry of Finance KSeF API

Test my invoices

Disclaimer

This guide is provided for informational purposes and is based on the KSeF API 2.0 documentation and our real-world tests on the TEST environment. Specifications may change. Always consult the official Polish Ministry of Finance documentation for the most up-to-date information.

1. Environments

KSeF provides three distinct environments. Always use TEST or DEMO for development.

EnvironmentURLPurpose
TESThttps://api-test.ksef.mf.gov.pl/v2Free testing, fictitious NIPs
DEMOhttps://api-demo.ksef.mf.gov.pl/v2Demonstration
PRODhttps://api.ksef.mf.gov.pl/v2Production (mandatory since 01/02/2026)

2. Authentication

Each API session requires token authentication. The full flow uses RSA-OAEP encryption of the token with the KSeF public key.

Full flow (API token)

1. GET  /security/public-key-certificates     β†’ RSA keys
2. POST /auth/challenge                        β†’ challenge + timestampMs
3. RSA-OAEP(token|timestampMs)                 β†’ encrypt with "KsefTokenEncryption" key
4. POST /auth/ksef-token                       β†’ challenge + encryptedToken
   β†’ authenticationToken + referenceNumber (HTTP 202)
5. GET  /auth/{referenceNumber}                β†’ poll until code=200
6. POST /auth/token/redeem                     β†’ exchange β†’ accessToken

Required headers for authenticated calls

Authorization: Bearer {accessToken}
Accept: application/json
Content-Type: application/json

Public keys

GET /security/public-key-certificates returns 2 certificates:

UsageUsage
KsefTokenEncryptionToken encryption during authentication
SymmetricKeyEncryptionSession AES key encryption

No fingerprint field in the response (contrary to the documentation).

3. Online Session (interactive)

For sending invoices without attachments, one at a time.

1. POST /sessions/online                           β†’ open session
2. POST /sessions/online/{ref}/invoices             β†’ send invoice
3. GET  /sessions/{ref}/invoices/{invoiceRef}        β†’ check status
4. POST /sessions/online/{ref}/close                β†’ close session

Opening a session

{
  "formCode": {"systemCode": "FA (3)", "schemaVersion": "1-0E", "value": "FA"},
  "encryption": {
    "encryptedSymmetricKey": "RSA-OAEP(aes_key, cert_SymmetricKeyEncryption)",
    "initializationVector": "base64(iv_16_bytes)"
  }
}

The client generates the AES-256 key and IV, encrypts the key with RSA, and sends both.

Sending an invoice

{
  "invoiceHash": "base64(SHA256(xml_raw))",
  "invoiceSize": 2048,
  "encryptedInvoiceHash": "base64(SHA256(xml_encrypted))",
  "encryptedInvoiceSize": 2064,
  "encryptedInvoiceContent": "base64(AES-256-CBC(xml_raw))"
}

AES-256-CBC encryption with PKCS#7 padding. Same key/IV as the session.

4. Batch Session (wsadowa)

For sending invoices with or without attachments, in bulk (ZIP).

1. Create ZIP containing XML invoices (faktura_1.xml, faktura_2.xml, ...)
2. Encrypt ZIP with AES-256-CBC + PKCS#7 (NO IV prepend)
3. POST /sessions/batch           β†’ open session, receive upload URL
4. PUT  {uploadUrl}               β†’ upload encrypted ZIP (raw bytes)
5. POST /sessions/batch/{ref}/close  β†’ close and start processing
6. GET  /sessions/{ref}           β†’ poll session status
7. GET  /sessions/{ref}/invoices  β†’ accepted invoices
8. GET  /sessions/{ref}/invoices/failed β†’ rejected invoices

Opening a batch session

{
  "formCode": {"systemCode": "FA (3)", "schemaVersion": "1-0E", "value": "FA"},
  "batchFile": {
    "fileSize": 1078,
    "fileHash": "base64(SHA256(zip_raw))",
    "fileParts": [{
      "ordinalNumber": 1,
      "fileSize": 1088,
      "fileHash": "base64(SHA256(zip_encrypted))"
    }]
  },
  "encryption": {
    "encryptedSymmetricKey": "...",
    "initializationVector": "..."
  }
}
FieldValue
batchFile.fileSizeZIP size before encryption
batchFile.fileHashSHA256 of ZIP before encryption
fileParts[].fileSizeZIP size after encryption
fileParts[].fileHashSHA256 of ZIP after encryption

Upload: the response contains partUploadRequests with URL, method (PUT) and headers (x-ms-blob-type: BlockBlob). Send raw encrypted bytes, without Authorization header.

Each invoice is processed independently. An error on one invoice does NOT block the others.

Contrary to a common misconception, batch processing is not atomic: valid invoices receive their ksefNumber even if other invoices in the same batch are rejected.

Per-invoice results

After closing the batch session, each invoice is processed independently. Check results via two separate endpoints.

GET /sessions/{ref}/invoices          β†’ accepted invoices (with ksefNumber)
GET /sessions/{ref}/invoices/failed   β†’ rejected invoices (with processingCode + error)

Processing is parallel: accepted and rejected invoices may appear in a different order than in the ZIP.

5. Processing Codes

API responses use processing codes (processingCode) to indicate request status. The same code may have slightly different meanings depending on the endpoint (auth, session, invoice, export).

Progression (100-299)

CodeMeaning
100Process registered / accepted for processing
150Processing in progress (replaces former code 300 since RC3)
170Processing completed
200Success β†’ ksefNumber available, session processed, export completed
210Export expired, no longer available for download

Errors (400-550)

CodeMeaning
400Operation failed, request rejected
405Package verification error (sanity check) / session error
410Inconsistent identifiers, invalid permission scope
415Missing permissions, key decryption error, attachments not allowed
420Filters too broad, invoice limit exceeded, permission denied
425Token invalidated / invalid file hash
429Rate limit exceeded (~100/s, ~300/min, ~1200/h)
430Decompression error, invoice verification error, missing role
435File decryption error (incorrect encryption)
440Session cancelled (upload timeout), duplicate invoice, forbidden operation
441Invoice processing sub-process verification error
445Finalization error, no valid invoice found
450Invalid token, invoice structure error, invalid XML
460Certificate failure (invalid, untrusted chain, revoked)
470Attempt to use authorization methods of a deceased person
480Auth blocked: suspected security incident (contact MF via form)
500Unknown server error
550Operation cancelled by system (atomic, no partial processing). Retry.

Source : Issue #526, OpenAPI spec, api-changelog.md

6. Limits

ParameterValue
Max invoice size without attachments1 MB
Max invoice size with attachments3 MB (configurable up to 10 MB)
Max invoices per batch session10 000 000
Max ZIP size5 GB
Max parts per ZIP50 (100 MB each)
Unclosed batch session timeout12h β†’ auto cancellation (code 440)

Verifiable via GET /limits/context.

7. Attachments (Zalacznik)

Prerequisites

  • 1.Activation : POST /testdata/attachment with {"nip": "..."} (TEST env). In PROD: declaration via e-UrzΔ…d Skarbowy.
  • 2.Batch session required : online mode rejects attachments (code 415).

Verification

GET /permissions/attachments/status (authenticated)

{"isAttachmentAllowed": true}
// or with revocation date:
{"isAttachmentAllowed": true, "revokedDate": "2026-12-31T23:59:59+00:00"}

XML Structure

'<Zalacznik>' is a direct child of '<Faktura>' (not '<Fa>'), with structured content:

<Faktura xmlns="http://crd.gov.pl/wzor/2025/06/25/13775/">
  <Naglowek>...</Naglowek>
  <Podmiot1>...</Podmiot1>
  <Podmiot2>...</Podmiot2>
  <Fa>...</Fa>
  <Zalacznik>
    <BlokDanych>
      <MetaDane>
        <ZKlucz>Key</ZKlucz>
        <ZWartosc>Value</ZWartosc>
      </MetaDane>
      <Tabela>
        <Opis>Table description</Opis>
        <TNaglowek>
          <Kol Typ="txt"><NKom>Column 1</NKom></Kol>
          <Kol Typ="int"><NKom>Column 2</NKom></Kol>
        </TNaglowek>
        <Wiersz>
          <WKom>Value 1</WKom>
          <WKom>42</WKom>
        </Wiersz>
      </Tabela>
    </BlokDanych>
  </Zalacznik>
</Faktura>

Column types: txt, int, dec, date.

Attachment error codes

CodeModeMessage
415Online"Wysylanie faktury z zalacznikiem w trybie interaktywnym nie jest dozwolone"
410Batch"Sprzedawca {NIP} nie posiada zgody do wysylania faktur z zalacznikami"

8. Testdata endpoints (TEST env)

No authentication required. These endpoints allow configuring the test environment.

EndpointMethodStatusNotes
/testdata/subjectPOSTOK{"subjectNip": "...", "description": "..."}
/testdata/personPOSTOK{"nip": "...", "pesel": "...", "description": "..."}
/testdata/attachmentPOSTOK{"nip": "..."} β€” enables attachments
/testdata/attachment/revokePOSTOK{"nip": "..."} β€” revocation not immediate
/testdata/permissionsPOST500Broken since KSeF 2.0 launch

9. Information endpoints

Authenticated endpoints to check the status of your account and invoices.

EndpointDescription
/permissions/attachments/statusAttachment authorization status
/limits/contextSession limits (max size, etc.)
/rate-limitsCurrent rate limits
/auth/sessionsActive sessions
/invoices/query/metadataInvoice search (hasAttachment filter)
/invoices/ksef/{ksefNumber}Download an invoice by KSeF number

10. Known Issues

Points of attention when integrating with the KSeF API, based on our real-world tests:

ProblemSourceDetails
POST /testdata/permissions returns 500#296Broken since KSeF 2.0 launch. Even with the exact OpenAPI schema format, the server returns 500 Internal Server Error.
Attachment revocation not immediateTests 04/02/2026After calling /testdata/attachment/revoke, isAttachmentAllowed remains true with a future revokedDate.
TEST server slow (prolonged code 150)#408, #278Post-launch, processing may remain at code 150 ("Processing in progress") longer than expected.
Attachment rejected in online mode (415)#674Not a bug: attachments require a batch session. Online mode returns 415.

Need help with KSeF integration?

We support you with API integration: authentication, invoice submission, error handling, and production rollout.

Disclaimer

This guide is provided for informational purposes and is based on the KSeF API 2.0 documentation and our real-world tests on the TEST environment. Specifications may change. Always consult the official Polish Ministry of Finance documentation for the most up-to-date information.