# order

> Sent when a new order in today's SPX ledger matches one of your order rules.

Source: https://firmtape.com/docs/developers/live/events/order

Sent when a new order in today's SPX ledger matches one of your order rules.

`POST your https address` · `X-FirmTape-Event: order`

Plan ORDERS · Payload version 1 · Signed HMAC-SHA256 · Schema order.schema.json

**This event serves:** [ORDERS: Order rules](https://firmtape.com/docs/orders#order-rules), [LIVE: Webhooks](https://firmtape.com/docs/live#webhooks). **Plan:** [ORDERS](https://firmtape.com/features/orders). Free on every finished session. Today's ledger, live, and the live POSITIONS check are the ORDERS plan. Turn it on: ORDERS, RULES in the terminal; the alert goes where your level alerts go.

## When it is sent

- Today's ledger is read every five seconds in session; a new order that matches a rule that is on goes out once.
- At most thirty order alerts an hour per account.

## Payload

| Field | Meaning | Unit |
| --- | --- | --- |
| `version` (1) | Payload version, `1`. |  |
| `id` (string) | Event id: the order's, the same for every account. |  |
| `type` ("order") | `order`. |  |
| `symbol` (string) | `SPX`. |  |
| `ts` (string) | When it was found, ISO 8601 in New York time. |  |
| `day` (string) | The ET trading day. |  |
| `rule` (object) | The rule that matched. |  |
| `rule.id` (string) | The rule's id. |  |
| `rule.name` (string) | Its name. |  |
| `order` (object) | The order, as the ledger has it. |  |
| `order.id` (string) | The order's id in the session. |  |
| `order.time` (string \| null) | When it printed, ET. |  |
| `order.structure` (string) | The structure, in words. |  |
| `order.legs` (array) | The legs. |  |
| `order.legs[].strike` (number) | Strike. |  |
| `order.legs[].right` (string) | `C` or `P`. |  |
| `order.expiry` (string \| null) | The expiry carrying most of its gamma. |  |
| `order.expiry_bucket` (string \| null) | The expiry bucket the rules read. |  |
| `order.size` (number \| null) | Contracts. |  |
| `order.premium_usd` (number \| null) | Net premium. | USD |
| `order.flip_points` (number \| null) | Points from the flip at that minute. | index points |
| `order.flip_side` (above \| below \| null) | Its side of the flip. |  |
| `order.wall_points` (number \| null) | Points from the nearest wall. | index points |
| `order.strike_share_pct` (number \| null) | Its share of its strike's volume, percent. |  |
| `order.open_interest` (array \| null) | The legs' morning open interest. |  |
| `text` (string) | The sentence Telegram and push show. |  |

## Delivery

- **Answer**: Any 2xx within 3 seconds. Redirects are not followed.
- **Retries**: No answer, a 429 or a 5xx: tried again after 1 s and 4 s, three tries in all, the same id each time.
- **Switched off**: After 10 failed events in a row; the account page shows why. Saving the address switches it on again.
- **Volume**: Up to 30 events a minute per account; past that they are dropped and counted on the account page.
- **Test**: `type: "test"`, from **Send test**
- **Log**: The last delivery (time, status, tries) on the account page.

## Request we send

```
POST https://your.server/firmtape
Content-Type: application/json
User-Agent: FirmTape-Webhooks/1
X-FirmTape-Event: order
X-FirmTape-Event-Id: evt_5c1d2e3f4a5b6c7d8e9f0a1b
X-FirmTape-Timestamp: 1760034725
X-FirmTape-Signature: sha256=9b1e...
```

## Body

```json
{
  "version": 1,
  "id": "evt_5c1d2e3f4a5b6c7d8e9f0a1b",
  "type": "order",
  "symbol": "SPX",
  "ts": "2026-10-09T13:43:10-04:00",
  "day": "2026-10-09",
  "rule": {
    "id": "r1",
    "name": "Large put spreads"
  },
  "order": {
    "id": "p79654",
    "time": "13:43",
    "structure": "put vertical",
    "legs": [
      {
        "strike": 7740,
        "right": "P"
      },
      {
        "strike": 7750,
        "right": "P"
      }
    ],
    "expiry": "Oct 9",
    "expiry_bucket": "0dte",
    "size": 1958,
    "premium_usd": 150000,
    "flip_points": 38,
    "flip_side": "above",
    "wall_points": 12,
    "strike_share_pct": 31,
    "open_interest": null
  },
  "text": "Put vertical 7740/7750, $150k net, Oct 9"
}
```

## Verify

```js
const crypto = require("node:crypto");
// rawBody: the request body as received, a Buffer or a string, before any JSON parsing
function verify(rawBody, headers, secret) {
  const t = headers["x-firmtape-timestamp"];
  const got = String(headers["x-firmtape-signature"] || "");
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const want = "sha256=" + crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
}
```

```python
import hmac, hashlib, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    t = headers.get("X-FirmTape-Timestamp", "")
    got = headers.get("X-FirmTape-Signature", "")
    if not t or abs(time.time() - int(t)) > 300:
        return False
    want = "sha256=" + hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(got, want)
```
