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

# Webhooks overview

> How klikit pushes order events to a URL you host.

A webhook is an HTTP `POST` from klikit to a URL you host. Whenever something
happens on klikit that you care about — a new order, a status change, a cart
edit — klikit sends you the details. You don't poll. You just answer the
phone when it rings.

## The mental model

<Steps>
  <Step title="You host an endpoint">
    e.g. `https://api.yourcompany.com/integrations/klikit/webhooks`. It needs
    to be HTTPS, publicly reachable from klikit's egress IPs, and capable of
    handling a few requests per second per branch you operate.
  </Step>

  <Step title="Your klikit operator registers the URL">
    They map your URL to a `(brand, branch, event)` tuple. One URL per tuple
    today — there's no self-service registration. Send your URL +
    environment to [integrations@klikit.io](mailto:integrations@klikit.io).
  </Step>

  <Step title="An event happens on klikit">
    A customer places an order. An operator marks one as `READY`. A cart is
    edited mid-checkout. Klikit's outbound webhook delivery service picks
    up the event.
  </Step>

  <Step title="Klikit POSTs to your URL">
    JSON body, signed with your webhook secret in the `x-klikit-signature`
    header. See [Verify signatures](/partner-api/webhooks/verify) for the
    exact recipe.
  </Step>

  <Step title="You reply 2xx within 10 seconds">
    Any other reply (or a timeout) is treated as a failed delivery and
    retried with exponential backoff. Failed deliveries are persisted —
    your operator can replay them after you recover from downtime.
  </Step>
</Steps>

## What you'll receive

All three event types share the same JSON envelope:

```json theme={null}
{
  "brand_id":  123,
  "branch_id": 456,
  "orders":    [ /* one or more orders, same shape as GET /v1/partner/orders */ ]
}
```

Every delivery carries these headers:

| Header                | Purpose                                                                              |
| --------------------- | ------------------------------------------------------------------------------------ |
| `x-klikit-signature`  | `hex(HMAC-SHA256(your webhook_secret, raw request body))`                            |
| `x-klikit-event-id`   | Unique per delivery attempt. Use for [idempotency](#five-things-you-must-get-right). |
| `x-klikit-event-type` | One of the three event names. See [Event catalogue](/partner-api/webhooks/events).   |
| `Content-Type`        | Always `application/json`.                                                           |

## The three event types

<CardGroup cols={3}>
  <Card title="klikit.order.created.v2" icon="bell" href="/partner-api/webhooks/events#order-created">
    A new order just landed. Ring the kitchen.
  </Card>

  <Card title="klikit.order.status.updated" icon="arrow-right" href="/partner-api/webhooks/events#status-updated">
    An order moved to a new state.
  </Card>

  <Card title="klikit.order.cart.updated" icon="cart-shopping" href="/partner-api/webhooks/events#cart-updated">
    A cart was edited before checkout.
  </Card>
</CardGroup>

## Five things you must get right

<AccordionGroup>
  <Accordion title="1. Read the raw body before parsing it">
    The HMAC is computed over the exact bytes klikit sent. If you parse the
    JSON and re-serialise before verifying, key reordering or whitespace
    differences will break the signature. Most frameworks have a "capture
    the raw body" hook — see the [reference receivers](/partner-api/examples/node-receiver)
    for how.
  </Accordion>

  <Accordion title="2. Compare signatures in constant time">
    Use `crypto.timingSafeEqual` (Node), `hmac.compare_digest` (Python), or
    your language's equivalent. A naïve `==` is vulnerable to timing
    side-channels.
  </Accordion>

  <Accordion title="3. Be idempotent on x-klikit-event-id">
    Klikit retries on any non-2xx, so the same `x-klikit-event-id` *will*
    arrive twice in some failure modes. Store the IDs you've already
    processed (Redis `SETNX`, Postgres unique index, etc.) and silently
    return 2xx for repeats.
  </Accordion>

  <Accordion title="4. Acknowledge fast, do work async">
    Klikit's delivery timeout is **10 seconds**. If your endpoint takes 11
    seconds to push to a slow POS and then returns 200, klikit has already
    counted it as a timeout and queued a retry — you'll process every order
    twice. Reply 200 immediately, then queue the work.
  </Accordion>

  <Accordion title="5. Return JSON or empty">
    Klikit ignores the body of your response, but if you do return a body,
    make it valid JSON to avoid logging noise on both sides.
  </Accordion>
</AccordionGroup>

## Where to go from here

<CardGroup cols={2}>
  <Card title="Verify signatures" icon="signature" href="/partner-api/webhooks/verify">
    Copy-pasteable HMAC verification in Node, Python, Go, PHP, and shell.
  </Card>

  <Card title="Event catalogue" icon="list" href="/partner-api/webhooks/events">
    What each event means, when it fires, what the payload looks like.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/partner-api/webhooks/troubleshooting">
    Symptom → cause table for the common integration failures.
  </Card>

  <Card title="Reference Node receiver" icon="node-js" href="/partner-api/examples/node-receiver">
    \~80 lines, runs as-is, demonstrates all five musts.
  </Card>
</CardGroup>
