API LM Logistic
Wersja z 09.11.2026. API pozwala z Twojego programu albo sklepu wycenić przesyłkę, zamówić wybraną ofertę, pobrać etykietę, śledzić przesyłkę i poprosić o jej anulowanie. Widzisz te same ceny i przewoźników co w portalu klienta, a zamówienia z API rozliczamy tak samo jak zamówienia z portalu.
Kto może korzystać. Firmy z płatnością odroczoną. Firma płacąca z góry może wyceniać, ale zamówienie dostanie odpowiedź 403 PREPAYMENT_REQUIRED, bo API nie ma kroku płatności. Pobranie (COD) nie jest dostępne w API.
Opis wszystkich pól w formacie OpenAPI (do importu np. w Postmanie): openapi.yaml.
Adresy i klucz
| Środowisko | Adres bazowy | Klucz |
|---|---|---|
| Test | https://api-test.51.178.37.237.nip.io/api/partner/v1 | testowy, działa tylko tutaj |
| Produkcja | https://klient.lmlogistic.pl/api/partner/v1 | produkcyjny, od biura po zakończeniu testów |
Klucz ma postać lmp_ i 40 znaków. Wysyłaj go w każdym zapytaniu:
Authorization: Bearer lmp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Klucz trzymaj tylko na swoim serwerze, nie wstawiaj go do kodu strony w przeglądarce. Każde zapytanie ma format JSON (UTF-8). Kwoty podajemy w PLN (albo w walucie, w której rozliczamy Twoją firmę: pole currency), wagi w kg, wymiary w cm, daty yyyy-MM-dd, godziny HH:mm.
Limit: 300 zapytań na minutę z jednego adresu IP (po przekroczeniu odpowiedź 429), zapytanie do 1 MB.
Środowisko testowe
Działa tak samo jak produkcja, ale nic w nim nie jest prawdziwe:
- Ceny są przykładowe. Nikt niczego nie odbiera, nic nie jedzie i nie wystawiamy faktur.
- Nie wysyłamy e-maili ani SMS-ów.
- Oferty testowe to paczki UPS i FedEx oraz palety Hellmann i Rohlig SUUS. Na produkcji zobaczysz swoje cenniki i przewoźników.
- FedEx idzie przez sandbox przewoźnika. Pozostałych obsługuje symulator: numer listu zaczyna się od
SIM, a etykieta PDF jest oznaczona jako testowa i nie nadaje się do nadania. - Święta, dni wolne i wszystkie reguły zamówienia działają jak na produkcji.
- Prośba o anulowanie zamówionej przesyłki zostaje otwarta, bo po drugiej stronie nie ma biura.
- Nie wpisuj prawdziwych danych osobowych. Dane testowe możemy w każdej chwili wyczyścić.
Śledzenie przesyłki z symulatora liczy się od nadania, a nowe zdarzenia pojawiają się w API przy odświeżeniu, które działa raz na godzinę:
| Po czasie | status | Znaczenie |
|---|---|---|
| od razu | SIM_CREATED | Przyjęto zlecenie |
| 10 minut | SIM_PICKED_UP | Odebrano od nadawcy |
| 30 minut | SIM_IN_TRANSIT | W transporcie |
| 1 godzina | SIM_OUT_FOR_DELIVERY | W doręczeniu |
| 2 godziny | SIM_DELIVERED | Doręczona |
Na produkcji statusy mają kody przewoźnika, więc nie opieraj logiki na tych z tabeli. Do wyświetlania klientowi służy pole description.
Szybki start
Sprawdzenie klucza:
curl -s -H "Authorization: Bearer $LM_API_KEY" https://api-test.51.178.37.237.nip.io/api/partner/v1/ping
Wycena paczki:
curl -s -H "Authorization: Bearer $LM_API_KEY" -H "Content-Type: application/json" \
-d '{"sender":{"countryCode":"PL","postalCode":"05-500","private":false},"receiver":{"countryCode":"PL","postalCode":"00-950","private":false},"parcelType":"PACK","packages":[{"weightKg":5,"lengthCm":30,"widthCm":20,"heightCm":15}]}' \
https://api-test.51.178.37.237.nip.io/api/partner/v1/quotes
Dalej: wybierz offerId z odpowiedzi, wyślij POST /orders, odpytuj GET /orders/{orderId} aż pojawi się numer listu, pobierz etykietę z GET /documents/{documentId}.
Błędy
Kod HTTP mówi, co się stało, a treść ma stały kod błędu i komunikat po polsku:
{ "error": { "code": "VALIDATION", "message": "Brak kodu pocztowego.", "field": "receiver.postalCode" } }
| HTTP | Kod | Znaczenie |
|---|---|---|
| 400 | VALIDATION | Błędne albo brakujące pole (field wskazuje które) |
| 401 | UNAUTHORIZED | Brak klucza albo klucz unieważniony |
| 403 | ORDERS_BLOCKED | Zamawianie wstrzymane (zaległe faktury); wycena działa |
| 403 | PREPAYMENT_REQUIRED | Firma płaci z góry, więc nie zamówi przez API; wycena działa |
| 404 | QUOTE_NOT_FOUND, OFFER_NOT_FOUND, ORDER_NOT_FOUND, PARCEL_NOT_FOUND, DOCUMENT_NOT_FOUND | Nie ma albo nie należy do Ciebie |
| 409 | OFFER_NOT_AVAILABLE | Wycena zamówiona inną ofertą albo anulowana |
| 422 | QUOTE_REJECTED, ORDER_REJECTED, CANCEL_REJECTED | Reguła biznesowa (komunikat mówi, co poprawić) |
| 429 | RATE_LIMIT | Za dużo zapytań |
| 500, 502 | SERVER_ERROR, UNAVAILABLE | Błąd po naszej stronie, spróbuj ponownie |
1. Sprawdzenie połączenia
GET /ping zwraca nazwę firmy przypisanej do klucza. Pole environment ma wartość Production tylko na produkcji.
{ "partner": "Twoja Firma Sp. z o.o.", "environment": "Staging", "serverTime": "2026-11-09T08:00:00+00:00" }
2. Wycena
POST /quotes
{
"sender": { "countryCode": "PL", "postalCode": "05-500", "private": false },
"receiver": { "countryCode": "PL", "postalCode": "00-950", "private": true },
"parcelType": "PACK",
"packages": [ { "weightKg": 5, "lengthCm": 30, "widthCm": 20, "heightCm": 15 } ],
"insurance": 500,
"services": [],
"description": "Książki"
}
parcelType:PACK(paczka),PALETTE(paleta),LENGTH_BASED(dłużyca).- Paleta:
paletteTypew paczce to kod palety:E_PALLETE(paleta EURO 120×80),H_PALLETE(półpaleta),Q_PALLETE(ćwierćpaleta),I_PALLETE(przemysłowa 120×100). Bez niego klasę palety liczymy z wymiarów. private: trueoznacza osobę prywatną. Podawaj zgodnie z prawdą: część przewoźników dolicza opłatę za dostawę do osoby prywatnej i jest ona już w cenie oferty.services: wartościkeyzGET /services?originCountry=PL&destinationCountry=PL&parcelType=PALETTE. Pusta lista oznacza, że dla tego rodzaju przesyłki nie ma usług dodatkowych.- Do 50 paczek w jednej wycenie, opis do 50 znaków.
Odpowiedź:
{
"quoteId": 10235409,
"offers": [
{
"offerId": 10408243,
"carrier": "UPS Standard",
"service": "Transport drogowy",
"priceNet": 61.76, "vat": 14.20, "priceGross": 75.96, "currency": "PLN",
"validUntil": "2026-11-09T08:07:17+01:00",
"components": [ { "code": "PACK", "name": "UPS Standard", "net": 61.76, "gross": 75.96 } ],
"availableServices": [ { "name": "Powiadomienie SMS", "description": null, "priceNet": 1.50 } ]
}
]
}
Oferta jest ważna do validUntil, a gdy pola brak, przez 7 dni. Pusta lista ofert oznacza, że dla tej trasy i wymiarów nie mamy oferty. GET /quotes/{quoteId} zwraca oferty tej samej wyceny jeszcze raz.
3. Zamówienie
POST /orders
{
"offerId": 10408243,
"sender": { "companyName": "Jan Kowalski", "contactPerson": "Jan Kowalski", "email": "jan@example.com", "phone": "+48 600 100 200",
"street": "Polna", "houseNo": "1", "flatNo": "2", "city": "Piaseczno", "postalCode": "05-500", "countryCode": "PL" },
"receiver": { "companyName": "Anna Nowak", "contactPerson": "Anna Nowak", "email": "anna@example.com", "phone": "+48 600 200 300",
"street": "Marszałkowska", "houseNo": "10", "city": "Warszawa", "postalCode": "00-950", "countryCode": "PL" },
"pickup": { "date": "2026-11-12", "from": "09:00", "to": "15:00" },
"reference": "ZAM-2026-000123"
}
- Kod pocztowy i kraj muszą być takie jak w wycenie. Telefon i e-mail są wymagane po obu stronach, a e-mail odbiorcy musi być inny niż nadawcy.
pickupjest opcjonalny. Bez niego przyjmujemy najbliższy dzień roboczy (z pominięciem weekendów, świąt i dni wolnych przewoźnika), 09:00-15:00.- Okno odbioru musi obejmować co najmniej 4 godziny, między 06:00 a 20:00. Odbiór w weekend, święto albo dzień wolny przewoźnika dostanie
ORDER_REJECTEDz informacją, co poprawić. Przewoźnik może mieć własne, węższe godziny. referenceto numer zamówienia w Twoim systemie, do 100 znaków. Ponowne wysłanie zamówienia z tym samymreferencezwraca już złożone zlecenie (bez dubla), np. po zerwanym połączeniu.confirmCompany: truewyślij, gdy adresat to firma, choć nazwa wygląda na imię i nazwisko. Inaczej zamówienie z cennikiem, który dolicza opłatę za osobę prywatną, dostanieORDER_REJECTEDz prośbą o potwierdzenie.
Odpowiedź 201:
{
"orderId": 10214332, "orderNumber": "L10214332", "state": "IN_PROGRESS", "reference": "ZAM-2026-000123",
"parcels": [ { "parcelId": 10235409, "parcelNumber": "L10214332P10235409", "state": "IN_PROGRESS",
"waybillNumber": null, "carrier": "UPS", "pickupNumber": null,
"priceNet": 61.76, "priceGross": 75.96, "currency": "PLN", "documents": [] } ]
}
Zlecenie od razu trafia do przewoźnika. Numer listu i etykieta pojawiają się zwykle po kilku sekundach: odpytuj GET /orders/{orderId} co kilka sekund, aż waybillNumber i documents się wypełnią (state przesyłki zmienia się z IN_PROGRESS na WAY_BILL).
4. Zlecenie, etykieta, śledzenie
GET /orders/{orderId}: stan zlecenia i przesyłek, numer listu, numer zlecenia odbioru u przewoźnika (pickupNumber), dokumenty.GET /orders?reference=ZAM-2026-000123: to samo po Twoim numerze.GET /documents/{documentId}: plik etykiety (LABEL) albo listu przewozowego (WAYBILL), zwykle PDF.GET /parcels/{parcelId}/tracking: zdarzenia u przewoźnika (status,description,date,location,waybill).
Stany przesyłki: IN_PROGRESS (przekazana do przewoźnika), WAY_BILL (ma numer listu), dalsze stany według przewoźnika, CANCEL (anulowana).
5. Anulowanie
POST /parcels/{parcelId}/cancel z treścią { "reason": "Klient zrezygnował" }:
- wycena jeszcze niezamówiona: anulowana od razu, wynik
CANCELLED; - przesyłka zamówiona: prośba do biura LM Logistic, wynik
REQUESTEDz numerem prośby; biuro anuluje ją u przewoźnika i potwierdza.
Dotychczasowe API (/logistic/api/ext)
Klienci, którzy używali API starego systemu (nagłówek client_token), przechodzą na nowy system, zmieniając tylko adres serwera na https://klient.lmlogistic.pl/logistic/api/ext/…. Ścieżki, pola, jednostki (gramy, grosze) i komunikaty zostają bez zmian. Tylko HTTPS.