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

# Webhooks

> Receive order status updates by configuring a callback URL when creating an order.

When you create an order with a `callback` object, Origin One sends an HTTP `POST` to your URL every time the order or one of its services changes status. Each delivery is a small event that names the order; fetch the order for its current state. See the [Order updated](/api-reference/webhooks/order-updated) webhook reference for the exact payload.

## Payload

```json theme={null}
{
  "object": "event",
  "id": "4821",
  "type": "order.updated",
  "createdAt": "2026-10-02T10:00:00.000Z",
  "data": {
    "object": "order",
    "id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
    "code": "CPmJ6QbYMo",
    "url": "/api/v2/orders/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
  }
}
```

The event carries no status. `GET` the order at `data.url` with your API token to read its current `status`, `isFinal`, and services — see [Order lifecycle](/order-lifecycle) for how to interpret them. Retries of the same delivery carry the same `id`.

## Configure the callback

Pass a `callback` object when [creating an order](/api-reference/orders/create). Only `url` is required; it must be an HTTPS endpoint you control.

```json theme={null}
{
  "callback": {
    "url": "https://backend.example.com/origin-one/order-updates",
    "headers": {
      "X-Environment": "production"
    },
    "auth": {
      "type": "bearer",
      "token": "your-shared-token"
    }
  }
}
```

## Authentication options

Set `callback.auth.type` to control how Origin One authenticates with your endpoint:

| `type` | Behavior | Required fields |
| - | - | - |
| `bearer` | Sends `Authorization: Bearer <token>` | `token` |
| `basic` | Sends `Authorization: Basic <base64(username:password)>` | `username`, `password` |
| `apiKey` | Sends `X-API-Key: <apiKey>` | `apiKey` |
| `custom` | Merges the `custom` object's key-value pairs into the request headers | `custom` |

Independently of `type`, you can also set `secretKey` to receive an HMAC signature with every delivery — see below.

## Verify the signature

When `callback.auth.secretKey` is set, every delivery includes an `X-Signature` header in the form `sha256=<hex>`: the HMAC-SHA256 of the raw JSON request body, keyed with your secret. Verify it against the raw body bytes — before any JSON parsing or re-serialization — and compare using a constant-time function:

<CodeGroup>
  ```javascript verify.js theme={null}
  const crypto = require("node:crypto");

  function isValidSignature(rawBody, signatureHeader, secretKey) {
    const expected =
      "sha256=" +
      crypto.createHmac("sha256", secretKey).update(rawBody).digest("hex");
    return crypto.timingSafeEqual(
      Buffer.from(signatureHeader),
      Buffer.from(expected),
    );
  }
  ```

  ```python verify.py theme={null}
  import hashlib
  import hmac


  def is_valid_signature(raw_body: bytes, signature_header: str, secret_key: str) -> bool:
      expected = "sha256=" + hmac.new(secret_key.encode(), raw_body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(signature_header, expected)
  ```
</CodeGroup>

## Request headers

Every delivery includes:

| Header | Value |
| - | - |
| `Content-Type` | `application/json` |
| `User-Agent` | `Origin-Instant-Test-Notifications/1.0` |
| `X-Notification-Id` | The event `id`; the same on every retry of this delivery. |
| `X-Notification-Event` | The event `type`, such as `order.updated`. |
| `X-Notification-Timestamp` | When this attempt was sent (ISO 8601). |
| `X-Notification-Subscription` | Identifier of the webhook subscription created with the order. |
| `X-Signature` | Only when `secretKey` is configured — see [Verify the signature](#verify-the-signature). |

Plus any custom headers you configured in `callback.headers` and the authentication headers from `callback.auth`.

## Outbound IP addresses

Webhook deliveries originate from a fixed set of IP addresses. If your endpoint sits behind a firewall, allowlist the addresses for the environment you integrate with:

| Production | Staging |
| - | - |
| `44.221.225.113` | `44.215.24.91` |
| `3.210.100.188` | `34.197.113.94` |
| `54.174.141.24` | `3.220.245.245` |

## Delivery and retries

Respond with any `2xx` status within **30 seconds** to acknowledge a delivery. A non-`2xx` response or a timeout counts as a failure, and the delivery is retried with exponential backoff:

| Attempt | Delay after previous failure |
| - | - |
| 1 | — (initial delivery) |
| 2 | 30 seconds |
| 3 | 1 minute |
| 4 | 2 minutes |
| 5 | 4 minutes |

After the fifth failed attempt the delivery is abandoned. Later status changes on the same order trigger new deliveries as usual.

## Best practices

* **Acknowledge fast, process async.** Return `2xx` immediately and process the payload in a background job, so slow processing doesn't get counted as a failed delivery.
* **Handle duplicates and ordering.** Retries and closely-spaced status changes mean deliveries can arrive more than once or out of order. Drop events whose `id` you have already seen, and because every delivery makes you fetch the order's current state, compare its `statusUpdatedAt` against the last value you processed to discard stale updates.
* **Don't rely on webhooks alone.** If your endpoint is down past the retry window, deliveries for that change are lost — reconcile periodically with [Get an order](/api-reference/orders/get).
* **Use `secretKey`** so you can verify deliveries genuinely come from Origin One.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.