Webhooki wychodzące
Zweryfikowane
W skrócie
Dział zatytułowany „W skrócie”Webhook to odwrotność REST i MCP: to nie Państwo pytają, tylko MitoOps się odzywa. Gdy na koncie wystąpi zdarzenie, które jest subskrybowane, wysyłamy na podany adres POST z krótką podpisaną kopertą. Koperta zawiera identyfikatory i statusy, a nie treść zamówienia; szczegóły system odczytuje przez interfejs REST własnym kluczem.
Subskrypcjami zarządza się w aplikacji: Ustawienia → API & MCP → Webhooki wychodzące. Są dostępne od taryfy Start; potrzebne jest uprawnienie Webhooki wychodzące (odczyt lub zarządzanie).
Subskrypcja (endpoint)
Dział zatytułowany „Subskrypcja (endpoint)”Endpoint ma nazwę, adres, listę zdarzeń i zakres e-shopów. Przy zakładaniu otrzymuje się tajny klucz: wyświetla się raz, a potem już nigdy; w każdej chwili można go wymienić (rotacja), a stary natychmiast przestaje obowiązywać.
- Adres musi być publiczny i używać
https://na standardowym porcie. Adresy w sieci wewnętrznej (maszyna lokalna, zakresy prywatne, metadane chmury, zakresy VPN) są odrzucane przy zapisie i przy każdym doręczeniu: nazwa jest tłumaczona ponownie, a połączenie wiązane ze zweryfikowanymi adresami. - Przekierowania nie są wykonywane. Odpowiedź
3xxoznacza niepowodzenie. - E-shopy: wszystkie (także te, które dojdą) lub wybrane. Zdarzenie z e-shopu spoza zakresu nie jest wysyłane.
- Testowe doręczenie z przycisku wysyła zdarzenie
pingtą samą drogą co produkcyjne, łącznie z podpisem i wpisem do dziennika. - Endpoint można wyłączyć (oczekujące doręczenia są pomijane) i usunąć (dziennik pozostaje).
Zdarzenia
Dział zatytułowany „Zdarzenia”| Zdarzenie | Kiedy | resource.type |
Pola w data |
|---|---|---|---|
order.created |
nowe zamówienie z e-shopu | order |
order_code |
order.paid |
zamówienie oznaczone jako opłacone | order |
order_code |
order.status_changed |
zmiana statusu zamówienia | order |
order_code, status_id, previous_status_id |
shipment.created |
przesyłka utworzona u przewoźnika | shipment |
shipment_id, order_code, carrier_code, tracking_number |
shipment.status_changed |
zmiana statusu przesyłki (przewoźnik lub obsługa) | shipment |
shipment_id, order_code, carrier_code, status, previous_status, tracking_number |
shipment.delivered |
przesyłka doręczona; dodatkowo do shipment.status_changed |
shipment |
jak wyżej, status to DELIVERED |
inventory.stock_changed |
ruch zapasu pozycji | inventory_item |
item_id, sku, kind |
invoice.created |
wystawiona faktura | invoice |
invoice_code, order_code |
claim.created |
założona reklamacja (przez klienta lub obsługę) | claim |
claim_code, order_code |
claim.status_changed |
zmiana statusu reklamacji | claim |
claim_code, order_code, status_id |
claim.closed |
reklamacja zamknięta | claim |
claim_code, order_code |
search_console.connection.changed |
zmiana stanu połączenia Search Console (połączenie, zmiana witryny, utrata dostępu, odłączenie) | search_console_property |
market, status, previous_status, property, previous_property, changed_at |
search_console.sync.completed |
synchronizacja archiwum dodała lub zmieniła dni | search_console_property |
market, property, search_types, data_state, final_data_through, fresh_data_through, days_upserted, revised_dates, sync_version, synced_at |
search_console.sync.failed |
synchronizacja archiwum nie powiodła się | search_console_property |
market, property, error, error_code, last_successful_sync_at, retry_at |
product_content.changed |
MitoOps zapisał w sklepie zmieniony opis, krótki opis lub tekst SEO produktu | product |
store_id, market, entity, entity_id, canonical_url, source, source_run_id, changed_fields, before_hash, after_hash, changed_at |
category_content.changed |
MitoOps zapisał w sklepie zmieniony górny lub dolny opis albo tekst SEO kategorii | category |
jak przy product_content.changed |
ping |
testowe doręczenie z przycisku | webhook_endpoint |
message, endpoint |
Zdarzenia pochodzą z tej samej warstwy biznesowej co automatyzacje: to, co uruchamia automatyzację, można subskrybować także webhookiem. Przesyłki zwrotne (zwroty) nie są wysyłane jako shipment.*; należą do reklamacji.
Zmiana tekstów produktu i kategorii
Dział zatytułowany „Zmiana tekstów produktu i kategorii”product_content.changed i category_content.changed powstają raz na zapis i sklep, gdy MitoOps zapisze w sklepie tekst, który naprawdę się zmienił — z karty produktu, edycji zbiorczej, importu, ekranu kategorii, z zatwierdzonej propozycji lub z zapisu autonomicznego. Zmiana ceny, stanu magazynowego czy kodu ani ponowne zapisanie tego samego tekstu nie wywołują zdarzenia. Zmian wprowadzonych bezpośrednio w panelu sklepu to zdarzenie nie zgłasza.
entitytoproductlubcategory;entity_id(orazresource.id) to kod produktu lub ID kategorii w sklepie — te same, którymi czytasz teksty przezproducts_content_geticategories_content_get.store_idto kod sklepu, taki sam jakstore.source:ui(osoba w aplikacji),assistant(asystent w aplikacji),mcp(propozycja przez MCP zatwierdzona przez osobę),autonomous(propozycja zapisana w trybie autonomicznym). Wartośćrestjest zarezerwowana — REST służy dziś tylko do odczytu.source_run_id: ID propozycji przy zapisie zatwierdzonym i autonomicznym; przy pozostałych zapisach produktu ID partii zapisu (Zmiany produktów); przy kategorii zapisanej poza propozycjąnull.changed_fieldswymienia zmienione pola: dla produktudescription,shortDescription,metaTitle,metaDescription, dla kategoriidescription,descriptionBottom,metaTitle,metaDescription.before_hashiafter_hashzawierają dla każdego z nich SHA-256 tekstu w UTF-8 (brak wartości = odcisk pustego ciągu) — tę samą wartość, którą przy odczycie zwraca polehash. Sama koperta tekstu nie zawiera.- Po zatwierdzeniu propozycji
after_hashjest odciskiem tego, co sklep faktycznie zapisał (WordPress usuwa na przykład blok<style>), a nie tego, co zaproponowano. canonical_urlto publiczny adres produktu lub kategorii, jeśli MitoOps go zna; w przeciwnym razienull.
Co Twój klucz może czytać i proponować w danym sklepie oraz czy propozycja zapisze się sama, sprawdzisz z wyprzedzeniem w zasadach treści: GET /api/v1/product-content/policy?market=… lub narzędziem MCP products_content_policy (zakres product-content:read).
Koperta
Dział zatytułowany „Koperta”Każde doręczenie to JSON z wersją koperty. Nowe pole może dojść bez zmiany wersji; zmiana znaczenia pola oznacza nową wersję.
{ "event_id": "9b2f0c1e-6d3a-4c7e-9a2b-0f1e2d3c4b5a", "event_type": "shipment.delivered", "version": 1, "occurred_at": "2026-09-03T08:41:12.318Z", "tenant": "00001", "store": "sk", "resource": { "type": "shipment", "id": "5521" }, "data": { "shipment_id": "5521", "order_code": "2026001234", "carrier_code": "gls", "status": "DELIVERED", "previous_status": "IN_TRANSIT", "tracking_number": "GLS123456789" }}event_idjest unikalne dla zdarzenia. Jeśli to samo zdarzenie subskrybują dwa endpointy, otrzymają to samo ID. Ponowna próba niesie to samo ID i tę samą treść.storeto kod e-shopu (market), ten sam, którego używa REST.tenantto identyfikator konta w MitoOps.
Nagłówki i podpis
Dział zatytułowany „Nagłówki i podpis”Content-Type: application/jsonUser-Agent: MitoOps-Webhooks/1X-MitoOps-Event: shipment.deliveredX-MitoOps-Event-Id: 9b2f0c1e-6d3a-4c7e-9a2b-0f1e2d3c4b5aX-MitoOps-Timestamp: 1756888872X-MitoOps-Signature: v1=3f1a…c9e0X-MitoOps-Delivery: 184Podpis to HMAC-SHA256 z kluczem = tajny klucz endpointu nad ciągiem:
<X-MitoOps-Timestamp> + "." + <dokładna treść żądania>Wynik jest szesnastkowy i w nagłówku ma prefiks wersji v1=. Znacznik czasu to czas uniksowy w sekundach.
Weryfikacja po stronie odbiorcy
Dział zatytułowany „Weryfikacja po stronie odbiorcy”Weryfikację należy wykonywać na surowej treści żądania (bajtach w takiej postaci, w jakiej przyszły), a nie na ponownie zserializowanym JSON-ie: każda zmiana odstępu lub kolejności kluczy unieważnia podpis. Porównanie powinno odbywać się w stałym czasie, a wiadomości starsze niż 5 minut należy odrzucać.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyMitoOpsWebhook({ secret, headers, rawBody, now = Math.floor(Date.now() / 1000) }) { const ts = Number(headers['x-mitoops-timestamp']); if (!Number.isFinite(ts) || Math.abs(now - ts) > 300) return false; // stary lub brakujący znacznik czasu
const received = String(headers['x-mitoops-signature'] || '') .split(',').map((s) => s.trim()).find((s) => s.startsWith('v1=')); if (!received) return false;
const expected = createHmac('sha256', secret) .update(String(ts) + '.').update(rawBody).digest('hex'); const a = Buffer.from(received.slice(3), 'hex'); const b = Buffer.from(expected, 'hex'); return a.length === b.length && timingSafeEqual(a, b);}import hmac, hashlib, time
def verify_mitoops_webhook(secret: str, headers: dict, raw_body: bytes) -> bool: try: ts = int(headers["X-MitoOps-Timestamp"]) except (KeyError, ValueError): return False if abs(int(time.time()) - ts) > 300: return False sig = next((s.strip() for s in headers.get("X-MitoOps-Signature", "").split(",") if s.strip().startswith("v1=")), None) if not sig: return False expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(sig[3:], expected)Ochrona przed powtórzeniem
Dział zatytułowany „Ochrona przed powtórzeniem”Podpis obejmuje znacznik czasu, więc przechwyconej wiadomości nie można później wysłać z nowym czasem. Oprócz kontroli czasu warto przechowywać event_id ostatnich doręczeń i ignorować duplikaty: ponowne doręczenie jest cechą systemu, a nie błędem.
Odpowiedź, ponowienia i dziennik
Dział zatytułowany „Odpowiedź, ponowienia i dziennik”- Należy odpowiedzieć
2xxw ciągu 10 sekund. Treść odpowiedzi nie jest odczytywana. Przetwarzanie, które trwa dłużej, należy przenieść do tła i odpowiedzieć od razu. - Inna odpowiedź (
4xx,5xx), przekroczenie czasu lub błąd połączenia oznacza niepowodzenie i ponowienie z odstępami 1, 5, 15, 60 i 240 minut; łącznie sześć prób. Potem doręczenie ma statusFAILEDi czeka na ręczne ponowienie z dziennika. - Odpowiedź
3xxoraz adres, który przestał się tłumaczyć na sieć publiczną, to niepowodzenia bez ponowienia: same się nie rozwiążą. - Dziennik doręczeń w aplikacji pokazuje dla każdej próby status, kod HTTP, czas, liczbę prób i zdarzenie. Treść wiadomości nie jest w dzienniku wyświetlana.
| Status | Znaczenie |
|---|---|
PENDING |
czeka na próbę lub kolejne ponowienie |
SENT |
odbiorca odpowiedział 2xx |
FAILED |
próby wyczerpane lub błąd trwały; można ponowić ręcznie |
SKIPPED |
endpoint został w międzyczasie wyłączony lub usunięty |
Bezpieczeństwo
Dział zatytułowany „Bezpieczeństwo”- Tajny klucz jest przechowywany w postaci zaszyfrowanej i nigdy nie jest zapisywany w logach ani w dzienniku. Wyświetla się raz; rotacja działa także wtedy, gdy taryfa nie obejmuje już webhooków, ponieważ jest to operacja bezpieczeństwa.
- Bez danych osobowych. Koperta zawiera identyfikatory i statusy. Imię i nazwisko, adres ani e-mail klienta nie są wysyłane; kto ich potrzebuje, odczytuje je przez REST z własnym zakresem i audytem.
- Tylko HTTPS, tylko adresy publiczne, bez przekierowań. Kontrola przy zapisie i przy każdym doręczeniu chroni przed kierowaniem do sieci wewnętrznej i przed zmianą DNS.
- Subskrypcję zakłada człowiek, a nie klucz. Klucz dostępu do REST ani zgoda OAuth nie mogą założyć subskrypcji: odczyt na żądanie i stały strumień zdarzeń na zewnątrz to dwa różne uprawnienia.
- Izolacja. Subskrypcje i dziennik znajdują się w przestrzeni konta; zakres e-shopów jest oceniany przy każdym zdarzeniu.
| Taryfa | Webhooki wychodzące | Endpointów |
|---|---|---|
| Wersja próbna | nie | 0 |
| Początkujący | nie | 0 |
| Start | tak | 5 |
| Wzrost | tak | 20 |
| Pro | tak | 50 |
| Na miarę | według umowy | według umowy |
Liczą się nieusunięte endpointy, włączone i wyłączone. Obniżenie taryfy nie usuwa istniejących endpointów; nowy nie zostanie dodany, dopóki liczba nie spadnie poniżej limitu, a przy taryfie bez webhooków zdarzenia przestają być wysyłane do czasu przywrócenia taryfy.
Webhook z automatyzacji
Dział zatytułowany „Webhook z automatyzacji”Endpoint można użyć także jako kroku Wyślij webhook w Automatyzacjach: wiadomość wychodzi dopiero po spełnieniu warunków reguły, a nie przy każdym zdarzeniu. Krok tylko wybiera endpoint; adres, tajny klucz i zakres e-shopów pozostają w rejestrze.
Wiadomość ma typ automation.action (nie można go subskrybować bezpośrednio) oraz taką samą kopertę, podpis, ponowienia i dziennik jak pozostałe zdarzenia. W data zawiera automation.workflow_id, automation.run_id, automation.node_id, automation.source_event (zdarzenie, które wywołało regułę) oraz identyfikatory zamówienia, przesyłki lub zgłoszenia reklamacyjnego; resource to encja przebiegu. Szczegóły odczytuje się przez REST API.
Automatyczne wstrzymanie
Dział zatytułowany „Automatyczne wstrzymanie”Jeśli odbiorca nie odpowie na 30 kolejnych prób lub endpoint ma więcej niż 5 000 oczekujących doręczeń, MitoOps wstrzymuje endpoint i wysyła powiadomienie. Oczekujące doręczenia pozostają; po naprawie odbiorcy należy włączyć endpoint, a doręczenia zostaną zrealizowane. Dziennik doręczeń jest przechowywany przez 30 dni.