Features / LIVE / Webhooks

Every alert as signed JSON to your own server.

Paste one https address and every level alert, fired alert line and order alert is POSTed to it with the price, the levels of the book at that second and a signature only you can check. Wire it into a bot, a journal or a dashboard.

LIVE.

Open the terminalIn the LIVE guide

The webhook block on the Level alerts page with its address, signing secret and test button

Why: every alert as data your own code can act on. Where: Account, Level alerts, Webhook.

How to use it

  1. Paste one https:// address and press Save.
  2. Press Send test event to post an event of type test.
  3. Open Signing secret and keep it on your server; verify every request with it.
  4. Rotate secret replaces it; the old one stops verifying at once.

What it measures

Every event on your list (the same symbols, events and holds as Telegram), every fired alert line and every order alert is POSTed to the address as JSON, signed with HMAC-SHA256 over the timestamp header, a dot and the raw body.

POST https://your.server/firmtape
Content-Type: application/json
X-FirmTape-Event: flip_cross
X-FirmTape-Event-Id: evt_3f9a0c6e21d4b7a8e5f10c2d
X-FirmTape-Timestamp: 1760034725
X-FirmTape-Signature: sha256=<hex HMAC-SHA256 of "<timestamp>.<raw body>">

{"version": 1, "id": "evt_3f9a0c6e21d4b7a8e5f10c2d", "type": "flip_cross",
 "symbol": "SPX", "level": {"kind": "flip", "value": 6715.5}, "price": 6712.25,
 "book": "volume", "ts": "2026-10-09T14:32:05-04:00", "day": "2026-10-09",
 "levels": {"flip": 6715.5, "call_wall": 6750, "put_wall": 6700, "put_support": 6700},
 "text": "SPX crossed below the flip 6,715.50", "direction": "below"}
FieldWhat it holds
typeflip_cross, wall_break, regime_change, wall_move, flip_move, level_near; for an alert line price_alert (a price line) or level_line (a line that follows a level); for an order rule's match order (its rule and order: time, structure, legs, expiry, size, premium, points from the flip and a wall, never a side); or test
levelkind is flip, call_wall, put_wall or, for a regime change, net_gamma; value the level; a moved level adds from
levelsThe book's flip, call_wall and put_wall at the event. Deprecated: put_support, the same number as put_wall, is sent until 2026-11-30; read put_wall
directionabove or below for a cross or an approach, up or down for a move, positive or negative for a regime
ts, dayWhen the event was found, ISO 8601 in New York time with its offset, and the ET trading day
idStable: every retry of one event carries the same id, so drop one you have seen
alsothreshold_points for flip_move and level_near (SPX points); from and basis for ES, NQ and RTY; levels_asof for a ticker's five-minute book
alertAn alert line's own: id, kind (price or level), level (flip, call_wall, put_wall), price, mode (once or repeat)

Verify every request: recompute the signature over the timestamp header, a dot and the raw body, compare in constant time, and refuse a timestamp more than five minutes old. The POSITIONS webhook is signed the same way; Geo's webhooks sign theirs differently, and the developer reference has each one and the single format all three move to. Until 2026-10-11 the POSITIONS webhook signed the raw body alone; that signature is still sent, until 2026-11-30, as X-FirmTape-Signature-V0.

// Node
const crypto = require("crypto");
function verify(rawBody, headers, secret) {
  const t = headers["x-firmtape-timestamp"], sig = 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 sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}

# Python
import hmac, hashlib, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
    t = headers.get("X-FirmTape-Timestamp", "")
    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(want, headers.get("X-FirmTape-Signature", ""))

Limits

Answer with any 2xx within three seconds. No answer, a 429 or a 5xx is tried again after one and four seconds, three tries in all; redirects are not followed. Ten failed events in a row switch the webhook off, and the account page shows why; saving the address switches it back on. Up to thirty events a minute per account. An address that resolves to a private, loopback or link-local address is refused.

Related

Questions

Can I receive alerts without Telegram?
Yes. The webhook, push and Telegram are independent destinations; turn on any of them.
How do I test my endpoint?
Press Send test event on the Level alerts page. It posts an event of type test with the same headers and signature.
What happens if my server is down?
The event is tried three times in all. After ten failed events in a row the webhook switches off until you save the address again.
Does the payload say which way to trade?
No. It carries the event, the price and the levels of the book at that second. An order event carries the order's structure and size, never a side.

Open the terminalAll of LIVE