Documentation for configuring, enabling, and verifying the signature of incoming webhook requests.

1) How to get the settings

UI path

Merchant → Merchant name → Settings

Fields
  • Webhook URL — the URL of your handler.
  • Token — used as webhook_secret (HMAC signing secret).
Important

Token is a secret. Do not publish it or store it in plain text. Use it as an HMAC key.

2) How to enable webhook delivery

UI path

Statuses & path → Order status → [status] → Webhook on / off

Delivery condition

A webhook is sent when the order transitions to a status where Webhook = ON.

3) Request format

HTTP
  • Method: POST
  • Content-Type: application/json
  • Body: JSON order object
Headers
  • X-Signature: request signature (HMAC)
Note

The signature is calculated from the raw request body (raw JSON). To verify it, use the original request body (e.g. php://input) rather than a re-encoded json_encode().

4) Request signature (HMAC SHA-256)

4.1 How the platform generates the signature

The JSON string sent in the webhook body is signed.

$payload = json_encode($data, JSON_UNESCAPED_UNICODE);
$sign = hash_hmac('sha256', $payload, $merchant['token']);

The signature is passed in the header:

CURLOPT_HTTPHEADER => [
  'Content-Type: application/json',
  'X-Signature: ' . $sign
],

4.2 Signature verification

Webhook signature verification is performed to ensure:

  • the request was actually sent by our platform
  • the request body was not modified in transit
  • the data was not forged by third parties

Below is the general algorithm (language-agnostic).

General principle

We use the HMAC-SHA256 algorithm.

The signature is generated as follows:

signature = HMAC_SHA256(raw_request_body, webhook_secret)

Where:

  • raw_request_body — the exact HTTP request body in its original (raw) form
  • webhook_secret — the Token from Merchant → Settings

The resulting signature is sent in the X-Signature header.

PHP example

<?php
// Token from Merchant → Settings (used as webhook_secret)
$secret = 'WEBHOOK_TOKEN_FROM_MERCHANT_SETTINGS';

// raw body as received (important!)
$rawBody = file_get_contents('php://input');

// signature from X-Signature header
$received = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

// calculate signature
$calculated = hash_hmac('sha256', $rawBody, $secret);

// compare in constant time
if (!hash_equals($calculated, $received)) {
    http_response_code(401);
    echo 'Invalid signature';
    exit;
}

http_response_code(200);
echo 'OK';
Important

Always verify against the raw body. If you parse JSON and run json_encode() again, key order/escaping may differ and the signature will not match.

Python example

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)

WEBHOOK_SECRET = "WEBHOOK_TOKEN_FROM_MERCHANT_SETTINGS"

@app.route("/webhook", methods=["POST"])
def webhook():
    # Raw body exactly as received
    raw_body = request.get_data()  # bytes

    # Signature from header
    received_signature = request.headers.get("X-Signature", "")

    # Calculate HMAC-SHA256
    calculated_signature = hmac.new(
        WEBHOOK_SECRET.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    # Constant-time comparison
    if not hmac.compare_digest(calculated_signature, received_signature):
        abort(401, "Invalid signature")

    return "OK", 200


if __name__ == "__main__":
    app.run(port=5000)
Important

Always use the raw request body (request.get_data()). Do not re-serialize JSON before calculating the signature, otherwise the hash may not match.

5) Payload fields

Field Type Description
order_idnumberOrder ID
userobjectUser data
user.user_idstringUser ID
user.namestringUser name
user.phonestringPhone
user.emailstring|nullEmail
typenumberOrder type (internal)
companynumberCompany ID
merchantnumberMerchant ID
destination_addressobjectAddress in multi-language format (EN/IL/ES/RU)
destination_coordinatesobjectDelivery coordinates
destination_coordinates.latstringLatitude
destination_coordinates.lonstringLongitude
destination_infostringAdditional info (plain string)
destination_info_jsonarrayAdditional info (structured)
commentstringCustomer comment
infostringAdditional information (service field)
costnumberItems subtotal (base)
discountnumberDiscount
bonusnumberBonuses
promocodestringPromo code
promocode_amountnumberPromo discount amount
tipsnumberTips (amount)
tips_percentnumberTips (percent)
feenumberService fee
vatnumberVAT
deliverynumberDelivery fee
payment_commissionnumberPayment provider commission
totalnumberTotal to pay
paidboolWhether the order is paid
order_statusobjectCurrent order status
order_status.statusstringStatus ID
order_status.valueobjectStatus name (multi-language)
time_createstringCreated at (YYYY-MM-DD HH:MM:SS)
last_timestringLast update time
time_finishstring|nullCompleted at
time_deliverystring|nullDelivered at
couriernull|objectCourier data (if assigned)
cartarrayOrder items
cart[].idnumberProduct ID
cart[].qtynumberQuantity
cart[].pricenumberUnit price
cart[].discountnumberItem discount
cart[].amountnumberItem amount
cart[].totalnumberItem total
cart[].nameobjectName (multi-language)
cart[].descriptionobjectDescription (multi-language)
cart[].addonsbool|arrayAdd-ons (if used)
cart[].balancenumberStock/balance (service field)
currencystringCurrency symbol
is_pickup_pointboolPickup point flag
external_idstring|nullExternal identifier
Note

Some fields are service fields and may be null depending on the merchant configuration and order scenario.

6) Payload example

Example JSON sent in the webhook request body:

{
  "order_id": 2088,
  "user": {
    "user_id": "107",
    "name": "Test",
    "phone": "972000000000",
    "email": null
  },
  "type": 1,
  "company": 28,
  "merchant": 44,
  "destination_address": {
    "EN": "HaShita, Ofir, Eilat, Beersheba Subdistrict, South District, 8804625, Israel",
    "IL": "HaShita, Ofir, Eilat, Beersheba Subdistrict, South District, 8804625, Israel",
    "ES": "HaShita, Ofir, Eilat, Beersheba Subdistrict, South District, 8804625, Israel",
    "RU": "HaShita, Ofir, Eilat, Beersheba Subdistrict, South District, 8804625, Israel"
  },
  "destination_coordinates": {
    "lat": "29.556174355301",
    "lon": "34.949371218681"
  },
  "destination_info": "",
  "destination_info_json": [],
  "comment": "",
  "info": "",
  "cost": 58,
  "discount": 0,
  "bonus": 0,
  "promocode_amount": 0,
  "tips": 3,
  "tips_percent": 5,
  "fee": 0,
  "vat": 0,
  "delivery": 0.47,
  "payment_commission": 0,
  "total": 61.47,
  "promocode": "",
  "paid": false,
  "order_status": {
    "status": "4",
    "value": {
      "EN": "Ready",
      "IL": " מוּכָן",
      "RU": "Ready"
    }
  },
  "time_create": "2026-02-26 14:25:32",
  "last_time": "2026-02-26 17:28:01",
  "time_finish": "2026-02-26 15:56:32",
  "time_delivery": null,
  "courier": null,
  "cart": [
    {
      "addons": false,
      "price": 38,
      "discount": 0,
      "amount": 38,
      "total": 38,
      "id": 399,
      "qty": 1,
      "name": {
        "EN": "Toast Lebowski",
        "IL": "טוסט לבובסקי",
        "ES": "Tostada Lebowski",
        "RU": "Toast Lebowski"
      },
      "description": {
        "EN": "Tomato sauce, pesto, Gouda, purple onion, tomato and Kalamata olives\nServed with a green salad seasoned with vinaigrette dressing",
        "IL": "רוטב עגבניות, פסטו, גבינת גאודה, בצל סגול, עגבנייה וזיתים קלמטה  \nמוגש עם סלט ירוק מתובל ברוטב ויניגרט",
        "ES": "Salsa de tomate, pesto, Gouda, cebolla morada, tomate y aceitunas Kalamata  \nServido con una ensalada verde aderezada con vinagreta",
        "RU": "Tomato sauce, pesto, Gouda, purple onion, tomato and Kalamata olives\nServed with a green salad seasoned with vinaigrette dressing"
      },
      "balance": 1
    },
    {
      "addons": false,
      "price": 20,
      "discount": 0,
      "amount": 20,
      "total": 20,
      "id": 396,
      "qty": 1,
      "name": {
        "EN": "Summer muesli",
        "ES": "Summer muesli",
        "RU": "Summer muesli"
      },
      "description": {
        "EN": "Real Greek yogurt with green smith apple, pear, banana, dates, halva and granola with touches of silan and raw tahini",
        "ES": "Real Greek yogurt with green smith apple, pear, banana, dates, halva and granola with touches of silan and raw tahini",
        "RU": "Real Greek yogurt with green smith apple, pear, banana, dates, halva and granola with touches of silan and raw tahini"
      },
      "balance": 1
    }
  ],
  "max_coock_time": 0,
  "delivery_time": 0,
  "pay_type": {
    "pay_type_id": "2",
    "name": {
      "EN": "Cash",
      "RU": "Cash",
      "IL": "כסף מזומן"
    }
  },
  "cutlery": false,
  "asap": true,
  "pay_link": null,
  "pay_date": null,
  "pay_info": null,
  "platform": null,
  "ver": null,
  "promocode_type": 1,
  "promocode_discount": "0",
  "odd": 100,
  "print": "0",
  "last_order_status": 4,
  "currency": "₪",
  "is_pickup_point": false,
  "external_id": null
}
Handler responses

It is recommended to respond with 200 OK (or any 2xx) after successful processing. For an invalid signature — 401. For a temporary integrator-side error — 5xx.