ZR Express Delivery Integration
ZR Express is an Algerian cash-on-delivery delivery platform
(https://api.zrexpress.app, dashboard zrexpress.app). Its REST API covers the
full supplier flow: parcels/orders (create, bulk, exchange, refund, labels,
state updates), catalog (products, stock, receipts), customers
(individual + company, addresses, imports), claims, delivery pricing
(rates per territory), treasury (payments, payment requests, balance),
users (profile, API keys), hubs, and webhooks (programmatic
registration).
Start at references/ in this folder — do not guess from the raw swagger.
Read CONFORMANCE.md before assuming the current adapter behavior is correct —
it lists live-verified bugs (tracking 404, delete 405, address update 400, stop-desk
hub 404, geo naming) that the code comments below may not reflect.
Reference files (in this skill folder)
| File |
What it holds |
endpoints-index.md |
Master index — all 129 endpoints, per-domain, with auth notes |
CONFORMANCE.md |
Verified audit (2026-09-10, live-tested): what works / what's broken / fix order — read before assuming current behavior is correct |
references/catalog.md |
Products, stock movements, receipts, categories, catalog reports (17) |
references/claims.md |
Claim categories, claims, comments, workflows (11) |
references/customers.md |
Individual/company customers, addresses, imports/exports (15) |
references/orders.md |
Parcels, pickup bags, modification requests, territories, labels, reports (50) |
references/delivery-pricing.md |
Rates, service pricing, price lists (5) |
references/hubs.md |
Hubs (2) |
references/supplier.md |
Supplier profile, blocking, price-list assignments (3) |
references/treasury.md |
Supplier payments, payment requests, treasury reports (15) |
references/users.md |
Profile, API-key management (4) |
references/webhooks.md |
Webhook endpoint CRUD (7) |
schemas/*.json |
Full request/response schemas per domain (verbatim from swagger) |
Path prefix is /api/v1/… (version defaults to 1). Read the domain
reference + its schemas/{domain}.json before writing adapter code.
Codebase
cod-server/src/endpoints/delivery-companies/providers/zr_express/ — the adapter
(adapter.ts, types.ts, capabilities.ts) implementing the
DeliveryProvider interface used by dispatch.ts.
cod-server/src/endpoints/delivery-companies/providers/registry.ts — routes
company code zr_express to the adapter; apiToken = ZR API key,
apiUserGuid = ZR tenant Id.
cod-server/src/endpoints/delivery-companies/webhook-handlers.ts — programmatic
webhook registration (registerZrWebhook) against /api/v1/webhooks/endpoints.
cod-server/src/endpoints/webhooks/zr-status-mapper.ts — maps ZR state names
to CodFlow statuses.
Auth
- Headers:
X-Api-Key: {secretKey} + X-Tenant: {tenantId}
(the adapter uses these consistently; the swagger also lists Bearer).
- Every request also carries
Accept/Content-Type application/json.
Gotchas (verified against the live integration)
- No separate validation step — ZR auto-activates parcels on creation;
validateShipment is a no-op.
- Single create returns a parcel UUID; the tracking number only comes back from
GET /parcels/{id}. Bulk create (POST /parcels/bulk) returns the
tracking number directly. maxBulkCreate = 100.
- Territories are UUIDs (city + district), not
wilaya_id/commune strings —
resolve them via POST /territories/search and cache the city per wilaya.
- Auth is
X-Api-Key + X-Tenant — do not switch to Authorization: Bearer
just because examples show it; the stored apiToken is an API key.
- State names are free text (
data.state.name), not a fixed enum: only
"Out for Delivery", "In Transit" and "At Hub" are documented defaults.
Unknown names must surface as unmapped — never guessed into a status.
- Label URLs expire (~1 hour, SAS-token based) — from create you get a
deferred label token (CodFlow uses
DEFERRED_LABEL_MARKER); serve labels
server-side, never expose a signed URL to the browser.
- Update semantics —
PATCH /parcels/{id}/amount, /customer and
/deliveryAddress apply while canUpdateAfterValidation: true.
- Delete is unreliable (HTTP 405 in tests) —
canDeleteBeforeValidation/
AfterValidation are both false; prefer state updates/refund flows.
- Webhook registration is API-driven —
POST /webhooks/endpoints with
X-Api-Key + X-Tenant; the registered URL is CodFlow's /webhooks/zr_express.
- All IDs (parcel, customer, address, claim, etc.) are UUIDs — any other
format is an error or a legacy/alias field.
Workflow A — Connect ZR Express
- Create the delivery company with
code: "zr_express", apiToken = ZR API key,
apiUserGuid = ZR tenant Id.
- Verify: token check → create a test parcel (
POST /parcels,
POST /customers/individual first) → GET /parcels/{id} returns the tracking
number → POST /parcels/labels/individual → tracking pull
(GET /parcels/{id}/state-history).
- Register the webhook (
registerZrWebhook → POST /webhooks/endpoints) and
configure webhook_status_mapping for any non-default state names.
Workflow B — Audit / fix the integration
- Read the relevant
references/{domain}.md + schemas/{domain}.json.
- Diff
adapter.ts / types.ts endpoint-by-endpoint against the reference.
- Keep every header name, path, query param, and body field exactly as the
reference spells them (the generated files are verbatim from swagger).
Security
Never commit ZR API keys or tenant IDs. They live in wrangler secrets /
.dev.vars only (repo convention).
1---2name: zr-express3description: ZR Express delivery platform integration for CodFlow — all 129 public API endpoints (catalog, claims, customers, orders/parcels, delivery pricing, hubs, supplier, treasury, users, webhooks), organized per domain. Use when connecting ZR Express as a delivery company, auditing or fixing the zr_express adapter in cod-server, verifying behavior against the official API, pulling tracking/state, registering webhooks, or extending ZR Express support.4---56# ZR Express Delivery Integration78ZR Express is an Algerian cash-on-delivery delivery platform9(`https://api.zrexpress.app`, dashboard `zrexpress.app`). Its REST API covers the10full supplier flow: **parcels/orders** (create, bulk, exchange, refund, labels,11state updates), **catalog** (products, stock, receipts), **customers**12(individual + company, addresses, imports), **claims**, **delivery pricing**13(rates per territory), **treasury** (payments, payment requests, balance),14**users** (profile, API keys), **hubs**, and **webhooks** (programmatic15registration).1617Start at `references/` in this folder — do not guess from the raw swagger.18**Read `CONFORMANCE.md` before assuming the current adapter behavior is correct** —19it lists live-verified bugs (tracking 404, delete 405, address update 400, stop-desk20hub 404, geo naming) that the code comments below may not reflect.2122## Reference files (in this skill folder)2324| File | What it holds |25|---|---|26| `endpoints-index.md` | Master index — all 129 endpoints, per-domain, with auth notes |27| `CONFORMANCE.md` | **Verified audit (2026-09-10, live-tested): what works / what's broken / fix order** — read before assuming current behavior is correct |28| `references/catalog.md` | Products, stock movements, receipts, categories, catalog reports (17) |29| `references/claims.md` | Claim categories, claims, comments, workflows (11) |30| `references/customers.md` | Individual/company customers, addresses, imports/exports (15) |31| `references/orders.md` | **Parcels, pickup bags, modification requests, territories, labels, reports (50)** |32| `references/delivery-pricing.md` | Rates, service pricing, price lists (5) |33| `references/hubs.md` | Hubs (2) |34| `references/supplier.md` | Supplier profile, blocking, price-list assignments (3) |35| `references/treasury.md` | Supplier payments, payment requests, treasury reports (15) |36| `references/users.md` | Profile, API-key management (4) |37| `references/webhooks.md` | Webhook endpoint CRUD (7) |38| `schemas/*.json` | Full request/response schemas per domain (verbatim from swagger) |3940Path prefix is `/api/v1/…` (`version` defaults to `1`). Read the domain41reference + its `schemas/{domain}.json` before writing adapter code.4243## Codebase4445- `cod-server/src/endpoints/delivery-companies/providers/zr_express/` — the adapter46 (`adapter.ts`, `types.ts`, `capabilities.ts`) implementing the47 `DeliveryProvider` interface used by `dispatch.ts`.48- `cod-server/src/endpoints/delivery-companies/providers/registry.ts` — routes49 company code `zr_express` to the adapter; `apiToken` = ZR API key,50 `apiUserGuid` = ZR **tenant Id**.51- `cod-server/src/endpoints/delivery-companies/webhook-handlers.ts` — programmatic52 webhook registration (`registerZrWebhook`) against `/api/v1/webhooks/endpoints`.53- `cod-server/src/endpoints/webhooks/zr-status-mapper.ts` — maps ZR state names54 to CodFlow statuses.5556## Auth5758- Headers: `X-Api-Key: {secretKey}` **+** `X-Tenant: {tenantId}`59 (the adapter uses these consistently; the swagger also lists `Bearer`).60- Every request also carries `Accept`/`Content-Type` `application/json`.6162## Gotchas (verified against the live integration)63641. **No separate validation step** — ZR auto-activates parcels on creation;65 `validateShipment` is a no-op.662. **Single create returns a parcel UUID**; the tracking number only comes back from67 `GET /parcels/{id}`. **Bulk create** (`POST /parcels/bulk`) returns the68 tracking number directly. `maxBulkCreate` = 100.693. **Territories are UUIDs** (city + district), not `wilaya_id`/commune strings —70 resolve them via `POST /territories/search` and cache the city per wilaya.714. **Auth is `X-Api-Key` + `X-Tenant`** — do not switch to `Authorization: Bearer`72 just because examples show it; the stored `apiToken` is an API key.735. **State names are free text** (`data.state.name`), not a fixed enum: only74 `"Out for Delivery"`, `"In Transit"` and `"At Hub"` are documented defaults.75 Unknown names must surface as `unmapped` — never guessed into a status.766. **Label URLs expire (~1 hour, SAS-token based)** — from create you get a77 deferred label token (CodFlow uses `DEFERRED_LABEL_MARKER`); serve labels78 server-side, never expose a signed URL to the browser.797. **Update semantics** — `PATCH /parcels/{id}/amount`, `/customer` and80 `/deliveryAddress` apply while `canUpdateAfterValidation: true`.818. **Delete is unreliable** (HTTP 405 in tests) — `canDeleteBeforeValidation`/82 `AfterValidation` are both false; prefer state updates/refund flows.839. **Webhook registration is API-driven** — `POST /webhooks/endpoints` with84 `X-Api-Key` + `X-Tenant`; the registered URL is CodFlow's `/webhooks/zr_express`.8510. All IDs (parcel, customer, address, claim, etc.) are **UUIDs** — any other86 format is an error or a legacy/alias field.8788## Workflow A — Connect ZR Express89901. Create the delivery company with `code: "zr_express"`, `apiToken` = ZR API key,91 `apiUserGuid` = ZR **tenant Id**.922. Verify: token check → create a test parcel (`POST /parcels`,93 `POST /customers/individual` first) → `GET /parcels/{id}` returns the tracking94 number → `POST /parcels/labels/individual` → tracking pull95 (`GET /parcels/{id}/state-history`).963. Register the webhook (`registerZrWebhook` → `POST /webhooks/endpoints`) and97 configure `webhook_status_mapping` for any non-default state names.9899## Workflow B — Audit / fix the integration1001011. Read the relevant `references/{domain}.md` + `schemas/{domain}.json`.1022. Diff `adapter.ts` / `types.ts` endpoint-by-endpoint against the reference.1033. Keep every header name, path, query param, and body field **exactly** as the104 reference spells them (the generated files are verbatim from swagger).105106## Security107108Never commit ZR API keys or tenant IDs. They live in `wrangler secrets` /109`.dev.vars` only (repo convention).