Developers

API and webhooks

Connect your ERP, warehouse or automation tools to your store: read orders, update stock, and receive events as they happen.

Create an API key https://yaliko.com/api/v1

Authentication

Create a key in Settings → Developers (owner only). Send it in the Authorization: Bearer header. A "Read" key reads; a "Read and write" key can also change orders and products. Keep it on your server.

Authorization: Bearer yaliko_live_…

Limits

120 requests per minute per key. Past that the API answers 429: wait a moment and retry.

Pagination

Lists return { data, next_cursor }, newest first. Pass limit (1 to 100, default 50) and cursor=next_cursor for the next page. next_cursor is null on the last page.

Errors

An error returns { error: { code, message } } with the HTTP status: 400 invalid body, 401 missing or revoked key, 403 read-only key, 404 not found in your store, 422 invalid field, 429 too many requests.

Amounts are whole numbers in the store's currency (currency). Dates are ISO 8601, UTC.

Endpoints

GET /storeThe key's store: name, currency, language. Handy to test a key.read
GET /ordersOrders, newest first. Filters: status, created_after, created_before.read
GET /orders/{id}One order with its items, customer and shipping.read
PATCH /orders/{id}Change status (PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELED), carrier, tracking_number, tracking_url. Same effects as in the admin: restock, customer update, loyalty points.write
GET /productsProducts with their variants. Filter: q (in the name).read
GET /products/{id}One product.read
PATCH /products/{id}Update price, compare_at_price, stock and variants: [{ id, price, stock }]. Customers waiting are told when it's back in stock.write
GET /customersCustomers. Filters: email, phone.read
GET /customers/{id}One customer.read

Example: list the orders to prepare

curl https://yaliko.com/api/v1/orders?status=PENDING&limit=2 \
  -H "Authorization: Bearer yaliko_live_…"
{
  "data": [
    {
      "id": "cmuo2k1x40001…",
      "reference": "40001ABC",
      "status": "PENDING",
      "payment": { "mode": "COD", "status": "PENDING", "refunded": 0 },
      "total": 730,
      "currency": "MAD",
      "customer": { "id": "cmung…", "name": "Sara Alaoui", "phone": "212612345678", "email": null },
      "shipping": { "address": "12 rue …", "city": "Casablanca", "carrier": null, "tracking_number": null },
      "items": [{ "product_id": "cmunc…", "name": "Théière", "quantity": 1, "unit_price": 590 }],
      "created_at": "2026-09-30T10:12:00.000Z"
    }
  ],
  "next_cursor": "cmuo2k1x40001…"
}

Example: mark an order shipped

curl -X PATCH https://yaliko.com/api/v1/orders/cmuo2k1x40001… \
  -H "Authorization: Bearer yaliko_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "status": "SHIPPED", "carrier": "Amana", "tracking_number": "RR123456789MA" }'

Webhooks

Add an https URL in Settings → Developers and pick the events. We send a signed JSON POST for each event. Answer 2xx within 10 seconds and do the heavy work afterwards.

Events

  • order.createdAn order is placed (storefront, confirmed card payment, or created in the admin).
  • order.updatedAn order's status changes.
  • customer.createdA customer creates an account.
POST /webhooks/yaliko
Yaliko-Event: order.updated
Yaliko-Delivery: cmup…
Yaliko-Signature: t=1790745600,v1=5f2b…

{
  "id": "evt_…",
  "type": "order.updated",
  "created_at": "2026-09-30T10:20:00.000Z",
  "data": { "id": "cmuo2k1x40001…", "status": "SHIPPED", … }
}

Verify the signature

The Yaliko-Signature header holds t (a timestamp) and v1 (HMAC-SHA256 of "t.body" with the webhook's secret). Compute it on the raw body and reject timestamps older than 5 minutes.

import crypto from "node:crypto";

// Express: app.post("/webhooks/yaliko", express.raw({ type: "application/json" }), handler)
function isFromYaliko(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "");
  return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}

Retries

If your URL doesn't answer 2xx, we retry after 1 min, 5 min, 30 min, 2 h and 12 h. The delivery history and a Resend button are in the settings. The same event can arrive twice: deduplicate on the event id.