Repository navigation
API Reference
Ta strona jest dla deweloperów budujących własny frontend (headless PWA, aplikacja mobilna,
integracja z zewnętrznym CRM/ERP) na module Ordo Automation. Opisuje wszystkie publiczne
endpointy HTTP: REST API (/rest/V1/ordo/...) oraz publiczne, niezalogowane endpointy śledzące
(/ordo/track/...), z których korzysta natywny skrypt tracker.js, ale które może wołać
dowolny klient.
Endpointy panelu admina (admin/ordo/...) nie są tu opisane — to interfejs administracyjny
Magento, wymagający sesji admina i ACL, nie przeznaczony do integracji.
Ten moduł ma dwie oddzielne granice bezpieczeństwa, w zależności od grupy endpointów:
-
REST API (
/rest/V1/ordo/...) — standardowe uwierzytelnianie Magento: token OAuth/Bearer (integracja) albo token klienta (customertoken, przez/rest/V1/customers/melub logowanie). Każdy route ma zadeklarowany zasób ACL (resourceswwebapi.xml):-
Ordo_Automation::campaigns,Ordo_Automation::config,Ordo_Automation::free_gifts— wymagają tokena admina z odpowiednim ACL. -
self— wymaga tokena klienta (dowolnego zalogowanego), własność konkretnego zasobu (np. koszyka, oferty) jest sprawdzana wewnątrz serwisu, nie przez ACL. -
anonymous— brak wymogu tokena. Tylko dwa endpointy: zatwierdzenie/odrzucenie zamówienia przez token z linku e-mail (patrz sekcja Order Approval niżej).
-
-
Endpointy śledzące (
/ordo/track/...) — brak tokena, brak CSRF (poza wyjątkiem opisanym przy rejestracji push/price-watch). Identyfikacja odbywa się przez:- cookie
ordo_visitor_id(UUID, ustawiane przeztracker.js, żywotność 365 dni) — dla anonimowych odwiedzających, przekazywane jako parametrvisitor_id; - sesję klienta Magento (
Magento\Customer\Model\Session) — jeśli klient jest zalogowany, jegocustomer_idjest brane automatycznie z sesji (ciasteczko sesyjne PHP), nie trzeba go przekazywać w żądaniu.
To ten sam model zaufania co dowolny "piksel" analityczny — każdy, kto ma poprawny
visitor_id, może odpytać te endpointy. Nie umieszczaj w nich nic wrażliwego. - cookie
Wszystkie POST-y jako application/x-www-form-urlencoded (fetch z URLSearchParams albo
klasyczny FormData). Odpowiedzi zawsze Content-Type: application/json.
Loguje surowe zdarzenie zachowania (podgląd produktu/kategorii, kliknięcie elementu) — źródło automatycznego tagowania po zachowaniu (patrz Śledzenie i popupy).
| Parametr | Typ | Wymagany | Opis |
|---|---|---|---|
visitor_id |
string | tak | UUID odwiedzającego |
event_type |
string | tak | jedno z: page_view, product_view, category_view, element_clicked
|
event_key |
string | nie | identyfikator kontekstu, np. SKU produktu; max 255 znaków, dłuższy jest ucinany |
Odpowiedź: {"ok": true} albo {"ok": false, "reason": "tracking_disabled" | "invalid_payload"}.
curl -X POST "https://twoj-sklep.pl/ordo/track/event" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "event_type=product_view" \
-d "event_key=24-MB01"Odpytuje o jednorazowy popup zakolejkowany przez akcję kampanii show_popup. Konsumuje
wynik — ten sam popup nigdy nie wróci drugi raz (claim-before-deliver), więc trzeba pollować
regularnie (natywny klient robi to co 25s, pierwszy raz po 2s).
Odpowiedź: {"popup": {"headline": "...", "body": "...", "cta_label": "...", "cta_url": "..."} | null}.
curl "https://twoj-sklep.pl/ordo/track/popup?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Odpytuje o wszystkie nieprzeczytane, nieprzedawnione trwałe powiadomienia (akcja kampanii
show_notification). W odróżnieniu od popupu, nie konsumuje niczego — ten sam zestaw wraca
przy każdym poll, aż zostanie jawnie odrzucony.
Odpowiedź: {"notifications": [{"id": 123, "headline": "...", "body": "...", "cta_label": "...", "cta_url": "..."}]}.
curl "https://twoj-sklep.pl/ordo/track/notification?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Odrzuca powiadomienie (ustawia read_at).
| Parametr | Typ | Wymagany |
|---|---|---|
visitor_id |
string | tak (dla anonimowego) |
notification_id |
int | tak |
Odpowiedź: {"ok": true} albo {"ok": false} — w tym gdy notification_id nie należy do
wołającego (sprawdzane po customer_id lub visitor_id).
curl -X POST "https://twoj-sklep.pl/ordo/track/dismissnotification" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "notification_id=123"Odpytuje o jednorazową ankietę NPS/satysfakcji (akcja kampanii nps_survey). Claim-before-deliver,
jak popup.
Odpowiedź: {"survey": {"id": 456, "question": "..."} | null}.
curl "https://twoj-sklep.pl/ordo/track/survey?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Zapisuje odpowiedź na ankietę.
| Parametr | Typ | Wymagany | Opis |
|---|---|---|---|
visitor_id |
string | tak (dla anonimowego) | |
survey_id |
int | tak |
id z odpowiedzi endpointu survey
|
score |
int | tak | 0-10 |
Odpowiedź: {"ok": true} albo {"ok": false} — odrzucane gdy ankieta już odpowiedziana, nie
istnieje, albo nie należy do wołającego.
curl -X POST "https://twoj-sklep.pl/ordo/track/submitsurveyresponse" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "survey_id=456" \
-d "score=9"Rejestruje subskrypcję Web Push (po PushManager.subscribe() w przeglądarce).
| Parametr | Typ | Wymagany |
|---|---|---|
endpoint |
string | tak |
p256dh |
string | tak |
auth |
string | tak |
Odpowiedź: {"ok": true} albo {"ok": false, "reason": "push_disabled" \| "invalid_payload" \| "invalid_endpoint"}.
Uwaga o CSRF: dla zalogowanego klienta ten endpoint sprawdza nagłówek Origin/Referer
(brak klasycznego form_key, bo to gołe fetch()) — musi zgadzać się z domeną sklepu.
Anonimowe rejestracje (brak sesji) nie mają tego wymogu.
curl -X POST "https://twoj-sklep.pl/ordo/track/registerpushsubscription" \
-d "endpoint=https://fcm.googleapis.com/fcm/send/xyz123" \
-d "p256dh=BNcRd..." \
-d "auth=tBHIt..."| Parametr | Typ | Wymagany |
|---|---|---|
endpoint |
string | tak |
Odpowiedź: {"ok": true} albo {"ok": false, "reason": "invalid_payload"}.
curl -X POST "https://twoj-sklep.pl/ordo/track/unregisterpushsubscription" \
-d "endpoint=https://fcm.googleapis.com/fcm/send/xyz123"Rejestruje subskrypcję "powiadom mnie" na PDP (spadek ceny / powrót na stan).
| Parametr | Typ | Wymagany | Opis |
|---|---|---|---|
product_id |
int | tak | |
watch_type |
string | tak |
price_drop albo back_in_stock
|
Odpowiedź: {"ok": true} albo {"ok": false, "reason": "price_watch_disabled" \| "invalid_payload" \| "invalid_product"}.
Ten sam model CSRF co registerpushsubscription.
curl -X POST "https://twoj-sklep.pl/ordo/track/registerpricewatch" \
-d "product_id=123" \
-d "watch_type=price_drop"Standardowe REST API Magento — Content-Type: application/json, nagłówek
Authorization: Bearer <token>. Pełny, aktualny opis parametrów/typów zawsze dostępny przez
Swagger Magento (/rest/V1/schema?services=ordoAutomationAll), tu tylko mapa endpointów.
-
GET/POST /V1/ordo/campaigns,GET/PUT/DELETE /V1/ordo/campaigns/:entityId -
GET/POST /V1/ordo/campaign-triggers,GET/PUT/DELETE /V1/ordo/campaign-triggers/:entityId -
GET/POST /V1/ordo/campaign-conditions,GET/PUT/DELETE /V1/ordo/campaign-conditions/:entityId -
GET/POST /V1/ordo/campaign-actions,GET/PUT/DELETE /V1/ordo/campaign-actions/:entityId
Triggery/warunki/akcje są płaskimi zasobami filtrowanymi po campaign_id przez standardowe
searchCriteria Magento (?searchCriteria[filterGroups][0][filters][0][field]=campaign_id&...),
nie zagnieżdżonym URL-em.
curl "https://twoj-sklep.pl/rest/V1/ordo/campaigns" \
-H "Authorization: Bearer $ADMIN_TOKEN"Podstawowy mechanizm segmentacji ("wyślij do wszystkich z tagiem X") — headless storefront/CRM może zarządzać tagami bez sesji admina w panelu.
-
GET /V1/ordo/customers/:customerId/tags— lista tagów klienta -
GET /V1/ordo/customers/:customerId/tags/:tag— czy klient ma dany tag (bool) -
PUT /V1/ordo/customers/:customerId/tags/:tag— dodaje tag (odpala triggertag_added) -
DELETE /V1/ordo/customers/:customerId/tags/:tag— usuwa tag -
GET /V1/ordo/tags/:tag/customers— lista ID klientów z danym tagiem
curl -X PUT "https://twoj-sklep.pl/rest/V1/ordo/customers/42/tags/vip" \
-H "Authorization: Bearer $ADMIN_TOKEN"-
GET/POST /V1/ordo/offers,GET/PUT/DELETE /V1/ordo/offers/:entityId— ACL admina (Ordo_Automation::config) -
POST /V1/ordo/offers/:offerId/self-extend—self, token dowolnego zalogowanego klienta; własność konkretnej oferty sprawdzana wewnątrz (nie ACL) — klient może samodzielnie przedłużyć swoją ofertę o kolejny okres.
curl -X POST "https://twoj-sklep.pl/rest/V1/ordo/offers/15/self-extend" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"-
GET /V1/ordo/order-approvals,GET /V1/ordo/order-approvals/:entityId— tylko odczyt, ACL admina (Ordo_Automation::config). Sam token decyzyjny nigdy nie jest tu zwracany. -
POST /V1/ordo/order-approvals/:token/approvei.../:token/reject—anonymous, bez żadnego tokena OAuth/customer. Posiadanie:token(ten sam, co w linku e-mail do admina) jest jedynym poświadczeniem — identyczny model zaufania jak link "kliknij, aby zatwierdzić" w mailu. -
GET /V1/ordo/order-approvals/:entityId/decision-links— ACL admina; zwraca gotowe URL-e approve/reject (z wbudowanym tokenem) bez odsłaniania samego tokena jako osobnego pola — przydatne np. dla aplikacji mobilnej handlowca.
curl -X POST "https://twoj-sklep.pl/rest/V1/ordo/order-approvals/a1b2c3d4e5f6/approve"- CRUD dla
free-gift-offers,free-gift-offer-tiers,free-gift-offer-products— ACL admina (Ordo_Automation::free_gifts) -
GET /V1/ordo/carts/:cartId/free-gift-eligibility—self; działa też dla koszyka gościa (bez sprawdzania własności, gdy nie ma zalogowanego klienta) -
PUT /V1/ordo/carts/:cartId/free-gifts—self; wybór konkretnych SKU z dostępnego puli
curl "https://twoj-sklep.pl/rest/V1/ordo/carts/98/free-gift-eligibility" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"
curl -X PUT "https://twoj-sklep.pl/rest/V1/ordo/carts/98/free-gifts" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"skus": ["FREE-GIFT-SKU-1"]}'-
GET /V1/ordo/credit-limit/mine—self; rozwiązuje klienta z samego tokena, żadencustomerIdnie jest potrzebny (i nie da się "zgadnąć" cudzego limitu) -
GET /V1/ordo/customers/:customerId/credit-limit— ACL admina (Ordo_Automation::config)
curl "https://twoj-sklep.pl/rest/V1/ordo/credit-limit/mine" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"-
GET /V1/ordo/reorder-cycles,GET /V1/ordo/reorder-cycles/:entityId— ACL admina (Ordo_Automation::config); liczone przez cron, nie zapisywalne przez API.
Zasila trigger kampanii webhook_received — dowolny zewnętrzny system (ERP, CRM, PIM) może
odpalić kampanię, wysyłając dowolny JSON.
Uwierzytelnianie: nagłówek X-Ordo-Signature: sha256=<hex> — HMAC-SHA256 liczony z
surowego ciała żądania, kluczem jest sekret ustawiony w konfiguracji (Sklepy → Konfiguracja →
Ordo Automation → Webhooks). Ten sam format co Meta/WhatsApp (X-Hub-Signature-256).
Przykład liczenia sygnatury (Node.js):
const crypto = require('crypto');
const signature = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');Odpowiedzi: 200 {"ok": true} / 401 {"ok": false} (błędna sygnatura) / 404 {"ok": false}
(webhook wyłączony w konfiguracji) / 500 {"ok": false} (błąd dispatchu kampanii).
Payload JSON trafia do kontekstu kampanii jako webhook_payload — warunki/akcje kampanii mogą
go odczytać przez pole "Params (JSON)".
Przykład (curl):
BODY='{"order_number":"12345","event":"shipped"}'
SECRET="twoj-sekret-z-konfiguracji"
SIGNATURE="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)"
curl -X POST "https://twoj-sklep.pl/ordo/webhook/receive" \
-H "Content-Type: application/json" \
-H "X-Ordo-Signature: $SIGNATURE" \
-d "$BODY"// 1. Odczytaj/wygeneruj visitor_id (jeśli tracker.js nie jest ładowany, trzeba to zrobić samemu
// i ustawić jako cookie ordo_visitor_id, żeby kampanie widziały tego samego odwiedzającego).
const visitorId = getOrCreateVisitorIdCookie();
// 2. Zaloguj zdarzenie
await fetch('/ordo/track/event', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({visitor_id: visitorId, event_type: 'product_view', event_key: sku}),
});
// 3. Pollowanie popupu (co 25s, pierwszy raz po 2s)
setInterval(async () => {
const res = await fetch(`/ordo/track/popup?visitor_id=${visitorId}`);
const {popup} = await res.json();
if (popup) renderPopup(popup);
}, 25000);This page is for developers building their own frontend (headless PWA, mobile app, an
integration with an external CRM/ERP) against the Ordo Automation module. It documents every
public HTTP endpoint: the REST API (/rest/V1/ordo/...) and the public, unauthenticated
tracking endpoints (/ordo/track/...) that the bundled tracker.js script uses — but that any
client can call directly.
Admin-panel endpoints (admin/ordo/...) are out of scope here — those are Magento's own admin
UI, requiring an admin session and ACL, not meant for integration.
This module has two separate trust boundaries, depending on the endpoint group:
-
REST API (
/rest/V1/ordo/...) — standard Magento auth: an OAuth/integration token, or a customer token (via/rest/V1/customers/melogin). Every route declares an ACL resource (resourcesinwebapi.xml):-
Ordo_Automation::campaigns,Ordo_Automation::config,Ordo_Automation::free_gifts— require an admin token with the matching ACL. -
self— requires any logged-in customer's token; ownership of the specific resource (a cart, an offer) is checked inside the service, not by ACL. -
anonymous— no token at all. Only two routes use this: approving/rejecting an order via the email-link token (see Order Approval below).
-
-
Tracking endpoints (
/ordo/track/...) — no token, no CSRF (except where noted for push/price-watch registration). Identity comes from:- the
ordo_visitor_idcookie (a UUID set bytracker.js, 365-day lifetime) — for anonymous visitors, passed as thevisitor_idparameter; - the Magento customer session (
Magento\Customer\Model\Session) — if the visitor is logged in, theircustomer_idis read automatically from the session (PHP session cookie), no need to pass it in the request.
Same trust model as any analytics "pixel" — anyone who has a valid
visitor_idcan query these endpoints. Don't put anything sensitive in them. - the
All POSTs are application/x-www-form-urlencoded (a fetch call with URLSearchParams, or
classic FormData). Responses are always Content-Type: application/json.
Logs a raw behavior event (product/category view, element click) — the source data for automatic behavior-based tagging (see Tracking & Popups).
| Param | Type | Required | Notes |
|---|---|---|---|
visitor_id |
string | yes | the visitor's UUID |
event_type |
string | yes | one of: page_view, product_view, category_view, element_clicked
|
event_key |
string | no | a context identifier, e.g. a product SKU; max 255 chars, longer is truncated |
Response: {"ok": true} or {"ok": false, "reason": "tracking_disabled" | "invalid_payload"}.
curl -X POST "https://your-store.com/ordo/track/event" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "event_type=product_view" \
-d "event_key=24-MB01"Polls for a one-shot popup queued by a campaign's show_popup action. This consumes the
result — the same popup is never returned twice (claim-before-deliver), so poll regularly (the
bundled client polls every 25s, first poll after 2s).
Response: {"popup": {"headline": "...", "body": "...", "cta_label": "...", "cta_url": "..."} | null}.
curl "https://your-store.com/ordo/track/popup?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Polls for every unread, unexpired persistent notification (a campaign's show_notification
action). Unlike the popup endpoint, this does not consume anything — the same set comes back
on every poll until explicitly dismissed.
Response: {"notifications": [{"id": 123, "headline": "...", "body": "...", "cta_label": "...", "cta_url": "..."}]}.
curl "https://your-store.com/ordo/track/notification?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Dismisses a notification (sets read_at).
| Param | Type | Required |
|---|---|---|
visitor_id |
string | yes (for anonymous visitors) |
notification_id |
int | yes |
Response: {"ok": true} or {"ok": false} — including when notification_id doesn't belong to
the caller (checked against customer_id or visitor_id).
curl -X POST "https://your-store.com/ordo/track/dismissnotification" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "notification_id=123"Polls for a one-shot NPS/satisfaction survey (a campaign's nps_survey action).
Claim-before-deliver, same as the popup endpoint.
Response: {"survey": {"id": 456, "question": "..."} | null}.
curl "https://your-store.com/ordo/track/survey?visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69"Records a survey answer.
| Param | Type | Required | Notes |
|---|---|---|---|
visitor_id |
string | yes (for anonymous visitors) | |
survey_id |
int | yes | the id from the survey endpoint's response |
score |
int | yes | 0-10 |
Response: {"ok": true} or {"ok": false} — rejected if the survey was already answered,
doesn't exist, or doesn't belong to the caller.
curl -X POST "https://your-store.com/ordo/track/submitsurveyresponse" \
-d "visitor_id=f56fc64a-6e7c-416c-bd66-67c1aa10be69" \
-d "survey_id=456" \
-d "score=9"Registers a Web Push subscription (after PushManager.subscribe() in the browser).
| Param | Type | Required |
|---|---|---|
endpoint |
string | yes |
p256dh |
string | yes |
auth |
string | yes |
Response: {"ok": true} or {"ok": false, "reason": "push_disabled" \| "invalid_payload" \| "invalid_endpoint"}.
On CSRF: for a logged-in customer, this endpoint checks the Origin/Referer header
(there's no classic form_key — this is a bare fetch() call) — it must match the store's own
domain. Anonymous registrations (no session) skip this check.
curl -X POST "https://your-store.com/ordo/track/registerpushsubscription" \
-d "endpoint=https://fcm.googleapis.com/fcm/send/xyz123" \
-d "p256dh=BNcRd..." \
-d "auth=tBHIt..."| Param | Type | Required |
|---|---|---|
endpoint |
string | yes |
Response: {"ok": true} or {"ok": false, "reason": "invalid_payload"}.
curl -X POST "https://your-store.com/ordo/track/unregisterpushsubscription" \
-d "endpoint=https://fcm.googleapis.com/fcm/send/xyz123"Registers a "notify me" subscription from a PDP (price drop / back in stock).
| Param | Type | Required | Notes |
|---|---|---|---|
product_id |
int | yes | |
watch_type |
string | yes |
price_drop or back_in_stock
|
Response: {"ok": true} or {"ok": false, "reason": "price_watch_disabled" \| "invalid_payload" \| "invalid_product"}.
Same CSRF model as registerpushsubscription.
curl -X POST "https://your-store.com/ordo/track/registerpricewatch" \
-d "product_id=123" \
-d "watch_type=price_drop"Standard Magento REST — Content-Type: application/json, Authorization: Bearer <token>
header. The full, always-current parameter/type schema is available via Magento's own Swagger
(/rest/V1/schema?services=ordoAutomationAll) — this is just the endpoint map.
-
GET/POST /V1/ordo/campaigns,GET/PUT/DELETE /V1/ordo/campaigns/:entityId -
GET/POST /V1/ordo/campaign-triggers,GET/PUT/DELETE /V1/ordo/campaign-triggers/:entityId -
GET/POST /V1/ordo/campaign-conditions,GET/PUT/DELETE /V1/ordo/campaign-conditions/:entityId -
GET/POST /V1/ordo/campaign-actions,GET/PUT/DELETE /V1/ordo/campaign-actions/:entityId
Triggers/conditions/actions are flat resources filtered by campaign_id via Magento's standard
searchCriteria (?searchCriteria[filterGroups][0][filters][0][field]=campaign_id&...), not a
nested URL.
curl "https://your-store.com/rest/V1/ordo/campaigns" \
-H "Authorization: Bearer $ADMIN_TOKEN"The core segmentation primitive ("send to everyone tagged X") — a headless storefront/CRM can manage tags without an admin panel session.
-
GET /V1/ordo/customers/:customerId/tags— a customer's tags -
GET /V1/ordo/customers/:customerId/tags/:tag— whether a customer has a given tag (bool) -
PUT /V1/ordo/customers/:customerId/tags/:tag— adds a tag (fires thetag_addedtrigger) -
DELETE /V1/ordo/customers/:customerId/tags/:tag— removes a tag -
GET /V1/ordo/tags/:tag/customers— customer IDs holding a given tag
curl -X PUT "https://your-store.com/rest/V1/ordo/customers/42/tags/vip" \
-H "Authorization: Bearer $ADMIN_TOKEN"-
GET/POST /V1/ordo/offers,GET/PUT/DELETE /V1/ordo/offers/:entityId— admin ACL (Ordo_Automation::config) -
POST /V1/ordo/offers/:offerId/self-extend—self, any logged-in customer's token; ownership of the specific offer is checked inside the service (not by ACL) — a customer can extend their own offer by one more period.
curl -X POST "https://your-store.com/rest/V1/ordo/offers/15/self-extend" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"-
GET /V1/ordo/order-approvals,GET /V1/ordo/order-approvals/:entityId— read-only, admin ACL (Ordo_Automation::config). The decision token itself is never returned here. -
POST /V1/ordo/order-approvals/:token/approveand.../:token/reject—anonymous, no OAuth/customer token at all. Possessing:token(the same one from the admin's email link) is the only credential — identical trust model to any "click to approve" email link. -
GET /V1/ordo/order-approvals/:entityId/decision-links— admin ACL; returns the ready-made approve/reject URLs (token baked in) without ever exposing the token as its own field — useful for e.g. a sales-rep mobile app.
curl -X POST "https://your-store.com/rest/V1/ordo/order-approvals/a1b2c3d4e5f6/approve"- CRUD for
free-gift-offers,free-gift-offer-tiers,free-gift-offer-products— admin ACL (Ordo_Automation::free_gifts) -
GET /V1/ordo/carts/:cartId/free-gift-eligibility—self; also works for a guest cart (ownership check is skipped when there's no logged-in customer) -
PUT /V1/ordo/carts/:cartId/free-gifts—self; picks specific SKUs from the eligible pool
curl "https://your-store.com/rest/V1/ordo/carts/98/free-gift-eligibility" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"
curl -X PUT "https://your-store.com/rest/V1/ordo/carts/98/free-gifts" \
-H "Authorization: Bearer $CUSTOMER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"skus": ["FREE-GIFT-SKU-1"]}'-
GET /V1/ordo/credit-limit/mine—self; resolves the customer from the token itself, nocustomerIdneeded (and no way to guess someone else's limit) -
GET /V1/ordo/customers/:customerId/credit-limit— admin ACL (Ordo_Automation::config)
curl "https://your-store.com/rest/V1/ordo/credit-limit/mine" \
-H "Authorization: Bearer $CUSTOMER_TOKEN"-
GET /V1/ordo/reorder-cycles,GET /V1/ordo/reorder-cycles/:entityId— admin ACL (Ordo_Automation::config); computed by a cron job, not writable via the API.
Feeds the webhook_received campaign trigger — any external system (ERP, CRM, PIM) can fire a
campaign by sending arbitrary JSON.
Authentication: the X-Ordo-Signature: sha256=<hex> header — an HMAC-SHA256 over the raw
request body, keyed by a secret set in configuration (Stores → Configuration → Ordo
Automation → Webhooks). Same shape as Meta/WhatsApp's X-Hub-Signature-256.
Signing example (Node.js):
const crypto = require('crypto');
const signature = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');Responses: 200 {"ok": true} / 401 {"ok": false} (bad signature) / 404 {"ok": false}
(webhook disabled in configuration) / 500 {"ok": false} (campaign dispatch error).
The JSON payload lands in the campaign's context as webhook_payload — campaign
conditions/actions can read it via the "Params (JSON)" field.
Example (curl):
BODY='{"order_number":"12345","event":"shipped"}'
SECRET="your-secret-from-configuration"
SIGNATURE="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)"
curl -X POST "https://your-store.com/ordo/webhook/receive" \
-H "Content-Type: application/json" \
-H "X-Ordo-Signature: $SIGNATURE" \
-d "$BODY"// 1. Read/create the visitor_id (if tracker.js isn't loaded, you need to do this yourself and
// set it as the ordo_visitor_id cookie so campaigns see the same visitor consistently).
const visitorId = getOrCreateVisitorIdCookie();
// 2. Log an event
await fetch('/ordo/track/event', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({visitor_id: visitorId, event_type: 'product_view', event_key: sku}),
});
// 3. Poll for a popup (every 25s, first poll after 2s)
setInterval(async () => {
const res = await fetch(`/ordo/track/popup?visitor_id=${visitorId}`);
const {popup} = await res.json();
if (popup) renderPopup(popup);
}, 25000);Ordo — Marketing Automation for Magento