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ść: od pakietu MINI (zgodnie z cennikiem). Zmiana stanów magazynowych przez API wymaga pakietu 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 → Ustawienia WebApi
- W polu Protokół API zaznacz REST (domyślnie ustawiony jest SOAP) — wybór zapisuje się od razu
- Kliknij Wygeneruj nowy klucz WebApi — otrzymasz klucz WebApi (32 znaki: cyfry i litery a–f)
Klucz jest przypisany do Twojego konta — działa tylko razem z loginem i hasłem tego konta (albo z profile_id jego profilu firmy). Klucz innego konta zostanie odrzucony kodem 401 z komunikatem „Niepoprawny klucz API”.
API SOAP działa niezależnie od tego wyboru, także przy zaznaczonym REST.
⚠️ 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 (weźmiesz go z GET /profiles albo z kolumny profile_id w Ustawienia → Firma → Twoje 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, stronicowanie limit i offset) |
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 PDF mailem na adres z pola email (opcjonalnie name) — adres kontrahenta nie jest pobierany sam |
POST /documents domyślnie zapisuje dokument jako wystawiony. Pole status: 0
tworzy wersję roboczą — czeka na zatwierdzenie w panelu i nie wchodzi do ewidencji
VAT, JPK ani KSeF.
Listy /clients i /items zwracają wszystkie pozycje naraz (bez stronicowania).
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: DocumentType, PaymentMethod, Currency, Kind, Status |
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 (POST /items, PUT /items/{id}) dane towaru przekazujesz w obiekcie
item — bez niego API zwróci 400 („Pole “item” jest wymagane”):
{
"login": "...", "password": "...", "webkey": "...",
"item": {"price_retail": "45.00"}
}
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. change, który sprowadziłby stan poniżej zera, zawsze kończy się kodem
409, a ujemne quantity — kodem 400. Przy włączonej blokadzie stanów ujemnych
409 może wrócić także przy wydaniu, gdy blokada zatrzymała dokument WZ.
KSeF
| Metoda | Endpoint | Co robi |
|---|---|---|
POST | /ksef/send | Wysyłka dokumentu do KSeF — wymaga pola document_id |
GET | /ksef/status | Sprawdzenie statusu wysyłki |
Odpowiedź z KSeF zawiera m.in. ksef_status, ksef_number, ksef_reference i qr_url.
⚠️ 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— brak danych logowania albo błędny login, hasło lub klucz WebApi (także klucz należący do innego konta — „Niepoprawny klucz API”)402 Payment Required— wykorzystany limit dokumentów z abonamentu403 Forbidden— REST API nie jest włączone dla konta (przełącz protokół w ustawieniach),profile_idnie należy do konta albo operacja wymaga wyższego pakietu (np. zmiana stanów bez pakietu Max)404 Not Found— zasób nie istnieje409 Conflict— zmiana stanu sprowadziłaby go poniżej zera albo zatrzymała ją blokada stanów ujemnych500— 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