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 inicjuje | Twój system pyta „czy coś nowego?” | Mobilna Faktura sama informuje |
| Opóźnienie | tyle, co przerwa między zapytaniami | kilka sekund |
| Obciążenie | setki zapytań dziennie, z czego większość pusta | tylko realne zdarzenia |
| Usunięcie faktury | trudne do wykrycia — dokumentu już nie ma | zwykłe zdarzenie |
| Opłacenie faktury | trzeba porównywać stan sprzed i po | zwykłe zdarzenie |
| Koszt w Zapier / Make | każde sprawdzenie to zużyte zadanie | tylko 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
- W panelu przejdź do Ustawienia → Integracje → Zdarzenia biznesowe (webhooki)
- Kliknij Dodaj webhook
- 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ć
- Kliknij Zapisz
- 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ń
| Klucz | Kiedy wysyłamy |
|---|---|
invoice.created | wystawiono nowy dokument sprzedaży |
invoice.updated | zapisano zmiany w istniejącym dokumencie |
invoice.deleted | usunięto dokument |
invoice.paid | oznaczono fakturę jako opłaconą |
contractor.created | dodano nowego kontrahenta |
product.created | dodano 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:
| Zdarzenie | Panel | API SOAP | API REST | Synchronizacja 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łówek | Znaczenie |
|---|---|
X-MF-Event | klucz zdarzenia |
X-MF-Delivery | numer próby dostarczenia — użyj go, żeby nie przetworzyć tego samego zdarzenia dwa razy |
X-MF-Timestamp | czas wysłania (unix); odrzucaj żądania starsze niż 5 minut |
X-MF-Signature | podpis 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óba | Kiedy |
|---|---|
| 1 | natychmiast |
| 2 | po 1 minucie |
| 3 | po 5 minutach |
| 4 | po 30 minutach |
| 5 | po 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
Ustawienia → Integracje → Dziennik 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 |
| Adres | musi być publicznie dostępny |
| Certyfikat | ważny i zaufany (sprawdzamy go) |
| Limit webhooków | 10 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
| Objaw | Przyczyna |
|---|---|
| „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 zgadza | liczysz 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 chwili | trwa ponowienie; przy dacie widać, kiedy nastąpi kolejna próba |
| To samo zdarzenie przyszło dwa razy | ponowienie po przekroczeniu czasu — użyj X-MF-Delivery do rozpoznania duplikatu |
| Brak zdarzeń mimo wystawiania faktur | sprawdź, czy webhook jest aktywny i czy zaznaczyłeś właściwe zdarzenia |
| Nie widzę webhooków w menu | funkcja 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