Poland · KSeF · FA(3) · test environment

Send FA(3) invoices to KSeF TEST from your software

Build your KSeF integration against the official KSeF TEST environment of the Ministry of Finance with the same API you use for other EU countries: FA(3) from JSON, checks before sending, KSeF number, UPO and a signed receipt. Test environment only: KSeF TEST uses fictitious companies and has no legal effect. We don't submit to KSeF production yet.

Needs curl and jq. All endpoints: API reference and GET https://api.wsystem.dev/einvoice/ksef. Sending counts like any /einvoice/send call (Platform plan: one checked invoice); the KSeF connection, status and UPO calls are free.

1. Create a fictitious test company in KSeF TEST

One call: we pick a random valid NIP, sign in to KSeF TEST with a self-signed company seal (allowed only in TEST), create a KSeF token with invoice permissions and store it encrypted under your API key. Up to 5 test companies a day per account.

shell
export KEY=...   # from https://api.wsystem.dev/account/free-key
curl -s -X POST https://api.wsystem.dev/einvoice/ksef/test-company -H "X-API-Key: $KEY"

Response (trimmed, from a real run on 12 November 2026):

{
 "id": "ksc_843ca3966c174c659407",
 "nip": "4112848364",
 "environment": "test",
 "kind": "test_company",
 "status": "active"
}

2. Build and check the FA(3) invoice (optional)

Send your invoice as JSON with format ksef-fa3. You get the FA(3) XML, checked against the official FA(3) schema and our FA(3) checks: NIP checksums, issue date not in the future, totals, VAT in PLN for foreign currencies, exemption basis. To check an FA(3) file you already have, POST it to /einvoice/validate.

shell
curl -s -X POST https://api.wsystem.dev/einvoice/create \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{
 "invoice_number": "FV/20261112-081036",
 "issue_date": "2026-11-12",
 "currency": "PLN",
 "seller": {
  "name": "Firma Testowa Sp. z o.o.",
  "nip": "4112848364",
  "street": "ul. Prosta 1",
  "city": "Warszawa",
  "postal_code": "00-001",
  "country": "PL"
 },
 "buyer": {
  "name": "Nabywca Testowy S.A.",
  "vat_id": "PL5265877635",
  "street": "ul. Krzywa 2",
  "city": "Kraków",
  "postal_code": "30-001",
  "country": "PL"
 },
 "lines": [
  {
   "description": "Usługa programistyczna",
   "quantity": 10,
   "unit": "HUR",
   "unit_price": 150.0,
   "vat_rate": 23
  }
 ],
 "payment": {
  "iban": "PL61109010140000071219812874",
  "due_date": "2026-11-12"
 },
 "format": "ksef-fa3"
}' | jq -r .xml > FV-1.xml

Response (trimmed, from a real run on 12 November 2026):

{
 "valid": true,
 "format": "ksef-fa3",
 "totals": {
  "currency": "PLN",
  "net_total": "1500.00",
  "vat_total": "345.00",
  "gross_total": "1845.00",
  "by_rate": {
   "23": "1500.00"
  }
 },
 "errors": [],
 "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Faktura xmlns=\"http://crd.gov.pl/wzor/2025/06/25/13775/\"><Naglowek><KodFormularza kodSystemowy=\"FA (3)\" wersjaSchemy=\"1-..."
}

3. Send it to KSeF TEST

The same send call as for Peppol, with transport ksef. We check the invoice and the buyer first (VAT, sanctions), refuse a duplicate number, encrypt the file as KSeF requires, send it and wait a few seconds for KSeF's answer. You get the KSeF number and a signed receipt. You can send your own FA(3) XML instead: {"transport": "ksef", "ksef_xml": "..."}.

shell
curl -s -X POST https://api.wsystem.dev/einvoice/send \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{
 "transport": "ksef",
 "invoice": {
  "invoice_number": "FV/20261112-081036",
  "issue_date": "2026-11-12",
  "currency": "PLN",
  "seller": {
   "name": "Firma Testowa Sp. z o.o.",
   "nip": "4112848364",
   "street": "ul. Prosta 1",
   "city": "Warszawa",
   "postal_code": "00-001",
   "country": "PL"
  },
  "buyer": {
   "name": "Nabywca Testowy S.A.",
   "vat_id": "PL5265877635",
   "street": "ul. Krzywa 2",
   "city": "Kraków",
   "postal_code": "30-001",
   "country": "PL"
  },
  "lines": [
   {
    "description": "Usługa programistyczna",
    "quantity": 10,
    "unit": "HUR",
    "unit_price": 150.0,
    "vat_rate": 23
   }
  ],
  "payment": {
   "iban": "PL61109010140000071219812874",
   "due_date": "2026-11-12"
  }
 }
}'

Response (trimmed, from a real run on 12 November 2026):

{
 "status": "ksef_accepted",
 "environment": "test",
 "ksef": {
  "id": "ksf_74263f19328b49f88b73",
  "state": "accepted",
  "ksef_number": "4112848364-20261112-398125C00000-B1",
  "status_url": "/einvoice/ksef/invoices/ksf_74263f19328b49f88b73",
  "upo_url": "/einvoice/ksef/invoices/ksf_74263f19328b49f88b73/upo"
 },
 "receipt_id": "ev_eef118304685417ea4833eaf650c5b05",
 "preflight": {
  "verdict": "warn"
 },
 "evidence": {
  "jws": "eyJhbGciOiJFZERTQSIsInR5..."
 }
}

4. Status and UPO

If KSeF is still processing, ask again. Once KSeF accepts the invoice, the UPO (the official receipt, signed by the Ministry of Finance) is available as XML.

shell
curl -s https://api.wsystem.dev/einvoice/ksef/invoices/$ID -H "X-API-Key: $KEY"

curl -s https://api.wsystem.dev/einvoice/ksef/invoices/$ID/upo -H "X-API-Key: $KEY" > UPO.xml

Response (trimmed, from a real run on 12 November 2026):

{
 "id": "ksf_74263f19328b49f88b73",
 "state": "accepted",
 "ksef_number": "4112848364-20261112-398125C00000-B1",
 "ksef_status": {
  "code": 200,
  "description": "Sukces",
  "details": []
 },
 "acquisition_date": "2026-11-12T07:10:42.4247515+00:00"
}

5. Use your own company in KSeF TEST

If you already have a company in KSeF TEST, generate a KSeF token there (permissions: issue and view invoices) and connect it. We sign in to KSeF TEST with it before saving; the token is stored encrypted and never shown again. DELETE the connection to delete it.

shell
curl -s -X POST https://api.wsystem.dev/einvoice/ksef/connections \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{
 "nip": "1234567890",
 "ksef_token": "...",
 "authorized": true,
 "client_ref": "cust-10442"
}'

When something says no

  • 422 invoice_invalidFA(3) schema or rule errors (rule ID, message). Nothing sent.
  • 403 ksef_connection_requiredNo KSeF connection for the seller's NIP under your key (step 1 or 5).
  • 409 duplicate_invoiceThis seller already sent this invoice number; KSeF would reject it as a duplicate.
  • 422 transport_rejectedKSeF TEST rejected the invoice; its code and reason are in the response.
  • 501 ksef_live_not_availablemode=live: KSeF production isn't available through W System.
  • 502 transport_unavailableKSeF TEST didn't answer. Nothing was registered; try again later.

Not available

  • KSeF production: not available. Nothing you send here has legal effect.
  • Batch sessions, offline modes (offline24, emergency) and QR codes for printed invoices.
  • Receiving or downloading purchase invoices from KSeF, attachments.
  • Advance and margin invoices from JSON (send them as your own FA(3) XML).

How it works

  • KSeF API 2.0 (online session): sign-in with a KSeF token, the file encrypted with AES-256 and the key with the Ministry's public key, as the KSeF documentation requires.
  • FA(3) schema version 1-0E, from the Ministry of Finance's official KSeF documentation.
  • KSeF tokens are stored encrypted, per API key, and deleted when you remove the connection.

Technical processing, not tax or legal advice.