Szybki start · ok. 5 minut
Pierwsza faktura w KSeF w 5 minut
Cztery żądania: sprawdzasz klucz, wysyłasz opłacone zamówienie, odbierasz numer KSeF i pobierasz PDF. Wszystko w trybie testowym, na KSeF TEST, bez skutków prawnych.
Potrzebujesz: klucza testowego sk_test_... (wydajemy go w ramach wczesnego dostępu), uzupełnionych danych sprzedawcy i połączenia z KSeF TEST w panelu klienta (testowy certyfikat KSeF tworzy się jednym kliknięciem) oraz curl, PHP z ext-curl, Node.js 18+ albo Pythona 3 z requests. Klucz trzymaj w zmiennej środowiskowej:
export KSEF_API_KEY=sk_test_...
Tryb testowy wysyła faktury do KSeF TEST Ministerstwa Finansów: dostajesz prawdziwy numer KSeF i PDF, ale faktury nie mają skutków prawnych i nie liczą się do limitu.
- 1Sprawdź klucz i połączenie z KSeF
- 2Wyślij opłacone zamówienie
- 3Odbierz numer KSeF
- 4Pobierz PDF z kodem QR
130 sekund
Sprawdź klucz i połączenie z KSeF
Zapytaj o konto. Gdy seller_configured i ksef_connected mają wartość true, możesz wystawiać faktury. Jeśli dostaniesz 401, klucz jest błędny; jeśli ksef_connected to false, w ksef_check_error jest powód.
curl https://app.ksefshop.pl/v1/account \
-H "Authorization: Bearer $KSEF_API_KEY"<?php
$ch = curl_init('https://app.ksefshop.pl/v1/account');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KSEF_API_KEY'),
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
echo var_export($data['seller_configured'], true), ' ', var_export($data['ksef_connected'], true), PHP_EOL;const res = await fetch("https://app.ksefshop.pl/v1/account", {
headers: {
Authorization: `Bearer ${process.env.KSEF_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data.seller_configured, data.ksef_connected);import os
import requests
resp = requests.get(
"https://app.ksefshop.pl/v1/account",
headers={
"Authorization": f"Bearer {os.environ['KSEF_API_KEY']}",
},
timeout=30,
)
data = resp.json()
if not resp.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data["seller_configured"], data["ksef_connected"]){
"id": "acct_Xy7Qw2Er4Ty6Ui8Op0As2Df4",
"object": "account",
"livemode": false,
"email": "sklep@example.com",
"platform": "woocommerce",
"ksef_environment": "test",
"seller": {
"nip": "4235405606",
"name": "Sklep Woo sp. z o.o.",
"address_line1": "ul. Testowa 1",
"address_line2": "00-001 Warszawa"
},
"seller_configured": true,
"ksef_connected": true,
"ksef_check_error": null,
"plan": {
"name": "Unlimited",
"monthly_limit": null,
"used_this_month": 12
}
}21 minuta
Wyślij opłacone zamówienie
Wyślij zamówienie z NIP nabywcy, pozycjami w cenach brutto i stawkami VAT. API od razu odpowiada 201 i fakturą w statusie pending; zapamiętaj jej id.
Idempotency-Key sprawia, że ponowienie po błędzie sieci nie utworzy drugiej faktury. Błędny NIP zwraca od razu 400 buyer_nip_invalid z param: order.buyer.nip.
curl -X POST https://app.ksefshop.pl/v1/invoices \
-H "Authorization: Bearer $KSEF_API_KEY" \
-H "Idempotency-Key: order-SO-1001" \
-H "Content-Type: application/json" \
-d '{
"order": {
"external_id": "SO-1001",
"number": "1001",
"paid_at": "2026-10-19T09:13:05Z",
"currency": "PLN",
"payment_method": "card",
"buyer": {
"nip": "5260250274",
"name": "Testowa Firma sp. z o.o.",
"address_line1": "ul. Prosta 20",
"address_line2": "00-850 Warszawa",
"email": "jan@example.com"
},
"lines": [
{
"name": "Kubek ceramiczny 350 ml",
"sku": "KUB-350",
"quantity": 2,
"unit_price": "49.00",
"vat_rate": "23"
},
{
"name": "Książka: Podstawy księgowości",
"sku": "KS-001",
"quantity": 1,
"unit_price": "59.90",
"discount": "9.90",
"vat_rate": "5"
}
],
"shipping": [
{
"name": "Kurier InPost",
"price": "15.99",
"vat_rate": "23"
}
]
}
}'<?php
$payload = [
'order' => [
'external_id' => 'SO-1001',
'number' => '1001',
'paid_at' => '2026-10-19T09:13:05Z',
'currency' => 'PLN',
'payment_method' => 'card',
'buyer' => [
'nip' => '5260250274',
'name' => 'Testowa Firma sp. z o.o.',
'address_line1' => 'ul. Prosta 20',
'address_line2' => '00-850 Warszawa',
'email' => 'jan@example.com',
],
'lines' => [
[
'name' => 'Kubek ceramiczny 350 ml',
'sku' => 'KUB-350',
'quantity' => 2,
'unit_price' => '49.00',
'vat_rate' => '23',
],
[
'name' => 'Książka: Podstawy księgowości',
'sku' => 'KS-001',
'quantity' => 1,
'unit_price' => '59.90',
'discount' => '9.90',
'vat_rate' => '5',
],
],
'shipping' => [
['name' => 'Kurier InPost', 'price' => '15.99', 'vat_rate' => '23'],
],
],
];
$ch = curl_init('https://app.ksefshop.pl/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KSEF_API_KEY'),
'Idempotency-Key: order-SO-1001',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
echo var_export($data['id'], true), ' ', var_export($data['status'], true), PHP_EOL;const res = await fetch("https://app.ksefshop.pl/v1/invoices", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KSEF_API_KEY}`,
"Idempotency-Key": "order-SO-1001",
"Content-Type": "application/json",
},
body: JSON.stringify({
order: {
external_id: "SO-1001",
number: "1001",
paid_at: "2026-10-19T09:13:05Z",
currency: "PLN",
payment_method: "card",
buyer: {
nip: "5260250274",
name: "Testowa Firma sp. z o.o.",
address_line1: "ul. Prosta 20",
address_line2: "00-850 Warszawa",
email: "jan@example.com",
},
lines: [
{
name: "Kubek ceramiczny 350 ml",
sku: "KUB-350",
quantity: 2,
unit_price: "49.00",
vat_rate: "23",
},
{
name: "Książka: Podstawy księgowości",
sku: "KS-001",
quantity: 1,
unit_price: "59.90",
discount: "9.90",
vat_rate: "5",
},
],
shipping: [
{ name: "Kurier InPost", price: "15.99", vat_rate: "23" },
],
},
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data.id, data.status);import os
import requests
resp = requests.post(
"https://app.ksefshop.pl/v1/invoices",
headers={
"Authorization": f"Bearer {os.environ['KSEF_API_KEY']}",
"Idempotency-Key": "order-SO-1001",
},
json={
"order": {
"external_id": "SO-1001",
"number": "1001",
"paid_at": "2026-10-19T09:13:05Z",
"currency": "PLN",
"payment_method": "card",
"buyer": {
"nip": "5260250274",
"name": "Testowa Firma sp. z o.o.",
"address_line1": "ul. Prosta 20",
"address_line2": "00-850 Warszawa",
"email": "jan@example.com",
},
"lines": [
{
"name": "Kubek ceramiczny 350 ml",
"sku": "KUB-350",
"quantity": 2,
"unit_price": "49.00",
"vat_rate": "23",
},
{
"name": "Książka: Podstawy księgowości",
"sku": "KS-001",
"quantity": 1,
"unit_price": "59.90",
"discount": "9.90",
"vat_rate": "5",
},
],
"shipping": [
{"name": "Kurier InPost", "price": "15.99", "vat_rate": "23"},
],
},
},
timeout=30,
)
data = resp.json()
if not resp.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data["id"], data["status"]){
"id": "inv_4fJ2kP9xQwErTy7uI0oPaSdF",
"object": "invoice",
"livemode": false,
"status": "pending",
"order": {
"external_id": "SO-1001",
"number": null
},
"number": null,
"ksef_number": null,
"ksef_environment": "test",
"issue_date": null,
"currency": null,
"total_gross": null,
"buyer_nip": null,
"pdf_url": null,
"last_error": null,
"created": 1760865185,
"updated": 1760865185,
"corrections": []
}310-60 sekund
Odbierz numer KSeF
Kolejka nadaje numer (queued), wysyła XML do KSeF (sending), a po przyjęciu status zmienia się na accepted i pojawia się ksef_number. W produkcji zamiast odpytywać, odbieraj webhooki invoice.accepted i invoice.rejected.
ID=inv_4fJ2kP9xQwErTy7uI0oPaSdF
for i in $(seq 1 30); do
STATUS=$(curl -s https://app.ksefshop.pl/v1/invoices/$ID \
-H "Authorization: Bearer $KSEF_API_KEY" | jq -r .status)
echo "$STATUS"
case "$STATUS" in accepted|rejected|skipped|error) break ;; esac
sleep 3
done
curl -s https://app.ksefshop.pl/v1/invoices/$ID \
-H "Authorization: Bearer $KSEF_API_KEY" | jq '{status, number, ksef_number, last_error}'<?php
function ksef_get(string $path): array
{
$ch = curl_init('https://app.ksefshop.pl' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('KSEF_API_KEY')],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
return $data;
}
$id = 'inv_4fJ2kP9xQwErTy7uI0oPaSdF';
$final = ['accepted', 'rejected', 'skipped', 'error'];
for ($i = 0; $i < 30; $i++) {
$invoice = ksef_get('/v1/invoices/' . $id);
if (in_array($invoice['status'], $final, true)) {
break;
}
sleep(3); // zwykle 10-60 s do przyjęcia
}
echo $invoice['status'], ' ', $invoice['ksef_number'] ?? '-', PHP_EOL;const id = "inv_4fJ2kP9xQwErTy7uI0oPaSdF";
const final = ["accepted", "rejected", "skipped", "error"];
let invoice;
for (let i = 0; i < 30; i++) {
const res = await fetch(`https://app.ksefshop.pl/v1/invoices/${id}`, {
headers: { Authorization: `Bearer ${process.env.KSEF_API_KEY}` },
});
invoice = await res.json();
if (final.includes(invoice.status)) break;
await new Promise((r) => setTimeout(r, 3000)); // zwykle 10-60 s do przyjęcia
}
console.log(invoice.status, invoice.ksef_number, invoice.last_error);import os
import time
import requests
invoice_id = "inv_4fJ2kP9xQwErTy7uI0oPaSdF"
headers = {"Authorization": f"Bearer {os.environ['KSEF_API_KEY']}"}
for _ in range(30):
invoice = requests.get(
f"https://app.ksefshop.pl/v1/invoices/{invoice_id}", headers=headers, timeout=30
).json()
if invoice["status"] in ("accepted", "rejected", "skipped", "error"):
break
time.sleep(3) # zwykle 10-60 s do przyjęcia
print(invoice["status"], invoice["ksef_number"], invoice["last_error"]){
"id": "inv_4fJ2kP9xQwErTy7uI0oPaSdF",
"object": "invoice",
"livemode": false,
"status": "accepted",
"order": {
"external_id": "SO-1001",
"number": "1001"
},
"number": "FV/2026/10/1",
"ksef_number": "4235405606-20261019-0E7570400000-57",
"ksef_environment": "test",
"issue_date": "2026-10-19",
"currency": "PLN",
"total_gross": "163.99",
"buyer_nip": "5260250274",
"pdf_url": "https://app.ksefshop.pl/pdf/cmgxa1b2c0000/Qk9tZ2FsX3ZhbGlkX3Rva2VuX2V4YW1w",
"last_error": null,
"created": 1760865185,
"updated": 1760865214,
"corrections": []
}410 sekund
Pobierz PDF z kodem QR
PDF ma numer KSeF i kod QR do weryfikacji faktury w KSeF. Przez kilka sekund po przyjęciu możesz dostać 409 pdf_not_ready; wtedy ponów. Kupującemu wyślij link pdf_url z obiektu faktury: działa bez klucza API, a gdy podasz buyer.email, PDF wysyłamy mu sami.
curl https://app.ksefshop.pl/v1/invoices/inv_4fJ2kP9xQwErTy7uI0oPaSdF/pdf \
-H "Authorization: Bearer $KSEF_API_KEY" \
--fail -o faktura.pdf<?php
$ch = curl_init('https://app.ksefshop.pl/v1/invoices/inv_4fJ2kP9xQwErTy7uI0oPaSdF/pdf');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KSEF_API_KEY'),
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException('PDF: HTTP ' . $status); // 409 pdf_not_ready: spróbuj ponownie za kilka sekund
}
file_put_contents('faktura.pdf', $body);import { writeFile } from "node:fs/promises";
const res = await fetch("https://app.ksefshop.pl/v1/invoices/inv_4fJ2kP9xQwErTy7uI0oPaSdF/pdf", {
headers: {
Authorization: `Bearer ${process.env.KSEF_API_KEY}`,
},
});
if (!res.ok) throw new Error(`PDF: HTTP ${res.status}`); // 409 pdf_not_ready: spróbuj ponownie za kilka sekund
await writeFile("faktura.pdf", Buffer.from(await res.arrayBuffer()));import os
import requests
resp = requests.get(
"https://app.ksefshop.pl/v1/invoices/inv_4fJ2kP9xQwErTy7uI0oPaSdF/pdf",
headers={
"Authorization": f"Bearer {os.environ['KSEF_API_KEY']}",
},
timeout=30,
)
resp.raise_for_status() # 409 pdf_not_ready: spróbuj ponownie za kilka sekund
with open("faktura.pdf", "wb") as f:
f.write(resp.content)Co dalej
- Webhooki: numer KSeF w Twoim systemie bez odpytywania.
- Idempotencja: bezpieczne ponawianie po błędach sieci i 5xx.
- Korekty: zwrot towaru jako faktura korygująca.
- Waluty obce: zamówienia w EUR i innych walutach.
- Referencja API: wszystkie pola i odpowiedzi.
Gdy coś nie działa
| Odpowiedź | Co zrobić |
|---|---|
401 api_key_invalid |
Sprawdź, czy w nagłówku jest Bearer i cały klucz. |
400 buyer_nip_invalid |
NIP ma błędną sumę kontrolną; testowo użyj 5260250274. |
status skipped, seller_not_configured |
Uzupełnij dane sprzedawcy w panelu. |
status queued, ksef_not_connected |
Połącz KSeF TEST w panelu (certyfikat testowy). |
403 live_mode_disabled |
Użyłeś klucza sk_live_; we wczesnym dostępie pracuj kluczem testowym. |
Pełna lista: błędy i kody.