HYLO Partner API v1

Connect your restaurant system to HYLO

Menus, stock and opening status flow in from your system. Orders flow out to your server the moment a customer pays. Your kitchen accepts and marks food ready from the screen it already uses.

You send HYLOMenu · item stock · open / closed · accept, ready, reject
HYLO sends youEvery new order and each status change, as signed webhooks
DeliveryYour delivery staff use the HYLO Rider app; customers track live in HYLO

Getting started

Build and test on the sandbox first. You receive your live key after the sign-off tests pass.

Sandboxhylo_test_…
https://sandbox-api.hylo.food/v1
Livehylo_live_…
https://api.hylo.food/v1 · key issued after sign-off

Test keys only work on the sandbox, and live keys only work on live. Send your key on every request:

Authorization: Bearer hylo_test_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Keep the key on your server only. Never put it in an app or a web page.
  • If a key may have leaked, tell HYLO. We revoke it within a minute and issue a new one.
  • Limit: 300 requests per minute per key. Above that you get 429; wait and retry.
Every order also rings on the HYLO Partner app. If your server is down, nothing is lost, and your staff can keep working from the app.

Errors

Every error has the same shape. Include the requestId when you report a problem.

{ "error": { "code": "invalid_request", "message": "prepMinutes must be a whole number from 1 to 120.", "requestId": "a1b2c3" } }
HTTPMeaning
400Something in your request is wrong. Fix it before retrying.
401The key is missing, wrong, revoked, or for the other environment.
404No such outlet or order, or it isn't one of yours.
409The order can't take this action now, for example it was already handled on the HYLO Partner app.
422The menu has errors, listed in details. Nothing was changed.
429Too many requests.
5xxA problem on HYLO's side. Retry after a few seconds.

Outlets

HYLO links your outlets to your account. Use HYLO's outlet id in every call.

GET/outlets

{ "outlets": [ { "id": "abc123", "name": "Your Kitchen — Manjeri", "open": true, "selfDelivery": true,
                "menu": { "lastPushAt": "2026-11-01T06:30:00.000Z", "items": 84 } } ] }

GET/outlets/{outletId}

Returns the same object for a single outlet.

POST/outlets/{outletId}/status

Open or close the outlet for orders. until and reason are optional. Without until, the outlet stays closed until you send { "open": true }.

{ "open": false, "until": "2026-11-01T18:00:00+05:30", "reason": "Kitchen maintenance" }

Stock

POST/outlets/{outletId}/stock

{ "type": "item", "ids": ["b1", "d1"], "inStock": false, "until": "2026-11-01T22:00:00+05:30" }
  • type: item, size or addon. Use your size or option ids with the matching type.
  • until is optional and only used with inStock: false. The item comes back on its own at that time.

Response

{ "ok": true, "updatedItems": 2, "notFound": [] }

Webhooks

Give HYLO one https address. HYLO sends a JSON POST to it for every change to your orders.

EventWhen
order.placedA new order to prepare. A scheduled order arrives when it's due.
order.acceptedAccepted, by you or on the HYLO Partner app
order.readyFood marked ready
order.rider_assignedA rider took the order. For your own delivery, this is your delivery staff member, with name and phone.
order.picked_upThe rider picked up the food
order.deliveredDelivered to the customer
order.rejectedRejected before it was accepted
order.cancelledCancelled after it was accepted, by HYLO support
pingA test event sent from HYLO
{
  "id": "83xzotNylGYcY1TkmFSl_placed",
  "type": "order.placed",
  "apiVersion": "v1",
  "createdAt": "2026-11-01T07:21:05.120Z",
  "data": { "order": { /* the order object */ } }
}

Your server must

  1. Check the signature and refuse the event if it doesn't match.
  2. Answer 2xx within 8 seconds. Do slow work after you answer.
  3. Ignore duplicates using the Hylo-Event-Id header (the same as id in the body), because the same event can arrive twice.
  4. Expect events out of order now and then. Each event carries the full current order, and order.updatedAt tells you which copy is newest.

If your server doesn't answer 2xx, HYLO retries after 1, 2, 5, 10 and 30 minutes, then after 1, 2, 4 and 8 hours. You can also fetch orders at any time with GET /orders.

Headers

Hylo-Event-Id: 83xzotNylGYcY1TkmFSl_placed
Hylo-Event-Type: order.placed
Hylo-Signature: t=1761981665,v1=5d0c…

Checking the signature

The signature is an HMAC-SHA256 of <t>.<raw body> using your webhook secret (whsec_…), which HYLO gives you separately from the API key.

const crypto = require('crypto');

// rawBody = the request body exactly as received (a string, before JSON.parse)
function isFromHylo(rawBody, signatureHeader, webhookSecret) {
  const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes = replay
  const expected = crypto.createHmac('sha256', webhookSecret).update(`${t}.${rawBody}`).digest('hex');
  return !!parts.v1 && expected.length === parts.v1.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

The order object

{
  "id": "83xzotNylGYcY1TkmFSl",
  "shortId": "83XZOTNY",
  "outletId": "abc123",
  "status": "placed",
  "placedAt": "2026-11-01T07:21:04.000Z",
  "scheduledFor": null,
  "prepMinutes": null,
  "updatedAt": "2026-11-01T07:21:04.000Z",
  "customer": { "firstName": "Anil", "area": "Manjeri" },
  "items": [
    {
      "line": 1, "itemId": "b1", "name": "Chicken Biryani", "quantity": 2,
      "size": { "id": "b1-full", "name": "Full" },
      "addons": [ { "groupId": "g-extra", "group": "Extras", "id": "raita", "name": "Raita", "price": 20 } ],
      "unitPrice": 280, "unitPriceBeforeOffer": 280, "lineTotal": 560
    }
  ],
  "note": "Less spicy please",
  "cutlery": false,
  "payment": { "method": "cash", "cashToCollect": 688.25 },
  "amounts": { "currency": "INR", "items": 560, "restaurantDiscount": 0, "packing": 0, "delivery": 30, "total": 590, "payout": 512.4 },
  "delivery": { "by": "restaurant", "rider": null },
  "cancelReason": null
}
FieldMeaning
statusscheduled, placed, accepted, ready, picked_up, delivered, rejected or cancelled
items[].unitPriceYour own price for one unit, including size and add-ons, after your own item offer. Ids are your ids from the menu push.
amounts.totalThe order's value at your prices, before GST: items − your share of any coupon + packing + your delivery fee. Discounts paid by HYLO never reduce it.
amounts.payoutWhat HYLO pays you for this order, after commission.
payment.cashToCollectCash your delivery staff collect from the customer, only on cash orders with your own delivery. Otherwise 0.
customerFirst name and area only. Your delivery staff see the address and phone number in the HYLO Rider app.

Order actions

GET/orders?outletId=…&since=…&limit=…

Orders placed since since, oldest first. since is an ISO time at most 7 days ago, and defaults to the last 24 hours. limit is at most 100; hasMore: true means call again with a later since. Use this to catch up after downtime.

GET/orders/{orderId}

Returns one order.

POST/orders/{orderId}/accept

{ "prepMinutes": 20 }

prepMinutes is optional (1–120). Calling accept twice is safe, and the second call answers "alreadyDone": true.

POST/orders/{orderId}/ready

Marks the food ready. If the order wasn't accepted yet, it's accepted first.

POST/orders/{orderId}/reject

{ "reason": "out_of_stock", "note": "", "outOfStockItemIds": ["d1"] }
  • reason: out_of_stock, too_busy, closing or other. other needs a note.
  • Only a new order (placed) can be rejected. To cancel an accepted order, call HYLO support.
  • Items listed in outOfStockItemIds are switched off until midnight.
  • The customer is refunded automatically.
Handled elsewhere? If someone already handled the order on the HYLO Partner app, you get 409 already_handled, and the message gives the order's current status.

Going live

On the sandbox, with your test outlet, show each of these working end to end. HYLO then issues your live key and switches on one outlet first.

  1. A menu push with sizes and add-ons, then the same push again, with nothing changed.
  2. An item switched out of stock and back, and the outlet closed and reopened.
  3. An order received by webhook with its signature checked, accepted with a prep time, then marked ready.
  4. An order rejected as out of stock.
  5. Your server down for a few minutes, then catching up from the retries and from GET /orders.
  6. Your delivery staff delivering a test order with the HYLO Rider app.

Data & security

  • Use customer data from HYLO only to prepare and deliver that order. Don't use it for marketing, and don't share it.
  • Store keys and webhook secrets only on your server, and tell HYLO at once if one may have leaked.
  • Use of the API is covered by the HYLO Partner API Agreement, signed before your live key is issued.