KSeF Faktury
EN
Join the start list
Early access

The API is in early access: we hand out test keys (KSeF TEST) in batches to people on the launch list. Live mode comes later.

Request access

Guides

WooCommerce and PrestaShop as API clients

The WooCommerce plugin uses the same public API you do. For PrestaShop we show how to connect a store directly.

WooCommerce: the KSeF Faktury plugin

The "KSeF Faktury for WooCommerce" plugin is an ordinary client of this API. It passed tests on KSeF TEST (classic and block checkout, refunds, HPOS); it will be listed on WordPress.org after launch, and early access participants get it sooner.

  • A "I want a VAT invoice" box and a NIP field with a checksum check in classic and block checkout. It can also read the NIP from an existing field (e.g. _billing_nip, vat_number).
  • Once an order with a NIP is paid, it sends it in the background (Action Scheduler) with an Idempotency-Key and retries on network errors, 429 and 5xx. Cash on delivery is sent on completion.
  • A WooCommerce refund becomes a correction.
  • The order screen shows the status, invoice and KSeF numbers and PDF links; the buyer sees the PDF on the order page.
  • Only orders with a NIP leave the store, with only the data needed: no IP address, user agent, phone or customer id.
  • Requirements: PHP 7.4+, WooCommerce 8.9+, HPOS compatible.

The plugin sends the order in the WooCommerce REST API v3 format as it is. You can do the same in your own code:

POST /v1/invoices
curl -X POST https://app.ksefshop.pl/v1/invoices \
  -H "Authorization: Bearer $KSEF_API_KEY" \
  -H "Idempotency-Key: woo-order-727" \
  -H "Content-Type: application/json" \
  -d '{
  "woocommerce_order": {
    "id": 727,
    "number": "727",
    "status": "processing",
    "currency": "PLN",
    "date_created_gmt": "2026-10-19T09:12:40",
    "date_paid_gmt": "2026-10-19T09:13:05",
    "billing": {
      "first_name": "Jan",
      "last_name": "Kowalski",
      "company": "Testowa Firma sp. z o.o.",
      "address_1": "ul. Prosta 20",
      "city": "Warszawa",
      "postcode": "00-850",
      "country": "PL",
      "email": "jan@example.com"
    },
    "payment_method": "cod",
    "meta_data": [
      {
        "key": "_ksef_buyer_nip",
        "value": "5260250274"
      }
    ],
    "line_items": [
      {
        "id": 41,
        "name": "Kubek ceramiczny 350 ml",
        "quantity": 2,
        "subtotal": "79.67",
        "subtotal_tax": "18.33",
        "total": "79.67",
        "total_tax": "18.33",
        "taxes": [
          {
            "id": 1,
            "total": "18.33"
          }
        ]
      }
    ],
    "tax_lines": [
      {
        "id": 51,
        "rate_id": 1,
        "rate_percent": 23
      }
    ],
    "shipping_lines": []
  }
}'

We read the NIP from meta_data: _ksef_buyer_nip (the plugin's field), the block checkout field, _billing_nip or vat_number. The VAT rate comes from tax_lines.rate_percent.

PrestaShop: directly through the API

There is no ready PrestaShop module yet. Until then a PrestaShop store connects to the API directly: once payment is confirmed (the actionPaymentConfirmation hook) it sends the order in the neutral format. Below is a module sketch for your developer to finish.

PrestaShop module sketch
<?php
// modules/ksefapi/ksefapi.php - sketch: paid orders with a NIP -> POST /v1/invoices
class KsefApi extends Module
{
    public function __construct()
    {
        $this->name = 'ksefapi';
        $this->version = '0.1.0';
        $this->author = 'Your company';
        parent::__construct();
        $this->displayName = 'KSeF Faktury API';
    }

    public function install()
    {
        return parent::install() && $this->registerHook('actionPaymentConfirmation');
    }

    public function hookActionPaymentConfirmation(array $params)
    {
        $order = new Order((int) $params['id_order']);
        $address = new Address((int) $order->id_address_invoice);
        if (trim((string) $address->vat_number) === '') {
            return; // consumer without a NIP: no KSeF invoice
        }
        $lines = [];
        foreach ($order->getProducts() as $p) {
            $lines[] = [
                'name' => $p['product_name'],
                'sku' => $p['product_reference'],
                'quantity' => (int) $p['product_quantity'],
                'unit_price' => number_format((float) $p['unit_price_tax_incl'], 2, '.', ''),
                'vat_rate' => (string) (int) round((float) $p['tax_rate']),
            ];
        }
        $shipping = [];
        if ((float) $order->total_shipping_tax_incl > 0) {
            $shipping[] = [
                'name' => (new Carrier((int) $order->id_carrier))->name,
                'price' => number_format((float) $order->total_shipping_tax_incl, 2, '.', ''),
                'vat_rate' => (string) (int) round((float) $order->carrier_tax_rate),
            ];
        }
        $payload = ['order' => [
            'external_id' => 'PS-' . $order->id,
            'number' => $order->reference,
            'paid_at' => gmdate('c'),
            'currency' => (new Currency((int) $order->id_currency))->iso_code,
            'buyer' => [
                'nip' => $address->vat_number,
                'name' => $address->company ?: trim($address->firstname . ' ' . $address->lastname),
                'address_line1' => $address->address1,
                'address_line2' => trim($address->postcode . ' ' . $address->city),
                'email' => (new Customer((int) $order->id_customer))->email,
            ],
            'lines' => $lines,
            'shipping' => $shipping,
        ]];

        // In production send from a queue or cron and retry with the same key.
        $ch = curl_init('https://app.ksefshop.pl/v1/invoices');
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . Configuration::get('KSEFAPI_KEY'),
                'Idempotency-Key: order-PS-' . $order->id,
                'Content-Type: application/json',
            ],
            CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
        ]);
        $invoice = json_decode((string) curl_exec($ch), true);
        curl_close($ch);
        if (isset($invoice['id'])) {
            Configuration::updateValue('KSEFAPI_INV_' . $order->id, $invoice['id']);
        }
    }
}

Before production:

  • Send from a queue or cron with retries rather than during the payment. Pattern: idempotency.
  • Spread cart rules over the lines as discount.
  • Send refunds (e.g. the actionOrderSlipAdd hook) to POST /v1/invoices/{id}/corrections. Corrections
  • Store the KSeF number and pdf_url on the order after the invoice.accepted event. Webhooks

Other systems

The API takes an order from any system that knows the order is paid: your own store, an ERP or a marketplace integration. A Shopify store does not need the API: the KSeF Faktury app is enough.

KSeF Faktury

API early access

We hand out test keys in batches. Leave your email: we will write when your access is ready.