Create a webhook
https://api.tickerbot.io/ v2/ webhooksCanonical create: a webhook is a trigger plus a delivery.
Body parameters
objectrequiredWhat fires the webhook. A discriminated object — trigger.type picks the shape, and the fields below belong inside it. Each shape is also available as a flat-params shortcut: POST /v2/scan/subscribe, /v2/tickers/{t}/subscribe, /v2/signals/{s}/subscribe, /v2/events/subscribe.
enumrequiredWhich trigger shape the rest of the object uses.
string**scan**: required — the SQL WHERE any ticker must match to fire. **ticker**: required — WHERE fragment evaluated for that ticker (auto-scoped; don't add ticker = … yourself; trigger.condition accepted as an alias). **event**: optional row-STATE filter on the event's ticker at fire time (market_cap > 1e10).
string**signal**: required — a built-in signal name (e.g. rsi_14) or one of your custom signals (custom SQL is expanded and frozen at creation).
string**signal**: required for numeric signals — a single bound like >70; sending one with a boolean or custom signal returns 400 (it does not apply). **ticker**: accepted as the original alias of trigger.q.
string**ticker**: required — the symbol to watch (e.g. NVDA). **signal**: optional — restrict the signal to one symbol (omit to watch the whole universe).
string**event**: optional symbol list, max 50 (e.g. AAPL,NVDA). Mutually exclusive with trigger.universe.
string[]**event**: required — event kinds to fire on, array or comma list (e.g. split,analyst).
string**event**: optional event-CONTENT filter in the /v2/events grammar over (ticker, ts, kind, payload) — e.g. payload->>'firm' = 'Goldman Sachs'. Composes with trigger.q.
string**scan / signal / event**: optional universe slug (top_10, top_100, or one of yours) scoping which tickers can fire. Mutually exclusive with trigger.tickers on event.
stringHTTPS delivery URL (the webhook channel), max 1024 characters. Omit for in-app delivery, or use channel + discord_url/device_id for other channels.
enumDelivery channel. See Delivery channels.
webhook | POST the payload to an HTTPS endpoint you own, signed with X-Tickerbot-Signature. |
in_app | Deliver to the in-app feed. No external receiver, so nothing to validate and no test_url. |
discord | Post to a Discord channel via its webhook URL. Delivered as a Discord embed, never HMAC-signed — the URL is itself the credential. |
mobile_push | Push to a registered device. Requires a device_id from Register a device. |
stringDiscord webhook URL (channel discord).
stringRegistered device id (channel mobile_push, see /v2/devices).
enumdefault realtimeEvaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only realtime is accepted on them (400 otherwise).
realtimedefault | Evaluate on every refresh — the canonical value. (1m is a deprecated alias that collapses to this.) |
hourly | Batch schedule: evaluate once an hour. |
nyse_open | Batch schedule: evaluate once per session, at the NYSE open. |
stringDisplay name, max 80 characters. Defaults to an auto-generated one from the trigger.
stringExtra columns echoed in fired payloads' match rows (fields accepted as an alias). Not accepted on event triggers (400) — event deliveries carry the event payload, not state rows.
stringdefault market_capColumn the fired payload's match lists are sorted by before the 100-row cap is applied — so a truncated list is the deterministic top 100, not an arbitrary sample. Same contract as POST /v2/scan. Not accepted on event triggers (they deliver one event at a time).
enumdefault descSort direction for order. Not accepted on event triggers (400).
asc | Ascending — smallest or earliest first. |
descdefault | Descending — largest or most recent first. |
Headers
stringOptional unique string (≤255 chars). A retry carrying the same key within 24h replays the original response (Idempotency-Replayed: true header) instead of creating a duplicate; a concurrent duplicate gets 409 idempotency_in_flight; reusing a key for a *different* request (another method or path) gets 422 idempotency_key_reused. 5xx responses are not stored — those retries re-execute.
Status codes
201GET /v2/webhooks/{id}) + test_url.400trigger, bad condition, or invalid delivery fields.403webhook_tier_required (Free plan) or webhook_limit_reached (at your plan's cap).404device_not_found — the device_id isn't registered to your account.409idempotency_in_flight — a concurrent request with the same Idempotency-Key is still executing; retry after it settles.422idempotency_key_reused — the Idempotency-Key was already used for a *different* request (another method or path). Use a fresh key per distinct request.501channel_not_implemented — channel: "slack" is declared but not yet deliverable.Notes
- Four trigger types. `scan` —
{ "type": "scan", "q": "…", "universe"?: "slug" }fires on any ticker matching a SQL WHERE. `ticker` —{ "type": "ticker", "ticker": "NVDA", "condition": "…" }fires when one ticker matches, auto-scoped, so do not addticker = …yourself. - `signal` —
{ "type": "signal", "signal": "rsi_14", "condition"?: ">70", "ticker"?: "NVDA", "universe"?: "slug" }fires on a named signal: booleans need no condition, numerics require a single bound, and custom signals resolve as booleans with their SQL frozen at creation. - `event` —
{ "type": "event", "kinds": ["split","analyst"], "tickers"?: […] | "universe"?: "slug", "q"?: "…", "event_q"?: "…" }fires when NEW events land. Two composable filters:qis a row-STATE filter on the event's ticker (market_cap > 1e10),event_qan event-CONTENT filter in the/v2/eventsgrammar over(ticker, ts, kind, payload). Deliversevents.fired; latency is ingest cadence (analyst within the hour, corporate kinds daily). See Subscribe to events. - The per-resource subscribe endpoints (
POST /v2/tickers/{T}/subscribe,/v2/signals/{S}/subscribe,/v2/scan/subscribe,/v2/events/subscribe) are permanent shorthand for this endpoint — every door builds the identical webhook object, listed, updated, and deleted at/v2/webhooks. - Wire-delivery responses (webhook / Discord / mobile-push) include a
test_url. Creating does NOT auto-fire — call it to validate your receiver. In-app subscriptions have nothing to validate and carry notest_url. - Prefer the subscribe shorthand when working from a resource:
POST /v2/tickers/{T}/subscribeetc. — same object, fewer keystrokes.
More examples
curl -X POST "https://api.tickerbot.io/v2/webhooks" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"trigger": { "type": "ticker", "ticker": "NVDA", "condition": "rsi_14 > 70 AND relative_volume > 2" },
"target_url": "https://example.com/hook"
}'{
"as_of": "2026-07-27T15:41:00.000Z",
"id": "wh_smRsF3-z36o",
"name": "NVDA: rsi_14 > 70 AND relative_volume > 2",
"q": "ticker = 'NVDA' AND (rsi_14 > 70 AND relative_volume > 2)",
"status": "active",
"subscription_origin": { "type": "ticker", "ref": "NVDA", "condition": "rsi_14 > 70 AND relative_volume > 2" },
"test_url": "/v2/webhooks/wh_smRsF3-z36o/test"
}curl -X POST "https://api.tickerbot.io/v2/webhooks" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"trigger": { "type": "signal", "signal": "rsi_14", "condition": ">70", "universe": "top_100" },
"channel": "discord",
"discord_url": "https://discord.com/api/webhooks/123456789012345678/aBcDeF…"
}'{
"as_of": "2026-07-27T15:42:00.000Z",
"id": "wh_dC9rTq-k77p",
"name": "rsi_14 >70 · top_100",
"q": "rsi_14 > 70",
"universe_id": "top_100",
"channel": "discord",
"status": "active",
"subscription_origin": { "type": "signal", "ref": "rsi_14", "condition": ">70" },
"test_url": "/v2/webhooks/wh_dC9rTq-k77p/test"
}curl -X POST "https://api.tickerbot.io/v2/webhooks" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"trigger": {
"type": "event",
"kinds": ["analyst"],
"q": "market_cap > 1e10",
"event_q": "payload->>'"'"'firm'"'"' = '"'"'Goldman Sachs'"'"'"
},
"target_url": "https://example.com/hook"
}'{
"as_of": "2026-07-27T15:43:00.000Z",
"id": "wh_e9Kp3q-r52s",
"name": "events: analyst",
"q": "market_cap > 1e10",
"trigger_kind": "event",
"event_kinds": ["analyst"],
"status": "active",
"subscription_origin": { "type": "event", "ref": "analyst", "condition": "market_cap > 1e10" },
"test_url": "/v2/webhooks/wh_e9Kp3q-r52s/test"
}