Integracje

Webhooki - powiadomienia o zdarzeniach w czasie rzeczywistym

Automatycznie powiadamiaj Zapier, Make, n8n lub własną aplikację o wystawionej fakturze, opłaconej należności czy nowym kontrahencie. Podpisane żądania HTTPS, automatyczne ponowienia, dziennik dostaw.

Zrzuty ekranu

Webhooki — przegląd

Webhook to powiadomienie, które Mobilna Faktura wysyła do Twojego systemu w momencie, gdy coś się wydarzy — wystawisz fakturę, klient ją opłaci, dodasz kontrahenta.

Nie musisz o nic pytać ani niczego sprawdzać. Podajesz adres URL, wybierasz interesujące Cię zdarzenia, a my wysyłamy tam podpisane żądanie POST z danymi w formacie JSON — zwykle w ciągu kilku sekund.

Dostępność: pakiet MAX.


Po co webhooki, skoro jest API

To pytanie wraca najczęściej, więc odpowiedzmy od razu. Różnica jest w tym, kto kogo pyta:

REST API (odpytywanie)Webhooki (powiadomienia)
Kto inicjujeTwój system pyta „czy coś nowego?”Mobilna Faktura sama informuje
Opóźnienietyle, co przerwa między zapytaniamikilka sekund
Obciążeniesetki zapytań dziennie, z czego większość pustatylko realne zdarzenia
Usunięcie fakturytrudne do wykrycia — dokumentu już nie mazwykłe zdarzenie
Opłacenie fakturytrzeba porównywać stan sprzed i pozwykłe zdarzenie
Koszt w Zapier / Makekażde sprawdzenie to zużyte zadanietylko faktyczne zdarzenia

Webhooki nie zastępują API — uzupełniają je. W treści powiadomienia znajdziesz odnośnik links.self do pełnych danych dokumentu przez REST API.


Zastosowania

  • Zapier / Make / n8n — bez pisania kodu: nowa faktura tworzy zadanie w Trello, wiersz w Arkuszach Google albo wiadomość na Slacku
  • Powiadomienia zespołu — informacja na kanale sprzedaży, gdy klient opłaci fakturę
  • Aktualizacja sklepu — oznaczenie zamówienia jako opłacone
  • Synchronizacja CRM — nowy kontrahent trafia od razu do bazy klientów
  • Automatyka magazynowa — wystawienie dokumentu uruchamia przygotowanie przesyłki
  • Własny system księgowy albo BI — dane spływają na bieżąco, bez eksportów

Konfiguracja krok po kroku

  1. W panelu przejdź do UstawieniaIntegracjeZdarzenia biznesowe (webhooki)
  2. Kliknij Dodaj webhook
  3. Wypełnij formularz:
    • Nazwa — dowolna etykieta, np. „Zapier — nowe faktury”
    • Adres URL — Twój endpoint, koniecznie https://
    • Opis — opcjonalnie, do czego służy
    • Zdarzenia — zaznacz te, które chcesz otrzymywać
  4. Kliknij Zapisz
  5. Skopiuj sekret, który pokaże się na zielonym tle

⚠️ Ważne: sekret wyświetlamy tylko raz, zaraz po utworzeniu webhooka. Służy do sprawdzenia, czy żądanie naprawdę pochodzi od nas. Jeśli go zgubisz, wygeneruj nowy — ale pamiętaj, że stary natychmiast przestanie działać.

Sprawdź, czy działa

Przy każdym webhooku jest przycisk Test. Wysyła przykładowe zdarzenie od razu, bez czekania na prawdziwą fakturę, i pokazuje odpowiedź Twojego serwera wraz z czasem reakcji. To najszybszy sposób, żeby upewnić się, że adres jest poprawny.


Lista zdarzeń

KluczKiedy wysyłamy
invoice.createdwystawiono nowy dokument sprzedaży
invoice.updatedzapisano zmiany w istniejącym dokumencie
invoice.deletedusunięto dokument
invoice.paidoznaczono fakturę jako opłaconą
contractor.createddodano nowego kontrahenta
product.createddodano nowy towar lub usługę

Klucze są stałe i nie zmieniają się między wersjami — możesz na nich bezpiecznie oprzeć własną logikę.


Które kanały wysyłają zdarzenia

Fakturę można wystawić na kilka sposobów, a nie każdy z nich wysyła już powiadomienia. Poniższa tabela pokazuje aktualny stan:

ZdarzeniePanelAPI SOAPAPI RESTSynchronizacja z programem
invoice.created
invoice.updated
invoice.deleted
invoice.paid
contractor.created
product.created

Kolumna Panel obejmuje także faktury wystawiane automatycznie z Allegro, WooCommerce, Shopify, PrestaShop, BaseLinkera i magazynu — wszystkie korzystają z tej samej ścieżki zapisu.

Tę samą tabelę znajdziesz w panelu, na stronie konfiguracji webhooków.


Format powiadomienia

Wysyłamy żądanie POST z treścią JSON i następującymi nagłówkami:

POST /twoj-endpoint HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: MobilnaFaktura-Webhook/1.0
X-MF-Event: invoice.created
X-MF-Delivery: 12345
X-MF-Timestamp: 1786865855
X-MF-Signature: sha256=3f2a1b...
NagłówekZnaczenie
X-MF-Eventklucz zdarzenia
X-MF-Deliverynumer próby dostarczenia — użyj go, żeby nie przetworzyć tego samego zdarzenia dwa razy
X-MF-Timestampczas wysłania (unix); odrzucaj żądania starsze niż 5 minut
X-MF-Signaturepodpis HMAC-SHA256 — patrz niżej

Przykładowa treść dla invoice.created:

{
  "event": "invoice.created",
  "event_id": 12345,
  "created_at": "2026-08-16T14:23:11+02:00",
  "source": "panel",
  "account": { "user_id": 8123, "profile_id": null },
  "data": {
    "id": 998877,
    "numer": "FV/2026/08/0042",
    "typ_dokumentu": 1,
    "typ_nazwa": "Faktura VAT",
    "data_wystawienia": "2026-08-16",
    "data_sprzedazy": "2026-08-16",
    "termin_platnosci": "2026-08-30",
    "netto": 1000.00,
    "vat": 230.00,
    "brutto": 1230.00,
    "waluta": "PLN",
    "zaplacono": false,
    "forma_platnosci": "przelew",
    "korekta": false,
    "ksef_status": "none",
    "ksef_number": null,
    "kontrahent": {
      "id": 4455,
      "nazwa": "ACME Sp. z o.o.",
      "nip": "1234563218",
      "email": "biuro@acme.pl",
      "adres": "ul. Testowa 1",
      "kod_pocztowy": "00-001",
      "miasto": "Warszawa"
    },
    "pozycje_count": 3
  },
  "links": {
    "self": "https://mobilna-faktura.pl/panel/api/v1/documents/998877"
  }
}

Kwoty są liczbami (nie tekstem), daty w formacie ISO 8601. Przy fakturach korygujących kwoty są ujemne. Pole profile_id wskazuje profil firmy — jeśli prowadzisz kilka podmiotów na jednym koncie, po nim je rozróżnisz.

Pozycji faktury nie wysyłamy w powiadomieniu (pokazujemy tylko ich liczbę) — pobierzesz je pod adresem z links.self.


Weryfikacja podpisu

Każde żądanie podpisujemy Twoim sekretem. Dzięki temu masz pewność, że przyszło od nas, a nie od kogoś, kto zgadł adres Twojego endpointu. Zawsze sprawdzaj podpis przed przetworzeniem danych.

Podpisujemy połączenie znacznika czasu i treści: timestamp + "." + body.

PHP

$body   = file_get_contents('php://input');
$ts     = $_SERVER['HTTP_X_MF_TIMESTAMP'] ?? '';
$sig    = $_SERVER['HTTP_X_MF_SIGNATURE'] ?? '';
$secret = 'twoj-sekret-z-panelu';

$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);

if (!hash_equals($expected, $sig)) {
    http_response_code(403);
    exit('Nieprawidlowy podpis');
}

// ochrona przed ponownym wysłaniem starego żądania
if (abs(time() - (int) $ts) > 300) {
    http_response_code(403);
    exit('Zadanie wygaslo');
}

$dane = json_decode($body, true);
// ... Twoja logika
http_response_code(200);

Python

import hmac, hashlib, time

def sprawdz(body: bytes, ts: str, sig: str, secret: str) -> bool:
    oczekiwany = "sha256=" + hmac.new(
        secret.encode(), f"{ts}.".encode() + body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(oczekiwany, sig):
        return False
    return abs(time.time() - int(ts)) <= 300

Node.js

const crypto = require('crypto');

function sprawdz(body, ts, sig, secret) {
  const oczekiwany = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(ts + '.' + body)
    .digest('hex');

  const ok = crypto.timingSafeEqual(Buffer.from(oczekiwany), Buffer.from(sig));
  return ok && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}

⚠️ Ważne: podpis liczymy z surowej treści żądania, przed jakimkolwiek parsowaniem. W Express.js użyj express.raw({ type: 'application/json' }), inaczej podpis się nie zgodzi.


Odpowiedź Twojego serwera

Odpowiedz kodem 2xx (najlepiej 200), gdy przyjmiesz powiadomienie. Każdy inny kod traktujemy jako błąd i ponawiamy wysyłkę.

Odpowiadaj szybko — czekamy maksymalnie 10 sekund. Jeśli przetwarzanie zajmuje więcej, zapisz dane u siebie i odpowiedz od razu, a resztę zrób w tle.

Nie podążamy za przekierowaniami — odpowiedź 301 czy 302 liczymy jako błąd.


Ponowienia i błędy

Gdy dostarczenie się nie powiedzie, ponawiamy próbę do 5 razy, z rosnącym odstępem:

PróbaKiedy
1natychmiast
2po 1 minucie
3po 5 minutach
4po 30 minutach
5po 2 godzinach

Po piątej nieudanej próbie oznaczamy dostawę jako nieudaną. Możesz ją ponowić ręcznie z poziomu dziennika.

Automatyczne wyłączanie: po 10 kolejnych nieudanych dostawach wyłączamy webhook i pokazujemy powód przy jego nazwie. Chroni to przed wysyłaniem powiadomień pod adres, który przestał istnieć. Po naprawieniu problemu włącz go ponownie przełącznikiem — licznik wyzeruje się przy pierwszej udanej dostawie.


Dziennik dostaw

UstawieniaIntegracjeDziennik webhookow pokazuje historię wszystkich prób: datę, zdarzenie, webhook, status, kod odpowiedzi, numer próby i czas reakcji Twojego serwera.

Przycisk z ikoną kodu rozwija pełną treść, którą wysłaliśmy, oraz odpowiedź, którą otrzymaliśmy — to najszybszy sposób na znalezienie przyczyny problemu. Nieudane dostawy można ponowić jednym kliknięciem.

Dziennik można filtrować po statusie, zdarzeniu, webhooku i zakresie dat oraz wyeksportować do pliku CSV.

Historię przechowujemy przez 30 dni, a same zdarzenia przez 90 dni.


Wymagania i limity

Wartość
Protokółwyłącznie HTTPS, port 443
Adresmusi być publicznie dostępny
Certyfikatważny i zaufany (sprawdzamy go)
Limit webhooków10 na konto
Limit powiadomień1000 na godzinę
Czas oczekiwania na odpowiedź10 sekund

Dlaczego tylko HTTPS? W treści powiadomienia są dane kontrahentów — nazwy, NIP-y, adresy i kwoty. Przez zwykłe http:// szłyby otwartym tekstem.

Dlaczego adres musi być publiczny? Nie wysyłamy powiadomień na adresy lokalne (127.0.0.1, localhost, 192.168.x.x, 10.x.x.x) — to standardowe zabezpieczenie serwera. Do testów na własnym komputerze użyj tunelu, np. ngrok albo Cloudflare Tunnel.


Najczęstsze problemy

ObjawPrzyczyna
„Adres wskazuje na sieć lokalną”podałeś localhost lub adres z sieci prywatnej — użyj tunelu
„Wymagany jest adres HTTPS”adres zaczyna się od http://
Podpis się nie zgadzaliczysz go z przetworzonego JSON-a zamiast z surowej treści żądania
Webhook sam się wyłączył10 kolejnych błędów — sprawdź dziennik i włącz ponownie
Status „Oczekuje” od dłuższej chwilitrwa ponowienie; przy dacie widać, kiedy nastąpi kolejna próba
To samo zdarzenie przyszło dwa razyponowienie po przekroczeniu czasu — użyj X-MF-Delivery do rozpoznania duplikatu
Brak zdarzeń mimo wystawiania faktursprawdź, czy webhook jest aktywny i czy zaznaczyłeś właściwe zdarzenia
Nie widzę webhooków w menufunkcja wymaga pakietu MAX

Korzyści

  • Reakcja w kilka sekund zamiast cyklicznego odpytywania
  • Bez kodu — wystarczy Zapier, Make albo n8n
  • Podpis HMAC — pewność, że powiadomienie pochodzi od nas
  • Automatyczne ponowienia — chwilowa awaria Twojego serwera nie gubi zdarzeń
  • Pełny dziennik z podglądem treści i odpowiedzi
  • Niższe koszty integracji — płacisz za realne zdarzenia, nie za puste zapytania