Skip to main content

Poland — KSeF Setup and Receipts

KSeF (Krajowy System e-Faktur) is Poland's national e-invoicing system, run by the Ministry of Finance. GoRoute's Poland pack sends your invoices to KSeF for you. This page covers the setup calls and the receipt call:

PUT    /api/v1/jurisdictions/{code}/settings
POST /api/v1/jurisdictions/pl/token
GET /api/v1/jurisdictions/pl/status
DELETE /api/v1/jurisdictions/pl/token
GET /api/v1/jurisdictions/pl/transactions/{transaction_id}/upo

The first is the generic settings call, with PL as the code. Paths are relative to https://app.goroute.ai/peppol-api. For the mandate dates and the Polish VAT rates GoRoute accepts, see the Poland country guide.

On production, your invoices go to KSeF's production environment

On GoRoute's production service, invoices go to KSeF production. There they are legal invoices with tax effect. On a test service they go to KSeF's test environment, which has no legal effect.

Check which environment your service uses before you send anything. Call GET /api/v1/jurisdictions/pl/status and read ksef_environment: production, test or demo. If the call answers 404, the Poland pack is not switched on for the service you are calling.

Who the pack applies to​

The pack is opt-in. It applies only to an organisation that meets all of these:

  • Its country is PL. The country is set when your organisation is created. If it is wrong, contact GoRoute support.
  • It has enrolled: its Poland settings say "enabled": true.

For such an organisation, every invoice and credit note it sends goes through the pack, apart from consumer sales when those are switched off. Until its NIP and legal name are saved, its sends are refused (C5_PL_NOT_READY). Until a KSeF token is connected, its invoices are accepted but wait (C5_PL_TOKEN_MISSING). Documents it receives are not sent to KSeF: the issuer files those.

Set up​

Setup is two calls, in this order. Both need the settings:manage or the authority:manage permission.

1. Save your settings​

curl -X PUT \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "nip": "1112223332", "legal_name": "Przykładowa Firma Sp. z o.o.", "consumer_invoices": false}' \
"https://app.goroute.ai/peppol-api/api/v1/jurisdictions/PL/settings"

The settings hold exactly four fields:

FieldTypeMeaning
enabledbooleanEnrols the organisation in the Poland pack. Default false.
nipstringYour NIP, Poland's 10-digit tax number. A PL prefix, spaces, dots and dashes are removed before it is stored. A NIP whose check digit is wrong is refused.
legal_namestringYour business name as KSeF knows it. Up to 256 characters.
consumer_invoicesbooleanWhether sales to consumers are sent to KSeF too. KSeF is voluntary for consumer invoices. Default false.

The call replaces your Poland settings as a whole, so send all four fields every time. A field of the wrong type, an invalid NIP or any other key is refused with 422 JURISDICTION_SETTINGS_INVALID and an issues list. For the generic settings call, see Jurisdiction settings.

The NIP in this example is made up. It passes the check digit, but it does not belong to any registered business.

2. Connect your KSeF token​

You create a KSeF token in KSeF itself, for the NIP you saved. It needs the InvoiceWrite and InvoiceRead permissions.

curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"token": "YOUR_KSEF_TOKEN"}' \
"https://app.goroute.ai/peppol-api/api/v1/jurisdictions/pl/token"

The token must be 10 to 512 characters. GoRoute first signs in to KSeF with it, for your NIP. Only if KSeF accepts it is the token stored, encrypted. GoRoute never returns the token, in this answer or any other.

{
"connected": true,
"nip": "1112223332",
"ksef_environment": "production",
"peppol_id": "9945:PL1112223332",
"identity_adopted": true
}
  • ksef_environment is the KSeF environment your invoices will go to.
  • peppol_id and identity_adopted: a KSeF token proves that your organisation controls the NIP. So if your organisation had no sending identity, it gets 9945:PL<NIP>, and identity_adopted is true. If it already had the same identity, that is confirmed (true). If it already had a different identity, that one is kept, peppol_id shows it, and identity_adopted is false.
  • This identity is not registered on the Peppol network. Nobody can send you documents over Peppol through it.

If you connect a new token, it replaces the old one.

Refusals. Each is a 422 with this body:

{
"error": "ksef_setup",
"error_code": "C5_PL_NOT_READY",
"message": "KSeF setup is not complete for this account; finish setup in the app. (save the business NIP first)"
}
error_codeWhen
C5_PL_NOT_READYNo NIP is saved yet. Do step 1 first.
C5_PL_TOKEN_INVALIDKSeF refused the token: revoked, expired or made for another NIP.
C5_PL_TOKEN_NO_PERMISSIONThe token lacks InvoiceWrite or InvoiceRead.
C5_PL_AUTH_BLOCKEDKSeF has blocked sign-in for this token or NIP.
C5_PL_KSEF_UNREACHABLEKSeF could not be reached. Try again later.
C5_PL_RATE_LIMITEDKSeF asked GoRoute to slow down. Try again shortly.

The full sentence for each code is in Reason codes.

Check your setup​

curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.goroute.ai/peppol-api/api/v1/jurisdictions/pl/status"

Any valid API key of your organisation can call it. No special permission.

{
"ready": true,
"missing": null,
"nip": "1112223332",
"legal_name": "Przykładowa Firma Sp. z o.o.",
"consumer_invoices": false,
"ksef_environment": "production",
"peppol_id": "9945:PL1112223332",
"token": {"connected": true, "connected_at": "2026-10-22T08:15:00+00:00"},
"invoices": {"acknowledged": 41, "pending": 1}
}
FieldMeaning
readytrue when setup is complete and invoices can be sent to KSeF.
missingnull when ready. Otherwise the first thing missing, as one of these sentences: "KSeF is not enabled for this account.", "The business NIP is missing.", "The business name is missing." or "Connect a KSeF token."
nip, legal_name, consumer_invoicesYour stored settings.
ksef_environmentThe KSeF environment invoices go to: production, test or demo.
peppol_idYour organisation's sending identity, or null.
token.connected, token.connected_atWhether a token is stored, and when it was connected.
invoicesHow many of your invoices are in each KSeF state. The states are listed under What happens when you send. A state with no invoices is left out.

Remove your token​

curl -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
"https://app.goroute.ai/peppol-api/api/v1/jurisdictions/pl/token"

Needs settings:manage or authority:manage. The stored token is erased:

{"connected": false, "revoked": 1}

revoked is the number of stored tokens erased: 0 if none was stored. After this, invoices you send are not lost: they wait (state held, reason C5_PL_TOKEN_MISSING) and are sent once you connect a new token. Removing the token here does not revoke it in KSeF. Do that in KSeF if you no longer want it used.

Download KSeF's receipt​

When KSeF accepts an invoice, it issues a signed receipt, the UPO (Urzędowe Poświadczenie Odbioru). GoRoute keeps it with the transaction.

curl -H "X-API-Key: YOUR_API_KEY" -o upo.xml \
"https://app.goroute.ai/peppol-api/api/v1/jurisdictions/pl/transactions/TRANSACTION_ID/upo"

The answer is the UPO as XML (application/xml), as a file named UPO-<transaction id>.xml.

It answers 404 when:

  • the transaction does not exist or belongs to another organisation; or
  • no receipt is kept for it. That is the case before KSeF has accepted the invoice. It is also the case if GoRoute could not download the receipt at the moment KSeF accepted the invoice: GoRoute does not try again later. The KSeF number is recorded either way.

What happens when you send​

You send invoices and credit notes the usual way, with POST /api/v1/invoices or POST /api/v1/documents. For an organisation the pack applies to:

  1. At send, GoRoute builds a KSeF FA(3) invoice from your document and checks it against the Ministry's schema. A credit note becomes a corrective invoice (KOR). If the FA(3) invoice cannot be built, the send is refused and nothing is created. The refusal says which field is wrong; see Refusals at send.
  2. Sales to consumers are not sent to KSeF unless consumer_invoices is true. GoRoute treats a sale as a consumer sale when the buyer has no tax or company identifier on the document.
  3. A currency other than PLN is allowed. GoRoute states the VAT in złoty at the National Bank of Poland's average rate from the last table before the issue date.
  4. A credit note must name the invoice it corrects, and that invoice must already have a KSeF number. Until it has one, the credit note is refused (C5_PL_CORRECTION_REFERENCE_REQUIRED). Send it again once KSeF has accepted the invoice.
  5. Afterwards, GoRoute sends the FA(3) invoice to KSeF with your token. It keeps trying until KSeF answers; an invoice is never given up on because of the number of attempts. When KSeF accepts it, GoRoute records the KSeF number and the UPO, and the invoice PDF gets the KSeF number and its verification QR code (KOD I).

Where it goes. With the default destination (peppol), the invoice also goes to your buyer the usual way. With "destination": "authority", it goes to KSeF only and to no buyer. A consumer sale with consumer_invoices off is not sent to KSeF, so an authority-only send of one is refused (400). See the destination field.

Where to read the result. GET /api/v1/transactions/{transaction_id} carries an authority object. Poland's entry is authority.PL:

"authority": {
"PL": {
"regime": "PL",
"status": "acknowledged",
"ksef_number": "KSEF_NUMBER_FROM_KSEF",
"reason_code": null,
"reason": null
}
}

ksef_number is the number KSeF gave the invoice. GoRoute shows it as KSeF issued it. The status is one of:

statusMeaning
pendingWaiting to be sent, or KSeF is still processing it, or KSeF was unreachable and GoRoute will try again.
heldWaiting for a usable KSeF token. reason_code says why. It is sent once you connect a new token.
acknowledgedKSeF accepted the invoice. ksef_number is set.
rejectedKSeF rejected the invoice. reason_code is C5_PL_KSEF_REJECTED and reason is that code's fixed sentence; it does not include KSeF's own message. Fix the document and issue it again.
failedGoRoute could not send the invoice, for example because the FA(3) invoice could not be rebuilt. reason says why.

Refusals at send​

POST /api/v1/invoices answers a refusal from the Poland pack with 422 JURISDICTION_REFUSED, with regime PL and the reason_code. With "destination": "authority" the same refusal is a 400 AUTHORITY_REFUSED. POST /api/v1/documents answers it with 400 INVALID_REQUEST. In each case the message carries the reason code and its sentence.

Reason codes​

These are the codes the Poland pack uses, with the sentence GoRoute shows for each.

CodeSentence
C5_PL_NOT_READYKSeF setup is not complete for this account; finish setup in the app.
C5_PL_NIP_REQUIREDThis invoice needs the buyer's NIP or tax ID; add it to the order and issue again.
C5_PL_RECORD_REFUSEDThe invoice could not be built as a KSeF FA(3) invoice; the message says which field, fix the order and issue again.
C5_PL_CURRENCY_RATE_REQUIREDThe order is not in PLN and no exchange rate to PLN was available; KSeF needs the tax in PLN.
C5_PL_VAT_RATE_UNSUPPORTEDThe order charges a VAT rate that is not a Polish rate (23, 8 or 5 %, or 0 %); set the store's tax for Poland and issue again.
C5_PL_NBP_RATE_UNAVAILABLEThe NBP exchange rate to PLN could not be read just now; issue the invoice again in a few minutes.
C5_PL_CORRECTION_REFERENCE_REQUIREDA corrective invoice needs the KSeF number of the invoice it corrects; it is issued once that invoice has been accepted by KSeF.
C5_PL_CONSUMER_NOT_ENABLEDA consumer sale: KSeF is optional for consumers and switched off for this account, so nothing is sent.
C5_PL_TOKEN_MISSINGNo KSeF token is connected; connect one in the app. Invoices wait until then.
C5_PL_TOKEN_INVALIDKSeF refused the token (revoked, expired or for another NIP); connect a new token. Invoices wait and are sent once it is connected.
C5_PL_TOKEN_NO_PERMISSIONThe KSeF token lacks the invoice permissions; create one with InvoiceWrite and InvoiceRead.
C5_PL_AUTH_BLOCKEDKSeF has blocked sign-in for this token or NIP; check the token in the Ministry's KSeF app and connect a new one. Invoices wait until then.
C5_PL_KSEF_REJECTEDKSeF rejected the invoice; the KSeF message says why. Fix the order and issue again.
C5_PL_KSEF_UNREACHABLEKSeF could not be reached; the invoice is kept and sent automatically when KSeF answers.
C5_PL_RATE_LIMITEDKSeF asked us to slow down; the invoice is sent automatically shortly.
C5_PL_RECEIVEDA received invoice is filed in KSeF by its issuer, not by the buyer: nothing to send.
C5_PL_NOT_RECORDEDThis document was issued before KSeF was active for the account, so nothing is sent through GoRoute.

The sentences are written for merchants using a store app, so some say "in the app". Through the API, "the app" means the calls on this page. The C5_PL_VAT_RATE_UNSUPPORTED sentence is shorter than the rule: GoRoute accepts standard-rated lines at 23, 22, 8, 7 or 5 %. The country guide has the full list.

API Reference​

The generated reference for each call: