REST API (WebApi) — przegląd
REST API pozwala połączyć Mobilną Fakturę z dowolnym zewnętrznym systemem: własnym sklepem, programem magazynowym, systemem ERP albo aplikacją mobilną. Dane wymieniane są w formacie JSON przez zwykłe zapytania HTTP.
Jeśli zamiast odpytywania wolisz otrzymywać powiadomienia o zdarzeniach, zajrzyj do artykułu o webhookach.
Dostępność: pakiet STANDARD i MAX.
Zastosowania
- Automatyczne fakturowanie ze sklepu napisanego od podstaw (bez WooCommerce czy PrestaShop)
- Integracja z systemem ERP albo programem magazynowym
- Synchronizacja z CRM — kontrahenci w jednym miejscu
- Aplikacja mobilna dla handlowców — wystawianie dokumentów w terenie
- Własne raporty i zestawienia na podstawie danych z panelu
- Wysyłka do KSeF bezpośrednio z zewnętrznego systemu
Włączenie REST API i klucz WebApi
REST API jest domyślnie wyłączone — trzeba je włączyć w panelu:
- Ustawienia → Integracje → WebAPI
- Przełącz protokół na REST (domyślnie ustawiony jest SOAP)
- Kliknij Generuj klucz — otrzymasz klucz WebApi
⚠️ Ważne: konto ma jeden klucz WebApi. Ponowne wygenerowanie unieważnia poprzedni, więc wszystkie integracje korzystające ze starego klucza przestaną działać — zaktualizuj je od razu.
Jeśli nie przełączysz protokołu na REST, każde zapytanie zwróci 403 z komunikatem
„REST API nie jest włączone dla tego konta”.
Autoryzacja
API jest bezstanowe — dane uwierzytelniające dołączasz do każdego zapytania:
| Pole | Opis |
|---|---|
login | login do panelu |
password albo password_md5 | hasło jawne albo jego skrót MD5 |
webkey | klucz WebApi z ustawień |
profile_id | opcjonalnie — identyfikator profilu firmy |
POST https://mobilna-faktura.pl/panel/api/v1/documents
Content-Type: application/json
{
"login": "jan@firma.pl",
"password_md5": "5f4dcc3b5aa765d61d8327deb882cf99",
"webkey": "a4c4dcf4c0280f1d2e6f3ac1fe43f8e9"
}
Przy zapytaniach GET te same pola możesz przekazać w adresie:
curl "https://mobilna-faktura.pl/panel/api/v1/clients?login=jan@firma.pl&password_md5=<md5>&webkey=<klucz>"
⚠️ Ważne: w zapytaniach przesyłasz hasło do konta panelu. Używaj wyłącznie HTTPS, trzymaj dane w zmiennych środowiskowych i nigdy nie umieszczaj ich w repozytorium kodu.
Pierwszy krok — sprawdzenie konfiguracji
Endpoint POST /api/v1/auth/check działa nawet gdy REST jest jeszcze wyłączony.
Zwraca informację, czy dane logowania są poprawne i czy protokół jest przełączony:
curl -X POST https://mobilna-faktura.pl/panel/api/v1/auth/check \
-H "Content-Type: application/json" \
-d '{"login":"jan@firma.pl","password":"tajne","webkey":"a4c4dcf4..."}'
Dostępne endpointy
Adres bazowy: https://mobilna-faktura.pl/panel/api/v1
Konto i profile
| Metoda | Endpoint | Co robi |
|---|---|---|
POST | /auth/check | Sprawdzenie danych logowania i konfiguracji |
GET | /profiles | Lista profili firm na koncie |
GET | /me | Dane zalogowanego konta |
Dokumenty
| Metoda | Endpoint | Co robi |
|---|---|---|
GET | /documents | Lista dokumentów (filtry w parametrach) |
GET | /documents/{id} | Szczegóły dokumentu wraz z pozycjami |
POST | /documents | Wystawienie nowego dokumentu |
DELETE | /documents | Usunięcie — identyfikatory w polu ids |
GET | /documents/{id}/pdf | Pobranie PDF |
POST | /documents/{id}/send | Wysyłka mailem do kontrahenta |
Kontrahenci
| Metoda | Endpoint | Co robi |
|---|---|---|
GET | /clients | Lista kontrahentów |
GET | /clients/{id} | Szczegóły kontrahenta |
POST | /clients | Dodanie nowego |
DELETE | /clients | Usunięcie — identyfikatory w polu ids |
Kontrahent zwraca i przyjmuje pole default_price_level — poziom cenowy stosowany
przy wystawianiu dokumentu (detaliczna, hurtowa, specjalna, podstawowa).
null oznacza dziedziczenie poziomu z kartoteki towaru. To ten sam mechanizm co
w panelu, więc integracja wystawiająca faktury dla klienta hurtowego dostanie
właściwe ceny bez przeliczania ich po swojej stronie.
Towary i słowniki
| Metoda | Endpoint | Co robi |
|---|---|---|
GET | /items | Lista towarów i usług |
GET | /items/{id} | Szczegóły pozycji |
POST | /items | Dodanie towaru |
PUT | /items/{id} | Edycja towaru (ceny, opis, poziomy cen) |
PUT | /items/{id}/stock | Zmiana stanu magazynowego |
GET | /dictionaries | Lista dostępnych słowników |
GET | /dictionaries/{nazwa} | Zawartość słownika (np. stawki VAT, jednostki) |
Endpointy PUT przyjmują także POST, gdy biblioteka HTTP nie obsługuje metody PUT.
Poziomy cen w odpowiedzi
Każdy towar zwraca obok ceny podstawowej trzy poziomy cenowe:
{
"id": 29130,
"name": "Kabel HDMI 2m",
"price_net": "39.00",
"price_gross": "47.97",
"default_price_level": "podstawowa",
"price_retail": {"net": "45.00", "gross": "55.35", "fallback": false},
"price_wholesale": {"net": "29.00", "gross": "35.67", "fallback": false},
"price_special": {"net": "39.00", "gross": "47.97", "fallback": true}
}
price_net/price_grossto cena podstawowa — pola niezmienione, więc dotychczasowe integracje działają bez zmian.fallback: trueoznacza, że poziom nie ma własnej ceny i dziedziczy podstawową (brutto liczone ze stawki VAT towaru).default_price_levelto domyślny poziom towaru. Poziom ustawiony na kartotece kontrahenta (default_price_levelw/clients) ma nad nim pierwszeństwo — dokładnie tak, jak przy wystawianiu faktury w panelu.
Przy zapisie ceny poziomów podaje się jako price_retail, price_wholesale,
price_special (netto) oraz warianty _gross. Wartość null lub "" czyści
poziom, czyli przywraca dziedziczenie ceny podstawowej.
Zmiana stanu magazynowego
Stan zmienia się wyłącznie przez dokument magazynowy: PZ przy zwiększeniu, WZ przy zmniejszeniu — tak samo jak w panelu. Dzięki temu każda zmiana z API jest widoczna w dokumentach, schodzi z partii FIFO i trafia do synchronizacji stanów ze sklepem.
Podaj dokładnie jedno z pól:
| Pole | Znaczenie | Przykład zastosowania |
|---|---|---|
quantity | stan docelowy (wartość absolutna) | inwentaryzacja, pełna synchronizacja ze sklepem |
change | zmiana względna | -3 po sprzedaży, +10 po dostawie |
{
"login": "...", "password": "...", "webkey": "...",
"change": -3,
"description": "Sprzedaż w sklepie"
}
Odpowiedź zawiera stan przed i po zmianie oraz identyfikator utworzonego dokumentu:
{
"success": true,
"item_id": 29130,
"quantity": 12,
"previous_quantity": 15,
"changed": true,
"document_type": "WZ",
"warehouse_document_id": 8412
}
⚠️ Wymaga pakietu Max oraz włączonej opcji Ustawienia → WZ/PZ → aktualizacja
stanów wraz z drukami. Bez tej opcji dokument powstanie, ale odpowiedź zwróci
changed: false z wyjaśnieniem. Gdy stan już się zgadza, żaden dokument nie
powstaje. Przy włączonej blokadzie stanów ujemnych zejście poniżej zera kończy się
kodem 409.
KSeF
| Metoda | Endpoint | Co robi |
|---|---|---|
POST | /ksef/send | Wysyłka dokumentu do KSeF |
GET | /ksef/status | Sprawdzenie statusu wysyłki |
⚠️ Ważne: usuwanie działa na kolekcji, nie na pojedynczym adresie — wyślij
DELETE /documents z listą identyfikatorów w polu ids, a nie DELETE /documents/12.
Dokumentów i kontrahentów nie edytuje się przez API — dokument można wystawić,
pobrać i usunąć, ale nie zmienić. Edycja PUT działa dla towarów.
Format odpowiedzi (JSON)
Każda odpowiedź zawiera pole success.
Powodzenie
{
"success": true,
"document_id": 998877,
"already_exists": false,
"positions": 3,
"message": "Dokument zostal dodany"
}
Błąd
{
"success": false,
"message": "Brak wymaganych danych logowania (login, webkey, password lub password_md5)"
}
Kody HTTP
200 OK— zapytanie wykonane (sprawdź polesuccess)400 Bad Request— brak wymaganego pola albo błędne dane401 Unauthorized— błędny login, hasło lub klucz WebApi402 Payment Required— wykorzystany limit dokumentów z abonamentu403 Forbidden— REST API nie jest włączone dla konta (przełącz protokół w ustawieniach)404 Not Found— zasób nie istnieje500— błąd po stronie serwera
⚠️ Ważne: przy duplikacie numeru dokumentu API zwraca success: true wraz z polem
already_exists: true i identyfikatorem istniejącego dokumentu — nie traktuj tego jako błędu.
Przykłady kodu
PHP
$auth = [
'login' => 'jan@firma.pl',
'password_md5' => md5('twoje-haslo'),
'webkey' => 'a4c4dcf4c0280f1d2e6f3ac1fe43f8e9',
];
$dokument = [
'numer_dokumentu' => 'FV/2026/08/0042',
'typ_dokumentu' => 1,
'data_wystawienia' => '16-08-2026',
'data_sprzedazy' => '16-08-2026',
'termin_zaplaty' => '30-08-2026',
'na_nazwa' => 'ACME Sp. z o.o.',
'na_nip' => '1234563218',
'waluta' => 'PLN',
'products' => [
['nazwa' => 'Usluga konsultingowa', 'amount' => 1,
'cena' => 1000, 'cena2' => 1230, 'stawka_vat' => '23', 'jednostki' => 'szt.'],
],
];
$ch = curl_init('https://mobilna-faktura.pl/panel/api/v1/documents');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($auth + ['document' => $dokument]),
]);
$wynik = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!empty($wynik['success'])) {
echo 'Dokument zapisany, id: ' . $wynik['document_id'];
} else {
echo 'Blad: ' . $wynik['message'];
}
Python
import hashlib, requests
BASE = 'https://mobilna-faktura.pl/panel/api/v1'
auth = {
'login': 'jan@firma.pl',
'password_md5': hashlib.md5('twoje-haslo'.encode()).hexdigest(),
'webkey': 'a4c4dcf4c0280f1d2e6f3ac1fe43f8e9',
}
odp = requests.post(f'{BASE}/documents', json={**auth, 'document': {
'numer_dokumentu': 'FV/2026/08/0042',
'typ_dokumentu': 1,
'data_wystawienia': '16-08-2026',
'na_nazwa': 'ACME Sp. z o.o.',
'na_nip': '1234563218',
'products': [
{'nazwa': 'Usluga', 'amount': 1, 'cena': 1000,
'cena2': 1230, 'stawka_vat': '23', 'jednostki': 'szt.'},
],
}}).json()
print(odp['document_id'] if odp.get('success') else odp['message'])
JavaScript
const auth = {
login: 'jan@firma.pl',
password: 'twoje-haslo',
webkey: 'a4c4dcf4c0280f1d2e6f3ac1fe43f8e9',
};
const odp = await fetch('https://mobilna-faktura.pl/panel/api/v1/documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...auth,
document: {
numer_dokumentu: 'FV/2026/08/0042',
typ_dokumentu: 1,
data_wystawienia: '16-08-2026',
na_nazwa: 'ACME Sp. z o.o.',
na_nip: '1234563218',
products: [
{ nazwa: 'Usluga', amount: 1, cena: 1000,
cena2: 1230, stawka_vat: '23', jednostki: 'szt.' },
],
},
}),
}).then(r => r.json());
console.log(odp.success ? odp.document_id : odp.message);
cURL — pobranie listy kontrahentów
curl "https://mobilna-faktura.pl/panel/api/v1/clients?login=jan@firma.pl&password_md5=<md5>&webkey=<klucz>"
⚠️ Ważne: daty przekazuj w formacie dd-mm-rrrr. Pola pozycji akceptują też warianty
z przedrostkiem product_ (product_nazwa, product_cena) — dla zgodności z API SOAP.
Dokumentacja szczegółowa w panelu
Po zalogowaniu, w Ustawienia → Integracje → Dokumentacja API (REST) znajdziesz opis każdego endpointu wraz z listą parametrów, przykładowymi odpowiedziami i gotową klasą pomocniczą w PHP.
Webhooki (powiadomienia w drugą stronę)
REST API działa na zasadzie odpytywania — to Twój system pyta, czy coś się zmieniło. Odwrotny kierunek zapewniają webhooki: Mobilna Faktura sama powiadamia Twój endpoint, gdy wystawisz fakturę, klient ją opłaci albo dodasz kontrahenta.
Powiadomienia są podpisane (HMAC-SHA256), ponawiane przy błędach i rejestrowane w dzienniku dostaw. Konfiguracja: Ustawienia → Integracje → Zdarzenia biznesowe (webhooki).
Pełny opis: Webhooki — powiadomienia o zdarzeniach w czasie rzeczywistym
Webhooki wymagają pakietu MAX.
Wersjonowanie
Adresy zawierają numer wersji (/api/v1/...). Zmiany łamiące zgodność trafią do kolejnej
wersji (v2) — wersja v1 pozostanie dostępna, więc istniejące integracje nie przestaną
działać z dnia na dzień.
Korzyści
- Pełna kontrola integracji custom (zamiast ograniczeń wtyczek)
- Standard REST + JSON — łatwe dla każdego dewelopera
- Webhooki — powiadomienia push zamiast odpytywania
- Dokumentacja w panelu z przykładami dla każdego endpointu
- Wsparcie profili firm — jedno konto, wiele podmiotów