> ## Documentation Index
> Fetch the complete documentation index at: https://platform.take.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive store events at your own endpoint, signed and verifiable.

Webhooks let your systems react to events in a Take App store in real time — no
polling required. When an order or product changes, Take App sends an HTTP
`POST` request to the endpoint(s) you configure.

The event payload reuses the **same object** returned by the matching Merchant
API V2 endpoint — [Get order](/api-reference/v2/endpoint/get-order) for order
events and [Get product](/api-reference/v2/endpoint/get-product) for product
events — so you only need to model each shape once.

## Configuring endpoints

Add and manage endpoints in your admin dashboard under **Settings → Apps →
Webhooks**. For each endpoint you choose:

* **Endpoint URL** — an `https://` URL that accepts `POST` requests.
* **Events** — which events this endpoint subscribes to.

Each endpoint has a **signing secret** used to verify incoming requests. Copy it
anytime from the endpoint's **⋯ → Copy signing secret** menu. You can rotate the
secret at any time; the old secret stops working immediately.

## Events

| Event             | When it fires                                                            |
| ----------------- | ------------------------------------------------------------------------ |
| `order.created`   | A new order is placed, or a draft order is activated for the first time. |
| `order.updated`   | An existing order changes — status, payment, fulfillment, or line items. |
| `product.updated` | An existing product changes — name, price, variants, or options.         |

## Payload

Every delivery has the same envelope. `data` is the V2 object for the event —
an Order for `order.*` events, a Product for `product.updated`.

```json theme={null}
{
  "type": "order.updated",
  "created_at": "2026-06-05T08:30:00.000Z",
  "data": {
    "id": "ord_123",
    "object": "order",
    "number": "1042",
    "name": "#1042",
    "store": {
      "id": "sto_123",
      "name": "Downtown Cafe",
      "alias": "downtown-cafe"
    },
    "order_status": "confirmed",
    "payment_status": "paid",
    "fulfillment_status": "unfulfilled",
    "customer": {
      "id": "cus_123",
      "object": "customer",
      "name": "Jane Doe",
      "phone": "6591234567"
    },
    "line_items": [
      {
        "id": "oli_1",
        "object": "order_line_item",
        "product_id": "prod_1",
        "name": "Nasi Lemak",
        "quantity": 2,
        "price": 650,
        "total_amount": 1300
      }
    ],
    "items_amount": 1300,
    "total_amount": 1300,
    "currency": "SGD",
    "created_at": "2026-06-05T08:00:00.000Z",
    "updated_at": "2026-06-05T08:30:00.000Z"
  }
}
```

See [Get order](/api-reference/v2/endpoint/get-order) for the full Order schema.

### Headers

| Header               | Description                                          |
| -------------------- | ---------------------------------------------------- |
| `X-Take-Event`       | The event type, e.g. `order.created`.                |
| `X-Take-Delivery-Id` | Unique ID for this delivery attempt. Use for dedupe. |
| `X-Take-Signature`   | Signature used to verify authenticity (see below).   |

## Verifying signatures

Each request is signed with your endpoint's signing secret using HMAC-SHA256.
The `X-Take-Signature` header looks like:

```
t=1717574400,v1=5257a869e7ec...
```

* `t` — the Unix timestamp (seconds) when the request was signed.
* `v1` — the HMAC-SHA256 of `{t}.{raw_request_body}`, hex-encoded.

To verify, recompute `v1` with your secret over `{t}.{raw_body}` and compare
using a constant-time comparison. Reject requests whose timestamp is too old
(e.g. older than 5 minutes) to defend against replays.

<CodeGroup>
  ```javascript Node.js theme={null}
  import crypto from "crypto";

  function verifyTakeSignature(rawBody, header, secret) {
    const parts = Object.fromEntries(
      header.split(",").map((kv) => kv.split("="))
    );
    const timestamp = parts.t;
    const received = parts.v1;

    // Reject old timestamps (replay protection)
    const age = Math.floor(Date.now() / 1000) - Number(timestamp);
    if (Number.isNaN(age) || age > 300) return false;

    const expected = crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex");

    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(received)
    );
  }
  ```

  ```python Python theme={null}
  import hashlib
  import hmac
  import time

  def verify_take_signature(raw_body: bytes, header: str, secret: str) -> bool:
      parts = dict(kv.split("=", 1) for kv in header.split(","))
      timestamp = parts.get("t", "")
      received = parts.get("v1", "")

      # Reject old timestamps (replay protection)
      try:
          if time.time() - int(timestamp) > 300:
              return False
      except ValueError:
          return False

      signed_payload = f"{timestamp}.".encode() + raw_body
      expected = hmac.new(
          secret.encode(), signed_payload, hashlib.sha256
      ).hexdigest()

      return hmac.compare_digest(expected, received)
  ```
</CodeGroup>

<Warning>
  Verify the signature against the **raw request body** bytes, before any JSON
  parsing or re-serialization. Reformatting the body changes the signature.
</Warning>

## Responding

Return a `2xx` status as soon as you've received the event. Do any heavy
processing asynchronously so you can respond quickly.

## Retries

If your endpoint returns a non-`2xx` status or times out, delivery is retried
with exponential backoff. A `4xx` response (other than `408` and `429`) is
treated as a permanent rejection and is **not** retried.

Because retries and at-least-once delivery are possible, treat webhooks as
**idempotent**: use `X-Take-Delivery-Id` to ignore duplicates.

## Ordering

Events are not guaranteed to arrive in order. Use the object's `updated_at`
field to discard stale updates if you persist order or product state.

<Note>
  Configure webhook endpoints in **Settings → Apps → Webhooks**.
</Note>
