# chorus_strength

> Sent when the chorus's strength (the CHORUS panel's -100 to 100 reading of how far the counted sets move together with SPX or NDX) reaches your chosen size, up or down.

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

Sent when the chorus's strength (the CHORUS panel's -100 to 100 reading of how far the counted sets move together with SPX or NDX) reaches your chosen size, up or down.

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

Plan LIVE · Payload version 1 · Signed HMAC-SHA256 · Schema chorus_strength.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 a chorus card on with its size, the symbol SPX or NDX on that card.

## When it is sent

- Read on each closed minute of the chorus of the symbol on the card, SPX or NDX.
- Each size and way fires once, then re-arms only after the strength has fallen back under 20 that way.
- The first minute of a day only arms: a day that opens past the size sends nothing until it re-arms.

## 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` ("chorus_strength") | `chorus_strength`. |  |
| `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 \| chorus \| 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`), `cvd` for a CVD line (its value in contracts of calls minus puts in one bar), or `chorus` for the chorus's strength (its value from -100 to 100). |  |
| `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` (up \| down) | The way the strength reached the size: `up` at +size or above, `down` at -size or below. |  |
| `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"]`. |  |
| `threshold` (number) | The size you picked: 40, 60 or 80, a strength, not points. |  |
| `sets` (object) | The counted sets at that minute: how many move in unison up, down, how many go against the index, and how many are counted. |  |
| `sets.up` (integer) |  |  |
| `sets.down` (integer) |  |  |
| `sets.against` (integer) |  |  |
| `sets.of` (integer \| null) |  |  |

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

## Body

```json
{
  "version": 1,
  "id": "evt_8b21d4c0e7f93a6512cd04ab",
  "type": "chorus_strength",
  "symbol": "SPX",
  "level": {
    "kind": "chorus",
    "value": -43
  },
  "price": 6731.5,
  "book": "volume",
  "ts": "2026-10-22T10:21:04-04:00",
  "day": "2026-10-22",
  "levels": {
    "flip": 6745.5,
    "call_wall": 6800,
    "put_wall": 6700,
    "put_support": 6700
  },
  "text": "SPX CHORUS ▼ strength 43, past 40 · 4 of 8 sets down · 10:20 ET",
  "direction": "down",
  "threshold": 40,
  "sets": {
    "up": 0,
    "down": 4,
    "against": 1,
    "of": 8
  }
}
```

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