{"components":{"schemas":{"AddAttachmentRequest":{"properties":{"attachment_type":{"enum":["POD_SCAN","METER_PHOTO","SITE_PHOTO","INVOICE_PDF"],"example":"POD_SCAN","type":"string"},"file_name":{"example":"wz_podpisana_123.pdf","type":"string"},"file_size_bytes":{"example":1048576,"type":"integer"},"file_url":{"example":"https://storage.partner.pl/docs/wz_123.pdf","type":"string"},"mime_type":{"example":"application/pdf","type":"string"}},"required":["attachment_type","file_name","file_url"],"type":"object"},"CancelRequest":{"properties":{"reason_code":{"enum":["out_of_stock","no_transport_capacity","area_out_of_range","client_unreachable","force_majeure","other"],"example":"out_of_stock","type":"string"},"reason_description":{"example":"Brak pelletu danej granulacji na magazynie","type":"string"}},"required":["reason_code"],"type":"object"},"CompleteServiceRequest":{"properties":{"completed_notes":{"example":"Dostarczono i rozładowano bez uwag.","type":"string"},"delivered_quantity":{"example":2150,"type":"number"},"labor_hours":{"example":2.5,"type":"number"},"meter_reading_after":{"example":144200,"type":"number"},"meter_reading_before":{"example":142050,"type":"number"},"parts_replaced":{"items":{"type":"string"},"type":"array"},"signature_base64":{"example":"data:image/png;base64,iVBORw0KGgoAAA...","type":"string"},"signature_name":{"example":"Jan Kowalski","type":"string"},"work_performed":{"example":"Wymiana pompy obiegowej","type":"string"}},"required":["signature_base64"],"type":"object"},"DeclineRequest":{"properties":{"reason_code":{"enum":["out_of_stock","no_transport_capacity","area_out_of_range","price_unfeasible","other"],"example":"price_unfeasible","type":"string"},"reason_description":{"example":"Zbyt niski budżet klienta w stosunku do kosztu dojazdu","type":"string"}},"required":["reason_code"],"type":"object"},"IntegrationServiceRequest":{"properties":{"cod_amount_gross":{"type":"number"},"customer_address":{"type":"string"},"customer_name":{"type":"string"},"customer_phone":{"type":"string"},"emergency_type":{"type":"string"},"fulfillment_short_url":{"description":"Publiczny /f/{kod} dla kierowcy bez aplikacji","type":"string"},"id":{"type":"integer"},"is_external_order":{"description":"true = Stream B (klient składu), false = platforma","type":"boolean"},"payment_channel":{"description":"Widoczne dla przypisanego dostawcy po akceptacji","enum":["cash","online","klarna"],"type":"string"},"pod_pdf_url":{"description":"Relatywna ścieżka GET PDF POD (tylko po COMPLETED)","type":"string"},"route_run_id":{"description":"Kurs dnia UgrzejMnie (nie trasa TMS)","type":"integer"},"status":{"type":"string"},"stop_order":{"type":"integer"},"weight_kg":{"description":"Znormalizowana waga (tony \u003c 50 liczone jako t→kg)","type":"number"}},"type":"object"},"LogisticsEnRouteRequest":{"properties":{"driver_name":{"example":"Tomasz Nowak","type":"string"},"estimated_arrival_minutes":{"example":45,"type":"integer"},"vehicle_plate":{"example":"WI 12345","type":"string"}},"type":"object"},"ManualDeliveryRequest":{"properties":{"cod_amount_gross":{"example":1450,"type":"number"},"customer_email":{"type":"string"},"customer_name":{"example":"Anna Kowalska","type":"string"},"customer_pesel":{"description":"PESEL lub nr dowodu — szyfrowany at rest","type":"string"},"customer_phone":{"example":"501202303","type":"string"},"delivery_address":{"example":"ul. Polna 12, Mrozy","type":"string"},"driver_email":{"type":"string"},"driver_name":{"type":"string"},"emergency_type":{"example":"heatingFuelDelivery","type":"string"},"fuel_type":{"example":"PELLET","type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"notes":{"type":"string"},"packaging":{"type":"string"},"payment_method":{"enum":["cash","online","prepaid"],"type":"string"},"quantity":{"example":2,"type":"number"}},"required":["customer_name","customer_phone","delivery_address"],"type":"object"},"OfferItem":{"properties":{"name":{"example":"Pellet drzewny certyfikowany ENplus A1 worki 15kg","type":"string"},"quantity":{"example":2,"type":"number"},"sku_code":{"example":"PELLET_A1_15KG","type":"string"},"total_gross":{"example":3444,"type":"number"},"unit":{"example":"T","type":"string"},"unit_price_net":{"example":1400,"type":"number"},"vat_rate":{"example":0.23,"type":"number"}},"required":["sku_code","name","unit","quantity","unit_price_net","vat_rate","total_gross"],"type":"object"},"PatchOfferRequest":{"properties":{"approx_fulfillment_at":{"format":"date-time","type":"string"},"assistance_cost_gross":{"type":"number"},"description":{"type":"string"},"estimated_duration_hours":{"type":"number"},"items":{"items":{"$ref":"#/components/schemas/OfferItem"},"type":"array"},"travel_cost_gross":{"type":"number"},"unloading_method":{"type":"string"}},"type":"object"},"RescheduleRequest":{"properties":{"new_fulfillment_at":{"example":"2026-09-03T14:00:00Z","format":"date-time","type":"string"},"reason":{"example":"Awaria pojazdu","type":"string"}},"required":["new_fulfillment_at"],"type":"object"},"SubmitOfferRequest":{"properties":{"approx_fulfillment_at":{"example":"2026-09-02T10:00:00Z","format":"date-time","type":"string"},"assistance_cost_gross":{"example":3444,"type":"number"},"description":{"example":"Dostawa pelletu workowanego z rozładunkiem HDS.","type":"string"},"estimated_duration_hours":{"example":1.5,"type":"number"},"items":{"items":{"$ref":"#/components/schemas/OfferItem"},"type":"array"},"travel_cost_gross":{"example":0,"type":"number"},"unloading_method":{"example":"HDS","type":"string"}},"required":["assistance_cost_gross"],"type":"object"}},"securitySchemes":{"ApiKeyAuth":{"description":"Klucz integracyjny dostawcy (format: wmu_live_...)","in":"header","name":"X-API-Key","type":"apiKey"}}},"info":{"contact":{"email":"kontakt@ugrzejmnie.pl","name":"Wsparcie Integracji UgrzejMnie","url":"https://ugrzejmnie.pl/integrations/docs"},"description":"Oficjalny interfejs REST API dla systemów magazynowo-handlowych (ERP/WMS) oraz logistyczno-spedycyjnych (TMS) partnerów dostarczających paliwa grzewcze (pellet, węgiel, olej opałowy, gaz) oraz serwis instalacji.\n\nChangelog 2.2.0: POST /service-requests/manual-delivery (Stream B z klucza API), GET /service-requests/{id}/pod.pdf, pole pod_pdf_url po COMPLETED; PESEL/nr dowodu szyfrowany at rest (AES-GCM).\n\nChangelog 2.1.0: zlecenia Stream B (is_external_order), COD/kanał płatności, kurs dnia (route_run_id, stop_order), fulfillment_short_url i weight_kg na GET /service-requests. Eventy order.created także dla dostaw własnych i „z trasy”; order.completed także po podpisie kierowcy na /f/. Complete z TMS przelicza kurs i wysyła e-WZ. Powtórny complete z TMS = 200 already_completed (HTML kierowcy = 409). Zdjęcie rozładunku nie jest wymagane.","title":"UgrzejMnie ERP \u0026 TMS External Integration API","version":"2.2.0"},"openapi":"3.0.3","paths":{"/events":{"get":{"description":"Sekwencyjny dziennik zdarzeń z filtrem after_id i since. Od 2.1.0: order.created także dla Stream B (manual-delivery, z trasy); order.completed także po podpisie kierowcy na /f/ (nie tylko POST /complete z TMS). Webhook jest cienki (event + request_id) — pełny stan bierz z GET /service-requests/{id}.","parameters":[{"in":"query","name":"after_id","schema":{"default":0,"type":"integer"}},{"in":"query","name":"limit","schema":{"default":100,"type":"integer"}},{"in":"query","name":"since","schema":{"format":"date-time","type":"string"}}],"responses":{"200":{"description":"Strumień zdarzeń"}},"summary":"Dziennik zdarzeń (Replay \u0026 Polling kursorowy)"}},"/ping":{"get":{"description":"Sprawdza aktywność klucza API i zwraca nazwę oraz ID dostawcy.","responses":{"200":{"description":"Połączenie aktywne"},"401":{"description":"Nieprawidłowy lub nieaktywny klucz API"}},"summary":"Weryfikacja klucza i połączenia"}},"/sandbox/create-test-request":{"post":{"responses":{"200":{"description":"Utworzono zapytanie testowe"}},"summary":"Utwórz izolowane zapytanie sandbox (pellet | heat_pump | lpg)"}},"/sandbox/simulate-accept/{id}":{"post":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Zaakceptowano sandbox"},"422":{"description":"NOT_SANDBOX_REQUEST / NO_OFFER_TO_ACCEPT"}},"summary":"Zasymuluj akceptację oferty przez klienta demonstracyjnego"}},"/service-requests":{"get":{"description":"Zwraca listę zleceń z podziałem na statusy (PENDING, ACCEPTED, IN_PROGRESS, COMPLETED, CANCELLED) z paginacją. PII klienta są maskowane do czasu akceptacji. Od 2.1.0: is_external_order, weight_kg, a dla przypisanego składu także COD, payment_channel, route_run_id, stop_order, fulfillment_short_url. Od 2.2.0: pod_pdf_url po COMPLETED. Po evencie GET /service-requests/{id} jest źródłem prawdy.","parameters":[{"description":"Filtruj wg statusu (np. PENDING, ACCEPTED)","in":"query","name":"status","schema":{"type":"string"}},{"in":"query","name":"page","schema":{"default":1,"type":"integer"}},{"in":"query","name":"limit","schema":{"default":20,"type":"integer"}},{"in":"query","name":"updated_since","schema":{"format":"date-time","type":"string"}}],"responses":{"200":{"description":"Lista zleceń"}},"summary":"Pobranie lejka zapytań i zleceń z filtrowaniem"}},"/service-requests/manual-delivery":{"post":{"description":"Tworzy zlecenie klienta składu (is_external_order=true), generuje link/QR dla kierowcy (fulfillment_short_url) i emituje order.created. PESEL/nr dowodu szyfrowany at rest. Chronione Idempotency-Key.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualDeliveryRequest"}}},"required":true},"responses":{"201":{"description":"Zlecenie utworzone — fulfillment_short_url dla kierowcy"},"400":{"description":"INVALID_PAYLOAD"},"401":{"description":"UNAUTHORIZED"}},"summary":"Utworzenie zlecenia własnego (Stream B)"}},"/service-requests/{id}":{"get":{"description":"Zwraca pełny stan pojedynczego zlecenia do uzgodnienia statusu i retry.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Szczegóły zlecenia"},"404":{"description":"Zlecenie nie znalezione"}},"summary":"Pojedyncze źródło prawdy zlecenia"}},"/service-requests/{id}/arrive":{"post":{"description":"Rejestruje przybycie kierowcy/serwisanta na adres klienta.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Zarejestrowano dotarcie"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Status TMS: Dotarcie na miejsce"}},"/service-requests/{id}/attachments":{"get":{"description":"Zwraca zarejestrowane dokumenty dla zaakceptowanego zlecenia.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Lista załączników"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Pobranie listy załączników zlecenia"},"post":{"description":"Rejestruje załącznik dla zaakceptowanego zlecenia (max 20MB, dozwolone: PDF, PNG, JPG, WebP).","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAttachmentRequest"}}},"required":true},"responses":{"200":{"description":"Załącznik zarejestrowany"},"403":{"description":"Brak uprawnień do tego zlecenia"},"422":{"description":"Nieobsługiwany MIME, przekroczony rozmiar lub zlecenie w statusie PENDING"}},"summary":"Dodanie załącznika wykonawczego (POD, WZ, Licznik, Faktura)"}},"/service-requests/{id}/cancel":{"post":{"description":"Anuluje zlecenie przez przypisanego wykonawcę.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"}}},"required":true},"responses":{"200":{"description":"Zlecenie anulowane"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Anulowanie zlecenia z kodem przyczyny"}},"/service-requests/{id}/complete":{"post":{"description":"Zamyka zlecenie w UgrzejMnie, zapisuje podpis cyfrowy klienta, stany licznika cysterny oraz zużyte części/robociznę, rozlicza prowizję, przelicza kurs dnia (jeśli zlecenie na nim siedzi) i wysyła e-WZ. Zdjęcie rozładunku nie jest wymagane. Powtórny POST na już COMPLETED zwraca 200 z error_code ALREADY_COMPLETED (to nie jest błąd). Kierowca na stronie /f/ dostaje 409 — nie mieszaj konwencji. Chronione kluczem Idempotency-Key.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}},{"description":"UUIDv4 chroniący przed duplikacją operacji","in":"header","name":"Idempotency-Key","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteServiceRequest"}}},"required":true},"responses":{"200":{"description":"Zlecenie zakończone pomyślnie"},"400":{"description":"Brak podpisu cyfrowego klienta"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Zakończenie realizacji z podpisem, licznikiem paliwa lub protokołem prac"}},"/service-requests/{id}/decline":{"post":{"description":"Ukrywa zapytanie z listy oczekujących (dozwolone wyłącznie dla statusów PENDING/QUOTED).","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeclineRequest"}}},"required":true},"responses":{"200":{"description":"Zapytanie ukryte"},"422":{"description":"Zlecenie jest już zaakceptowane – należy użyć /cancel"}},"summary":"Odrzucenie zapytania ofertowego"}},"/service-requests/{id}/en-route":{"post":{"description":"Zmienia status na IN_TRANSIT i rejestruje dane pojazdu/kierowcy.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogisticsEnRouteRequest"}}}},"responses":{"200":{"description":"Zarejestrowano wyjazd w trasę"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Status TMS: Kierowca wyjechał w trasę"}},"/service-requests/{id}/offers":{"post":{"description":"Wysyła ofertę do klienta z rozbiciem na pozycje towarowe items[] oraz metodę rozładunku. Wymaga zgodności sumy items[] z assistance_cost_gross.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}},{"description":"UUIDv4 chroniący przed duplikacją operacji","in":"header","name":"Idempotency-Key","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitOfferRequest"}}},"required":true},"responses":{"200":{"description":"Oferta złożona pomyślnie"},"409":{"description":"Dostawca złożył już aktywną ofertę do tego zlecenia"},"422":{"description":"Niezgodność sumy pozycji towarowych brutto z kwotą assistance_cost_gross"}},"summary":"Złożenie oferty cenowej z pozycjami SKU"}},"/service-requests/{id}/offers/{offer_id}":{"patch":{"description":"Modyfikuje kwotę, pozycje lub termin przed wyborem oferty przez klienta.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"offer_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchOfferRequest"}}},"required":true},"responses":{"200":{"description":"Oferta zaktualizowana"},"403":{"description":"Oferta należy do innego dostawcy"},"409":{"description":"Oferta została już zaakceptowana przez klienta"}},"summary":"Korekta aktywnej oferty"}},"/service-requests/{id}/offers/{offer_id}/withdraw":{"post":{"description":"Wycofuje złożoną wycenę przed jej zaakceptowaniem.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}},{"in":"path","name":"offer_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Oferta wycofana"},"409":{"description":"Oferta nie może być wycofana w obecnym statusie"}},"summary":"Wycofanie oferty"}},"/service-requests/{id}/pod.pdf":{"get":{"description":"Zwraca wygenerowany PDF potwierdzenia odbioru (podpis, litry/towar, GPS, oświadczenie). Dostępne tylko dla COMPLETED zlecenia należącego do dostawcy z klucza API.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"application/pdf"},"403":{"description":"FORBIDDEN"},"409":{"description":"NOT_COMPLETED"}},"summary":"Pobranie PDF POD (Proof of Delivery)"}},"/service-requests/{id}/reschedule":{"post":{"description":"Aktualizuje planowaną datę dostawy w rekordzie zlecenia i emituje zdarzenie.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleRequest"}}},"required":true},"responses":{"200":{"description":"Termin zaktualizowany"},"403":{"description":"Brak uprawnień do tego zlecenia"}},"summary":"Status TMS: Zmiana terminu realizacji"}},"/test-webhook":{"post":{"responses":{"200":{"description":"Wysłano"},"422":{"description":"WEBHOOK_URL_NOT_SET"}},"summary":"Wyślij próbny webhook ping / order.test na URL partnera"}}},"security":[{"ApiKeyAuth":[]}],"servers":[{"description":"Środowisko produkcyjne","url":"https://ugrzejmnie.pl/api/v1/integrations"}]}