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

# Order webhooks

> Receive order events from Fire spark and send tickets to your POS or RMS when order.injected fires.

When a customer checks out on any connected channel, Fire spark notifies your integration with a webhook. You do not poll for new orders — Fire spark pushes events to the URL you configure for the merchant.

## How orders reach your kitchen

This is the same flow whether the order came from your app, Uber Eats, or a kiosk:

<Steps>
  <Step title="Customer places an order">
    The order is created in Fire spark with line items, totals, store, channel,
    fulfillment type, and payment state.
  </Step>

  <Step title="Fire spark notifies your integration">
    Your server receives a webhook with the event type and order identifier.
  </Step>

  <Step title="order.injected sends the ticket to your POS">
    When Fire spark fires **`order.injected`**, your integration receives the
    full order payload. **This is the moment to create the ticket in your POS or
    RMS** — using the payment, fulfillment, and line-item state exactly as
    included in the event, without waiting for a separate API call.
  </Step>

  <Step title="Your POS updates Fire spark">
    As the order progresses in your operational system, call
    [Update order status](/docs/integrations-api/orders/\[id]/status/patch) so every
    channel stays aligned.
  </Step>
</Steps>

<Info>
  **For marketing and operations teams:** `order.injected` means “send this order
  to the kitchen system now.” Fire spark does not rewrite the order at injection
  time — the POS receives the order **in whatever state it is in** when the
  event fires (for example paid and ready to prepare, or still pending payment if
  your channel allows that).
</Info>

## Configure your endpoint

Register an HTTPS webhook URL in the merchant integration settings in the Fire spark dashboard. Fire spark sends `POST` requests to that URL for each subscribed event.

| Requirement | Detail                                                                 |
| ----------- | ---------------------------------------------------------------------- |
| Protocol    | HTTPS only                                                             |
| Response    | Return `2xx` within 30 seconds to acknowledge delivery                 |
| Retries     | Non-`2xx` responses or timeouts trigger automatic retries with backoff |
| Idempotency | Use the event `id` to ignore duplicate deliveries of the same event    |

<Note>
  Requires an integration configured for the merchant. See
  [Introduction](/docs/integrations-api/introduction) for the full integration
  setup flow.
</Note>

## Event types

| Event             | When Fire spark sends it                                   | What your integration should do                                                                   |
| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `order.created`   | A new order exists in Fire spark after checkout            | Store the order id, optionally fetch details with [Get order](/docs/integrations-api/orders/\[id]/get) |
| `order.updated`   | Payment, fulfillment, or order status changed              | Sync the change to your POS or internal queue if needed                                           |
| `order.injected`  | Fire spark is ready for the order to enter your POS or RMS | **Create the operational ticket** using the payload as-is                                         |
| `order.cancelled` | The order was cancelled on a channel or in Fire spark      | Void or cancel the matching ticket in your POS                                                    |

<Tip>
  Treat **`order.injected`** as the operational handoff. Earlier events such as
  `order.created` are useful for logging and analytics; the kitchen ticket should
  be created when `order.injected` arrives unless your integration design
  explicitly handles a different event.
</Tip>

## Webhook payload

Each delivery is a JSON object with a consistent envelope:

| Field        | Required | Type   | Description                                                                               |
| ------------ | -------- | ------ | ----------------------------------------------------------------------------------------- |
| `id`         | Yes      | string | Unique delivery id. Use for idempotency.                                                  |
| `type`       | Yes      | string | Event name (`order.created`, `order.injected`, …)                                         |
| `created_at` | Yes      | string | ISO 8601 timestamp when the event was created                                             |
| `data`       | Yes      | object | Event body. Always includes `order_id`; full order fields are present on `order.injected` |

### `data` object

| Field      | Required | Type   | Description                                                                    |
| ---------- | -------- | ------ | ------------------------------------------------------------------------------ |
| `order_id` | Yes      | string | External order identifier. Same value used in `/orders/{id}` paths.            |
| `order`    | No       | object | Full order object. Present on `order.injected` and optionally on other events. |

<ResponseExample>
  ```json order.injected theme={null}
  {
    "id": "evt_01j8k2m3n4p5q6r7s8t9u0v1w",
    "type": "order.injected",
    "created_at": "2026-06-17T14:32:00.000Z",
    "data": {
      "order_id": "ord-48291",
      "order": {
        "uid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "id": "ord-48291",
        "brand_id": "0001",
        "store_id": "downtown",
        "channel_id": "app",
        "customer_id": "cust-001",
        "is_anonymous": false,
        "fulfillment_id": "delivery",
        "fulfillment_type": "DELIVERY",
        "status": "OPEN",
        "payment_status": "PAID",
        "fulfillment_status": "IDLE",
        "lines": [
          {
            "type": "PRODUCT",
            "uid": "f6a7b8c9-d0e1-2345-f678-901234567890",
            "id": "burger-classic",
            "parent_uid": null,
            "parent_id": null,
            "name": "Classic burger",
            "metadata": {},
            "quantity": 1,
            "tax_rate": 0.15,
            "discount_rate": 0,
            "unit_subtotal": 12.99,
            "unit_discount": 0,
            "unit_tax_total": 1.95,
            "unit_total": 14.94,
            "subtotal": 12.99,
            "discount_total": 0,
            "tax_total": 1.95,
            "total": 14.94,
            "unit_group_subtotal": 12.99,
            "unit_group_discount": 0,
            "unit_group_tax_total": 1.95,
            "unit_group_total": 14.94,
            "group_subtotal": 12.99,
            "group_discount": 0,
            "group_tax_total": 1.95,
            "group_total": 14.94
          }
        ],
        "customer": {
          "customer_info": {
            "display_name": "Jane Doe",
            "first_name": "Jane",
            "last_name": "Doe",
            "email": "jane@example.com",
            "phone": "+593991234567"
          },
          "address_info": {
            "name": "Jane Doe",
            "email": "jane@example.com",
            "phone": "+593991234567",
            "address_line1": "Av. Amazonas 123",
            "unit_number": "4B",
            "postal_code": "170150",
            "city": "Quito",
            "country": "Ecuador"
          }
        },
        "payment_intents": [
          {
            "method": "CREDIT",
            "provider": "STRIPE",
            "status": "PAID",
            "totals": {
              "currency": "USD",
              "subtotal": 12.99,
              "discount_rate": 0,
              "discount_total": 0,
              "tax_rate": 0.15,
              "tax_total": 1.95,
              "total": 14.94
            },
            "placed_at": "2026-06-17T14:31:45.000Z",
            "resolved_at": "2026-06-17T14:31:50.000Z"
          }
        ],
        "totals": {
          "currency": "USD",
          "subtotal": 12.99,
          "discount_rate": 0,
          "discount_total": 0,
          "tax_rate": 0.15,
          "tax_total": 1.95,
          "total": 14.94
        },
        "metadata": {},
        "issued_at": "2026-06-17T14:31:45.000Z"
      }
    }
  }
  ```
</ResponseExample>

## Order status fields in the payload

These fields describe the order state **at the time the webhook fires**. On `order.injected`, map them directly into your POS ticket.

| Field                | Values                                                                              | Meaning                       |
| -------------------- | ----------------------------------------------------------------------------------- | ----------------------------- |
| `status`             | `OPEN`, `CLOSED`, `COMPLETED`, `CANCELLED`, `REFUNDED`                              | Overall order lifecycle       |
| `payment_status`     | `IDLE`, `PENDING`, `PAID`, `FAILED`, `REFUNDED`                                     | Payment processing state      |
| `fulfillment_status` | `IDLE`, `PREPARING`, `PREPARED`, `DELIVERING`, `DELIVERED`, `CANCELLED`, `RETURNED` | Kitchen and delivery progress |

## Handling `order.injected`

1. Read `data.order_id` and the full `data.order` object from the payload.
2. Map `fulfillment_id`, `store_id`, `channel_id`, `brand_id`, and line `id` values to your POS catalog (same external ids used during [menu sync](/docs/integrations-api/guides/menu-sync)). Use line `metadata` for POS-specific hints that do not belong on the order-level `metadata` object.
3. Create the ticket in your POS or RMS using the totals, customer, and status fields **as sent** — do not assume the order is always `PAID` or `PREPARING`; respect the values in the payload.
4. Return HTTP `200` (or another `2xx`) promptly. Perform slow POS calls asynchronously after responding if needed.
5. When your POS changes fulfillment progress, call [Update order status](/docs/integrations-api/orders/\[id]/status/patch).

<Warning>
  If your endpoint returns an error for `order.injected`, Fire spark retries
  delivery. Guard against creating duplicate POS tickets — check whether
  `order_id` was already injected before opening a new ticket.
</Warning>

## Fetching an order manually

If you receive `order.created` or `order.updated` without the full `order` object, call [Get order](/docs/integrations-api/orders/\[id]/get) with the external `order_id`. Requires the `orders:read` scope.

## Related endpoints

<CardGroup cols={2}>
  <Card title="Get order" icon="file-text" href="/docs/integrations-api/orders/[id]/get">
    Retrieve the full order by external id
  </Card>

  <Card title="Update order status" icon="refresh-cw" href="/docs/integrations-api/orders/[id]/status/patch">
    Report POS fulfillment progress back to Fire spark
  </Card>
</CardGroup>
