Auth
OAuth client_credentials for partners; per-tenant API keys for direct integrations.
Two flows. (1) OAuth β apply for a Marketing Developer Platform partner account; we issue you a client_id + client_secret and an access token endpoint at /oauth/token. (2) API keys β operators generate a per-tenant key in /app/api-keys. Both auth styles use the Authorization: Bearer header. All requests are TLS-only; HTTP requests are 308-redirected.
REST
Stable v1 endpoints. JSON in, JSON out. Errors follow the {error: {code, message}} shape.
POST/api/v1/orders+
Create an order. Use OAuth or tenant API key.
{ "brand_id": "...", "location_id": "...", "items": [{"item_id": "...", "qty": 2}], "fulfillment": "pickup", "guest": {"name": "Maya R.", "phone": "+13055550101"} }{ "id": "ord_...", "order_number": "Z-1024", "status": "received", "total_cents": 4180 }GET/api/v1/orders/{id}+
Fetch a single order with line items.
PATCH/api/v1/orders/{id}/status+
Advance an order's status. Server enforces the transition graph.
{ "to": "preparing" }POST/api/v1/menu/items+
Create a menu item under a brand.
{ "brand_id": "...", "name": "Chicken Shawarma", "price_cents": 1499, "category": "Plates" }PATCH/api/v1/menu/items/{id}/availability+
86 / un-86 an item. Optional location_id for per-location override.
{ "available": false, "location_id": "..." }GET/api/v1/customers+
List customers. Pagination via cursor.
{ "data": [...], "next_cursor": "..." }POST/api/v1/refunds+
Issue a refund against an order. Must include amount_cents and reason.
{ "order_id": "ord_...", "amount_cents": 500, "reason": "Customer request" }Webhooks
HMAC-SHA256-signed event push to a URL you configure. At-least-once delivery with exponential backoff.
Configure a delivery URL in /app/integrations. We POST every relevant event with header X-Zayos-Signature: t={timestamp},v1={hex_hmac}. Verify by computing HMAC-SHA256 of `{timestamp}.{raw_body}` using your shared secret. We retry up to 8 times with backoff: 30s, 1m, 5m, 30m, 2h, 6h, 12h, 24h. Replay protection: ignore deliveries with timestamps older than 5 minutes.
| Event | When it fires |
|---|---|
| order.received | New order entered the kitchen feed. |
| order.accepted | Operator accepted the order. |
| order.preparing | Cooking started. |
| order.ready | Order is ready for handoff. |
| order.out_for_delivery | Driver picked it up. |
| order.delivered | Customer received the order. |
| order.cancelled | Order was cancelled (reason in payload). |
| order.refunded | Full or partial refund issued. |
| menu.item.86 | Item went out of stock at one or more locations. |
| customer.created | First-time customer signed up. |
Rate limits
Token-bucket per tenant_id Γ IP. Fair limits for hot paths, generous for back-office.
REST: 600 req/min/tenant for read endpoints, 120 req/min for writes. /api/v1/orders POST: 30 req/min/IP unauthenticated, 600 with auth. Auth/OTP/password-reset: 5 req/min/IP. Promo validation: 60 req/min/IP. 429 responses include Retry-After header.
Errors
Consistent shape. Never leak internals β error.code is stable; error.message is human-readable.
All errors return {error: {code: "snake_case_id", message: "Human readable explanation", request_id: "req_..."}}. Common codes: invalid_request, not_found, forbidden, conflict, rate_limited, server_error. Always log request_id on the client side for support tickets.
Hello world (curl)
curl https://api.zayrev.com/api/v1/orders \
-H "Authorization: Bearer ${ZAYOS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"brand_id": "brand_...",
"location_id": "loc_...",
"items": [{"item_id": "itm_...", "qty": 2}],
"fulfillment": "pickup",
"guest": {"name": "Maya R.", "phone": "+13055550101"}
}'