KSeF

KSeF webhooks: automating invoice status end to end

How Notaly webhooks deliver KSeF status asynchronously: invoice.accepted, invoice.rejected and invoice.error, HMAC signing and retries.

Published 3 min readBy Tomek

Overview

KSeF decides whether an invoice is accepted asynchronously, so the submit call alone is not the end of the story: Notaly polls KSeF for you and pushes the final result to your endpoint as a signed webhook — invoice.accepted (with the KSeF number and UPO), invoice.rejected, or invoice.error. Each webhook is signed with HMAC and retried with backoff, so you can build reliable order-to-invoice automation without polling yourself.

Why webhooks fit KSeF

KSeF (Krajowy System e-Faktur) is Poland's national e-invoicing platform operated by the Ministry of Finance (Ministerstwo Finansów). When you submit a FA(3) invoice, the platform returns a submitted status and processes the document afterward. Polling for that result yourself means writing a background worker; a webhook inverts that, so the result comes to you the moment it is known.

The events you receive

Notaly sends three main events. invoice.accepted means KSeF accepted the invoice and the payload includes the KSeF number, the UPO and an absolute PDF URL. invoice.rejected means KSeF rejected it and the error field carries the code. invoice.error covers failures on Notaly's side, such as a submission that could not reach KSeF.

HMAC signing

Each delivery is signed with HMAC so you can verify the request really came from Notaly and was not tampered with. Verify the signature on your endpoint before trusting the body, and reject deliveries that fail the check.

Retries and idempotency

If your endpoint is unreachable or returns an error, Notaly retries the delivery with backoff. That means your handler must be idempotent: applying the same invoice.accepted event twice must not create two records. Use the event or invoice id as the deduplication key.

A minimal handler

On invoice.accepted, store the KSeF number and UPO against the invoice and surface the PDF to your customer. On invoice.rejected, read the error code, fix the data and resubmit. Keep the handler fast, verify the HMAC, and return 2xx only after you have persisted the result.

FAQ

Why do I need webhooks for KSeF? KSeF processes invoices asynchronously. The submit call returns submitted, and the final result arrives later as a webhook.

How do I know the webhook is genuine? Notaly signs each webhook with HMAC, so you can verify the signature before trusting the payload.

Disclaimer

Notaly is not a tax, accounting or legal advisor. This article is general information, not tax advice, and may be outdated. You are responsible for your own VAT compliance; confirm with a qualified accountant where required.

Sources

Source: www.podatki.gov.pl

Frequently asked questions

Why do I need webhooks for KSeF?

KSeF processes invoices asynchronously. The submit call returns submitted, and the final result arrives later as an invoice.accepted or invoice.rejected webhook.

How do I know the webhook is genuine?

Notaly signs each webhook with HMAC, so you can verify the signature on your endpoint before trusting the payload.

What should I do if my endpoint is down?

Notaly retries delivery with backoff. Keep your handler idempotent so a retried event is not applied twice.

Issue your first KSeF test invoice today

Create a sandbox account: API key in seconds, 3 free test invoices on KSeF TEST, no credit card.