# interest_cross

> Sent when the price crosses a level of interest and holds the new side. When a wall or other levels stand on the same price, one event goes out for all of them, named as the chart's level row names them, and joins lists the types it stands for.

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

Sent when the price crosses a level of interest and holds the new side. When a wall or other levels stand on the same price, one event goes out for all of them, named as the chart's level row names them, and `joins` lists the types it stands for.

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

Plan LIVE · Payload version 1 · Signed HMAC-SHA256 · Schema interest_cross.schema.json

**This event serves:** [LIVE: Level alerts on a watchlist](https://firmtape.com/docs/live#level-alerts), [LIVE: Webhooks](https://firmtape.com/docs/live#webhooks). **Plan:** [LIVE](https://firmtape.com/features/live). LIVE: today's session every second, alerts, ASK, the levels API and the platform integrations, and 5 LAB credits a month. Every finished session is free in REPLAY. Turn it on: Account, Level alerts: a webhook address, and the levels of interest card on with the event, the symbol on that card.

## When it is sent

- The new side has held for twenty seconds.
- The same level is not repeated within fifteen minutes.

## Payload

| Field | Meaning | Unit |
| --- | --- | --- |
| `version` (1) | Payload version, `1`. |  |
| `id` (string) | Event id, `evt_` and 24 hex digits. The same on every try and for every account the event goes to: drop one you have seen. |  |
| `type` ("interest_cross") | `interest_cross`. |  |
| `symbol` (string) | SPX, ES, NQ, RTY or a ticker. |  |
| `level` (object) | The level the event is about. |  |
| `level.kind` (flip \| call_wall \| put_wall \| net_gamma \| price \| cvd \| level_of_interest \| null) | `flip`, `call_wall`, `put_wall`, `net_gamma`, `price` for a price line,, `level_of_interest` for C1 to C4 and P1 to P4 (with `rank`), or `cvd` for a CVD line (its value in contracts of calls minus puts in one bar). |  |
| `level.value` (number \| null) | Its value at the event. | index points |
| `level.from` (number \| null) | Moves: where it stood before. | index points |
| `level.rank` (string) | Levels of interest: the rank, `C1` to `C4` or `P1` to `P4`, as on the chart. |  |
| `price` (number \| null) | The price at the event. Null on a `premarket` event (see `phase`). | index points |
| `book` (volume \| open_interest \| measured \| volume+open_interest) | The book the event was read in, as the account picked it for that card: `volume`, `open_interest`, `measured` (SPX, ES, NQ), or `volume+open_interest` (levels of interest only, as on the chart). A symbol without the book picked is read in `volume`. |  |
| `ts` (string) | When it was found, ISO 8601 in New York time. |  |
| `day` (string) | The ET trading day. |  |
| `levels` (object) | The flip and the walls of the book in `book` at the event (the volume book's for `volume+open_interest`). |  |
| `levels.flip` (number \| null) | Flip. | index points |
| `levels.call_wall` (number \| null) | Call wall. | index points |
| `levels.put_wall` (number \| null) | Put wall. | index points |
| `levels.put_support` (number \| null) deprecated | The put wall under its first name. Deprecated: read `put_wall`; removed after 2026-11-30. | index points |
| `text` (string) | The sentence Telegram and push show. |  |
| `direction` (above \| below) | The side the price crossed to. |  |
| `threshold_points` (number) | Sized events (`flip_move`, `level_near`, `interest_near`): the size the account picked, SPX points. | index points |
| `from` (string) | ES, NQ, RTY: the index whose book moved by the basis gives the levels. |  |
| `basis` (number \| null) | ES, NQ, RTY: the basis at the event. | index points |
| `levels_asof` (string \| null) | Symbols on a five-minute book: when that book was built. |  |
| `phase` (premarket) | Present only when a price line fired before the bell, from 04:00 ET to the open on a trading day: `premarket`. The line was crossed on the premarket's minute prices; `price` is then null, and `levels` are the previous session's closing levels. Absent on every event in the session. |  |
| `alert` (object) | Alert lines only: the line that fired. |  |
| `alert.id` (string) | The line, `al_...`. |  |
| `alert.kind` (string) | `price`, `level` or `delta` (a CVD line). |  |
| `alert.level` (string \| null) | A level line: `flip`, `call_wall` or `put_wall`. |  |
| `alert.price` (number \| null) | A price line: its price. | index points |
| `alert.mode` (string) | `once` or `repeat`. |  |
| `joins` (array) | One event for several levels on one price (a wall and levels of interest): the types it stands for, e.g. `["wall_break", "interest_cross"]`. |  |

## 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: interest_cross
X-FirmTape-Event-Id: evt_3f9a0c6e21d4b7a8e5f10c2d
X-FirmTape-Timestamp: 1760034725
X-FirmTape-Signature: sha256=9b1e...
```

## Body

```json
{
  "version": 1,
  "id": "evt_3f9a0c6e21d4b7a8e5f10c2d",
  "type": "interest_cross",
  "symbol": "SPX",
  "level": {
    "kind": "level_of_interest",
    "value": 6700,
    "rank": "P1"
  },
  "price": 6698.5,
  "book": "volume+open_interest",
  "ts": "2026-10-21T10:35:05-04:00",
  "day": "2026-10-21",
  "levels": {
    "flip": 6715.5,
    "call_wall": 6750,
    "put_wall": 6700,
    "put_support": 6700
  },
  "text": "SPX broke below 6,700.00 · PW VOL OI · P1 VOL OI",
  "direction": "below",
  "joins": [
    "wall_break",
    "interest_cross"
  ]
}
```

## 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)
```
