KSeF API 2.0 Guide
Complete technical reference for integration with the Polish Ministry of Finance KSeF API
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.
| Environment | URL | Purpose |
|---|---|---|
| TEST | https://api-test.ksef.mf.gov.pl/v2 | Free testing, fictitious NIPs |
| DEMO | https://api-demo.ksef.mf.gov.pl/v2 | Demonstration |
| PROD | https://api.ksef.mf.gov.pl/v2 | Production (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 β accessTokenRequired headers for authenticated calls
Authorization: Bearer {accessToken}
Accept: application/json
Content-Type: application/jsonPublic keys
GET /security/public-key-certificates returns 2 certificates:
| Usage | Usage |
|---|---|
KsefTokenEncryption | Token encryption during authentication |
SymmetricKeyEncryption | Session 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 sessionOpening 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 invoicesOpening 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": "..."
}
}| Field | Value |
|---|---|
batchFile.fileSize | ZIP size before encryption |
batchFile.fileHash | SHA256 of ZIP before encryption |
fileParts[].fileSize | ZIP size after encryption |
fileParts[].fileHash | SHA256 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)
| Code | Meaning |
|---|---|
| 100 | Process registered / accepted for processing |
| 150 | Processing in progress (replaces former code 300 since RC3) |
| 170 | Processing completed |
| 200 | Success β ksefNumber available, session processed, export completed |
| 210 | Export expired, no longer available for download |
Errors (400-550)
| Code | Meaning |
|---|---|
| 400 | Operation failed, request rejected |
| 405 | Package verification error (sanity check) / session error |
| 410 | Inconsistent identifiers, invalid permission scope |
| 415 | Missing permissions, key decryption error, attachments not allowed |
| 420 | Filters too broad, invoice limit exceeded, permission denied |
| 425 | Token invalidated / invalid file hash |
| 429 | Rate limit exceeded (~100/s, ~300/min, ~1200/h) |
| 430 | Decompression error, invoice verification error, missing role |
| 435 | File decryption error (incorrect encryption) |
| 440 | Session cancelled (upload timeout), duplicate invoice, forbidden operation |
| 441 | Invoice processing sub-process verification error |
| 445 | Finalization error, no valid invoice found |
| 450 | Invalid token, invoice structure error, invalid XML |
| 460 | Certificate failure (invalid, untrusted chain, revoked) |
| 470 | Attempt to use authorization methods of a deceased person |
| 480 | Auth blocked: suspected security incident (contact MF via form) |
| 500 | Unknown server error |
| 550 | Operation cancelled by system (atomic, no partial processing). Retry. |
Source : Issue #526, OpenAPI spec, api-changelog.md
6. Limits
| Parameter | Value |
|---|---|
| Max invoice size without attachments | 1 MB |
| Max invoice size with attachments | 3 MB (configurable up to 10 MB) |
| Max invoices per batch session | 10 000 000 |
| Max ZIP size | 5 GB |
| Max parts per ZIP | 50 (100 MB each) |
| Unclosed batch session timeout | 12h β auto cancellation (code 440) |
Verifiable via GET /limits/context.
7. Attachments (Zalacznik)
Prerequisites
- 1.Activation :
POST /testdata/attachmentwith {"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
| Code | Mode | Message |
|---|---|---|
| 415 | Online | "Wysylanie faktury z zalacznikiem w trybie interaktywnym nie jest dozwolone" |
| 410 | Batch | "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.
| Endpoint | Method | Status | Notes |
|---|---|---|---|
/testdata/subject | POST | OK | {"subjectNip": "...", "description": "..."} |
/testdata/person | POST | OK | {"nip": "...", "pesel": "...", "description": "..."} |
/testdata/attachment | POST | OK | {"nip": "..."} β enables attachments |
/testdata/attachment/revoke | POST | OK | {"nip": "..."} β revocation not immediate |
/testdata/permissions | POST | 500 | Broken since KSeF 2.0 launch |
9. Information endpoints
Authenticated endpoints to check the status of your account and invoices.
| Endpoint | Description |
|---|---|
/permissions/attachments/status | Attachment authorization status |
/limits/context | Session limits (max size, etc.) |
/rate-limits | Current rate limits |
/auth/sessions | Active sessions |
/invoices/query/metadata | Invoice 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:
| Problem | Source | Details |
|---|---|---|
| POST /testdata/permissions returns 500 | #296 | Broken since KSeF 2.0 launch. Even with the exact OpenAPI schema format, the server returns 500 Internal Server Error. |
| Attachment revocation not immediate | Tests 04/02/2026 | After calling /testdata/attachment/revoke, isAttachmentAllowed remains true with a future revokedDate. |
| TEST server slow (prolonged code 150) | #408, #278 | Post-launch, processing may remain at code 150 ("Processing in progress") longer than expected. |
| Attachment rejected in online mode (415) | #674 | Not a bug: attachments require a batch session. Online mode returns 415. |
Useful Links
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.