Server definition
- Hash
- sha256:f62638754741c8da9c5ee84237afa96ecb504df782950779fa14539aa00fc555
- What it is
- What a remote MCP server returned when asked what it offers: 32 tools
The blob, as servednamed by its sha256
{
"instructions": null,
"tools": [
{
"description": "A named boolean predicate you can reference anywhere a built-in signal goes.",
"inputSchema": {
"properties": {
"description": {
"description": "Free-form notes. Max 500 chars. Absent or empty comes back as `\"\"` rather than null.",
"type": "string"
},
"expr": {
"description": "Boolean SQL predicate. May reference built-in signals and other custom signals you own. Must evaluate to true/false. Max 4000 chars. Stricter grammar than scan `q`: comparisons, `AND`/`OR`/`NOT`, `IN`, `BETWEEN`, `IS [NOT] NULL`, arithmetic, and the functions `abs`/`coalesce`/`round`/`least`/`greatest` only — no `LIKE`/`ILIKE`, no `CASE`, no `::` casts, no other functions. An expression that scans fine can still be rejected here with `compile_failed`. What you send is what you read back: responses echo your expression as stored, not its expansion. A signal referencing another custom of yours returns the reference as you typed it — the inlined SQL exists only internally, and is what a subscribe endpoint freezes into a webhook. The one rewrite: a column named under its pre-2026-09-07 spelling (`bollinger_pct_b`) is stored under its current name (`bollinger_b`) and reported in `_meta.deprecated_columns`.",
"type": "string"
},
"name": {
"description": "Slug — `^[a-z][a-z0-9_]{0,63}$`. Must not collide with any built-in signal name, and 15 names are reserved outright: `columns`, plus the `/v2/series` OHLCV aliases `open`/`high`/`low`/`close`/`volume`/`vwap`/`trades` and `o`/`h`/`l`/`c`/`v`/`vw`/`n` (those resolve to bars before custom lookup). This is the signal's API handle: it's what you reference in `q` and in the CRUD path.",
"type": "string"
}
},
"required": [
"name",
"expr"
],
"type": "object"
},
"name": "tickerbot_create_custom_signal",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Only when `expr` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored `expr` carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"signal": {
"description": "The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.",
"type": "object"
}
},
"required": [
"as_of",
"signal"
],
"type": "object"
}
},
{
"description": "Create a named ticker list owned by your account.",
"inputSchema": {
"properties": {
"description": {
"description": "Free-form notes, up to 500 characters. Stored as `\"\"` when omitted.",
"type": "string"
},
"id": {
"description": "Optional slug — becomes the universe's permanent handle everywhere (`?universe=`, subscribe `universe`, CRUD path). Pattern `^[a-z][a-z0-9_]{0,62}$` — starts with a lowercase letter, then lowercase letters/digits/underscore, 63 chars max; the value is trimmed and lowercased before validation. `top_10` and `top_100` are reserved for system universes and rejected with 400. Must be unique within your account. Generated (`u_…`) if omitted.",
"type": "string"
},
"name": {
"description": "Human-readable label, up to 80 characters. Display-only — never used to reference the universe.",
"type": "string"
},
"tickers": {
"description": "Ticker symbols, up to 10,000. Validated against the active universe. `[]` is accepted — a shell universe you can fill later via PATCH.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"name",
"tickers"
],
"type": "object"
},
"name": "tickerbot_create_universe",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"description": {
"description": "Free-form notes; `\"\"` when unset.",
"type": "string"
},
"effective_at": {
"description": "System universes only; absent on yours.",
"type": "number"
},
"id": {
"description": "The slug — the universe's handle in `?universe=`.",
"type": "string"
},
"name": {
"description": "Display label.",
"type": "string"
},
"next_rebalance_at": {
"description": "System universes only; absent on yours.",
"type": "number"
},
"rebalance_method": {
"description": "System universes only; absent on yours.",
"type": "string"
},
"size": {
"description": "Member count.",
"type": "number"
},
"system": {
"description": "`false` — this is your universe.",
"type": "boolean"
},
"tickers": {
"description": "Members, after this call.",
"items": {
"type": "string"
},
"type": "array"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"description",
"tickers",
"size",
"system",
"created_at",
"updated_at"
],
"type": "object"
}
},
{
"description": "Canonical create: a webhook is a trigger plus a delivery. Trigger shapes: scan {type:\"scan\", q, universe?}; ticker {type:\"ticker\", ticker, condition}; signal {type:\"signal\", signal, ticker?, universe?, condition?}; event {type:\"event\", kinds, tickers?, universe?, event_q?}. The subscribe tools are flat sugar over exactly this. Webhooks need a paid plan (Free has no webhook slots).",
"inputSchema": {
"properties": {
"cadence": {
"default": "realtime",
"description": "Evaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).",
"enum": [
"realtime",
"hourly",
"nyse_open"
],
"type": "string"
},
"channel": {
"description": "Delivery channel. See Delivery channels.",
"enum": [
"webhook",
"discord",
"in_app",
"mobile_push"
],
"type": "string"
},
"columns": {
"description": "Extra 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.",
"type": "string"
},
"device_id": {
"description": "Registered device id (channel `mobile_push`, see /v2/devices).",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction for `order`. Not accepted on `event` triggers (400).",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"discord_url": {
"description": "Discord webhook URL (channel `discord`).",
"type": "string"
},
"name": {
"description": "Display name, max 80 characters. Defaults to an auto-generated one from the trigger.",
"type": "string"
},
"order": {
"default": "market_cap",
"description": "Signal 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).",
"type": "string"
},
"target_url": {
"description": "HTTPS delivery URL (the `webhook` channel), max 1024 characters. Omit for in-app delivery, or use `channel` + `discord_url`/`device_id` for other channels.",
"type": "string"
},
"trigger": {
"description": "What 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`.",
"properties": {
"condition": {
"description": "signal: required for every non-boolean signal — a single bound typed like the signal: `>70` (numeric), `<YYYY-MM-DDTHH:MM:SSZ` (timestamp), `>=YYYY-MM-DD` (date), `=ETF` (string); sending one with a boolean or custom signal returns 400 (it does not apply). ticker: accepted as the original alias of `trigger.q`.",
"type": "string"
},
"event_q": {
"description": "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`.",
"type": "string"
},
"kinds": {
"description": "event: required — event kinds to fire on, array or comma list (e.g. `split,analyst`).",
"type": "string"
},
"q": {
"description": "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`).",
"type": "string"
},
"signal": {
"description": "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).",
"type": "string"
},
"ticker": {
"description": "ticker: required — the symbol to watch (e.g. `NVDA`). signal: optional — restrict the signal to one symbol (omit to watch the whole universe).",
"type": "string"
},
"tickers": {
"description": "event: optional symbol list, max 50 (e.g. `AAPL,NVDA`). Mutually exclusive with `trigger.universe`.",
"type": "string"
},
"type": {
"description": "Which trigger shape the rest of the object uses.",
"enum": [
"scan",
"ticker",
"signal",
"event"
],
"type": "string"
},
"universe": {
"description": "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.",
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
}
},
"required": [
"trigger"
],
"type": "object"
},
"name": "tickerbot_create_webhook",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Returned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"channel_config": {
"description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).",
"type": "object"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"signing_secret": {
"description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"test_url": {
"description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at",
"channel_config",
"signing_secret"
],
"type": "object"
}
},
{
"description": "Delete one of your custom signals. Refused by default if another of your signals references it.",
"inputSchema": {
"properties": {
"force": {
"default": false,
"description": "When `true`, skip the reference check and delete. References will break on next recompile.",
"type": "boolean"
},
"signal": {
"description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.",
"type": "string"
}
},
"required": [
"signal"
],
"type": "object"
},
"name": "tickerbot_delete_custom_signal",
"outputSchema": null
},
{
"description": "Permanently delete one of your universes. Webhooks that reference the deleted universe fail on their next eval — clean those up first.",
"inputSchema": {
"properties": {
"force": {
"default": false,
"description": "A universe still referenced by live webhooks refuses to delete with `409 universe_referenced`. Pass `force=true` to delete anyway — those webhooks will match nothing until re-pointed or deleted.",
"type": "boolean"
},
"id": {
"description": "Universe slug.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_delete_universe",
"outputSchema": null
},
{
"description": "Delete a webhook subscription, and its delivery history with it.",
"inputSchema": {
"properties": {
"id": {
"description": "Webhook id.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_delete_webhook",
"outputSchema": null
},
{
"description": "Re-enable a disabled webhook and start it clean. Clears match-state, so the next eval treats every currently-matching ticker as new.",
"inputSchema": {
"properties": {
"id": {
"description": "Webhook id.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_enable_webhook",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at"
],
"type": "object"
}
},
{
"description": "OHLCV bars from 1-second through monthly. The prices underneath the table.",
"inputSchema": {
"properties": {
"adjusted": {
"description": "Default `true`: prices are split-adjusted — restated after each later split, as the tape is, so a series is continuous across a split. `false` returns the price as it printed that day (a name that later did a 1:10 reverse split reads `21.4` adjusted and `2.14` on the tape), which is what a broker fill or a chart from that time shows. Volume scales the other way. Un-adjusted on read from the splits table; the store is untouched.",
"type": "boolean"
},
"asof": {
"description": "Point-in-time: the most recent bar whose period had closed at or before that moment. A bare `YYYY-MM-DD` means that day's close. A full timestamp means the last FINISHED bar — at 10:00 ET on a Wednesday the day's close has not happened, so `1d` returns Tuesday's bar. A bare date on an intraday grid (`1m`, `1h`, …) is accepted and means the last bar of that UTC day. Returns one bar unless you also pass `limit`, which gives the last `limit` closed bars; follow `next_cursor` with the same parameters to keep paging back. Mutually exclusive with `before` (400). Unlimited depth. Response time: under 1.5 s cold, under 0.5 s warm.",
"type": "string"
},
"before": {
"description": "Return the N bars ending strictly before this date/timestamp — back-paging. Mutually exclusive with `cursor` (they are the same control — a 400 when both are sent).",
"type": "string"
},
"cursor": {
"description": "Continuation token from a prior response's `next_cursor`; sugar for `before` (sending both is a 400; a blank `cursor=` counts as absent), and the way to page inside a `from`/`to` window.",
"type": "string"
},
"from": {
"description": "Window start (inclusive): `YYYY-MM-DD`, ISO timestamp, or epoch-ms. Combines with `to` for an explicit window; page within it using `cursor`. Mutually exclusive with `asof` and `before` (400).",
"type": "string"
},
"interval": {
"description": "Bar interval. `2h`/`4h` roll up hourly bars; `1w`/`1mo` roll up daily bars into calendar weeks (Monday start) and months — the bar's `t` is the bucket start (UTC), and with `asof` the last bucket is the week/month to date.",
"enum": [
"1s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"4h",
"1d",
"1w",
"1mo"
],
"type": "string"
},
"limit": {
"default": 100,
"description": "Most-recent N bars. Max 1000 — an over-cap value is clamped, not an error.",
"type": "integer"
},
"session": {
"default": "all",
"description": "Sub-hour intervals only. `all` (default) includes pre- and post-market bars. `regular` keeps bars whose start is in 09:30–16:00 ET (DST-aware). Why you might want it: the vendor buckets trades by SIP report time, and late-reported off-exchange (Form T) prints on thin names can land 20 min to hours late in a pre-market minute — a `$1.70` print at 08:13 ET on a `$3.85` stock. Daily high/low are untouched by those. `limit` counts after the filter; paging still works.",
"enum": [
"all",
"regular"
],
"type": "string"
},
"ticker": {
"description": "Ticker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol.",
"type": "string"
},
"to": {
"description": "Window end (inclusive): a bare `YYYY-MM-DD` means through the end of that day, same as series. Mutually exclusive with `asof` and `before` (400).",
"type": "string"
}
},
"required": [
"ticker",
"interval"
],
"type": "object"
},
"name": "tickerbot_get_bars",
"outputSchema": {
"additionalProperties": false,
"properties": {
"adjusted": {
"description": "Whether the bars are split-adjusted — `true` unless you passed `adjusted=false`.",
"type": "boolean"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"bars": {
"description": "OHLCV bars, chronological, in the compact array shape. Bulk requests key this by symbol instead.",
"items": {
"type": "string"
},
"type": "array"
},
"count": {
"description": "Bars returned in single-symbol mode; the number of SYMBOLS in bulk mode.",
"type": "number"
},
"coverage": {
"description": "`covered` when bars were found, `no_data` when nothing exists for the symbol in the window (before its first session, a market holiday, a thin name with no prints). A page that could not be completed is never a 200: it is a `503 unavailable` with `Retry-After`.",
"type": "string"
},
"interval": {
"description": "The bar size served.",
"type": "string"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Absent on bulk (comma-list) requests, which are unpaged. Absent on bulk requests — page bulk symbol-by-symbol.",
"type": "string"
},
"note": {
"description": "Present only when there is something to disclose about how the page was served: the first request for a symbol at a sub-hour interval loads and stores its bars, so that one call is slower than later ones, and the note says so. Bulk responses carry `notes[symbol]` instead.",
"type": "string"
},
"notes": {
"description": "Bulk (comma-list) requests only: the per-symbol disclosures, keyed by symbol, in place of `note`.",
"type": "object"
},
"session": {
"description": "The session filter applied: `all` (default) or `regular` (09:30–16:00 ET, sub-hour intervals only).",
"type": "string"
},
"ticker": {
"description": "The symbol you asked for.",
"type": "string"
}
},
"required": [
"as_of",
"interval",
"adjusted",
"session",
"count",
"coverage",
"bars"
],
"type": "object"
}
},
{
"description": "Returns an ETF's constituents and their weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `holdings` is empty; `is_etf: true` with zero holdings means a real ETF whose holdings are not ingested yet. The reverse lookup (\"which ETFs hold NVDA\") is a scan filter on `etf_holders`, not this tool.",
"inputSchema": {
"properties": {
"limit": {
"default": 500,
"description": "Max holdings returned. Max 5000. When the cap cuts the list, the response sets `truncated: true` and `total` (the ETF's full holding count) — raise `limit` to at least `total` to get the full set, possible whenever `total` is within the 5000 cap (an over-cap `limit` is clamped to 5000, not an error). No `truncated` in the response means the list is complete.",
"type": "integer"
},
"ticker": {
"description": "ETF symbol. Case-insensitive.",
"type": "string"
}
},
"required": [
"ticker"
],
"type": "object"
},
"name": "tickerbot_get_etf_holdings",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Holdings in this page.",
"type": "number"
},
"holdings": {
"description": "Constituents, heaviest first, each with its weight.",
"items": {
"type": "string"
},
"type": "array"
},
"is_etf": {
"description": "Whether the symbol is an ETF, from the instrument type on its ticker record.",
"type": "boolean"
},
"ticker": {
"description": "The ETF you asked for.",
"type": "string"
},
"total": {
"description": "Total constituents held, before `limit`.",
"type": "number"
},
"truncated": {
"description": "`true` when `limit` cut the list short.",
"type": "boolean"
}
},
"required": [
"as_of",
"ticker",
"is_etf",
"count",
"holdings"
],
"type": "object"
}
},
{
"description": "Returns an ETF's sector weights, heaviest first. Always complete, since sector breakdowns are small. When the ticker is not an ETF, `is_etf` is false and `sectors` is empty.",
"inputSchema": {
"properties": {
"ticker": {
"description": "ETF symbol. Case-insensitive.",
"type": "string"
}
},
"required": [
"ticker"
],
"type": "object"
},
"name": "tickerbot_get_etf_sectors",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Sectors returned.",
"type": "number"
},
"is_etf": {
"description": "Whether the symbol is an ETF, from the instrument type on its ticker record.",
"type": "boolean"
},
"sectors": {
"description": "Sector weights, heaviest first, in the vendor's ETF-profile vocabulary.",
"items": {
"type": "string"
},
"type": "array"
},
"ticker": {
"description": "The ETF you asked for.",
"type": "string"
}
},
"required": [
"as_of",
"ticker",
"is_etf",
"count",
"sectors"
],
"type": "object"
}
},
{
"description": "Any signals for any tickers on one shared time grid — up to 50 tickers by 25 columns per call. One flat row per ticker per interval step, cursor-paged backward. `transitions_only: true` with boolean signals returns only the rows where a boolean flipped.",
"inputSchema": {
"properties": {
"asof": {
"description": "Point-in-time read: ONE row per ticker — the state at that instant — rather than a range. `YYYY-MM-DD` or a full ISO timestamp, the same meaning `asof` carries on `/v2/tickers`, `/v2/scan` and `/v2/signals`. Cannot be combined with `from`/`to` or `cursor` (400) — a point and a window are contradictory, and `limit` has no meaning under it. It also resolves WHICH COMPANY held the symbol at that instant: a ticker that changed hands returns the row of whoever traded it then, so `tickers=SHLD&asof=2010-06-30` returns Sears Holdings' price and `asof=2026-01-01` returns the Global X defence ETF. Returns the most recent row at or before the instant, so a date inside a trading gap gives the last row before it; on the `1d` and `1w` grids a timestamp returns the previous session's row, never that day's close. Also: `next_cursor` is always `null` (every requested ticker is on the one page). A ticker with no row on the grid at or before the instant (outside the roughly 1,000-ticker minute tier, say) comes back as an empty array with `_meta.coverage[ticker].rows: 0`, never a 404. A bare date on the `1m`/`1h` grid is accepted and means the last row of that UTC day (the state reads 400 that combination; this grid read does not). The look-back is the grid's window: `1d` walks back without limit, `1h` 60 days and `1m` 7 days before the instant. At `interval=1q` the anchor is the date the quarter was REPORTED (earnings release / filing), not fiscal period end — you get the latest quarter that was public knowledge at the instant, with restatements after it excluded. Response time: under 1.5 s cold, under 0.5 s warm.",
"type": "string"
},
"columns": {
"description": "Up to 25 columns (POST accepts an array): OHLCV names, signals, and your custom signals, freely mixed. Omitted → the ticker-history default set (price, change_1d_pct, relative_volume, market_cap), intersected with what the interval carries. At `1q`, `columns` is required and quarterly-only. `fields` accepted as an alias.",
"type": "string"
},
"cursor": {
"description": "Opaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates.",
"type": "string"
},
"from": {
"description": "Earliest timestamp (inclusive), `YYYY-MM-DD` or ISO. Intraday requests default to a recent window (`1m`: 7 days, `1h`: 60 days) — the cursor keeps walking further back window-by-window, or pass `from` to widen it up front.",
"type": "string"
},
"interval": {
"default": "1d",
"description": "Grid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid. This is the grid, not a state tier: there is no `auto` here and nothing blends — under `asof` you get the last row on this grid, and a column the grid does not carry is a `400`.",
"enum": [
"1m",
"1h",
"1d",
"1w",
"1q"
],
"type": "string"
},
"limit": {
"default": 252,
"description": "Grid steps per page (shared across tickers). Max 1000 — an over-cap `limit` is clamped to 1000 (house convention, `limit=10000` means \"max\"). Separately, tickers × limit may not exceed 25,000 rows per page — over THAT cap is an explicit 400.",
"type": "integer"
},
"ticker": {
"description": "Single-symbol form — `/v2/series?ticker=AAPL` is ticker history in its canonical spelling. Exactly one of `ticker` or `tickers` is required.",
"type": "string"
},
"tickers": {
"description": "Comma-separated symbols, up to 50 (POST accepts a JSON array). Exactly one of `tickers` or `ticker` is required; when both are passed, `ticker` wins — so sending both silently narrows the request to one symbol.",
"type": "string"
},
"to": {
"description": "Latest timestamp (inclusive), `YYYY-MM-DD` or ISO.",
"type": "string"
},
"transitions_only": {
"description": "Only rows where a boolean signal changed state. Accepted spellings: `true`/`1`/`yes` and `false`/`0`/`no` (case-insensitive) — anything else is a 400, never silently off. Requires at least one boolean signal (built-in boolean or custom signal); each returned row carries `transitions: {column: \"enter\"|\"exit\"}`, and `_meta` lists the driving columns. Strict truth: only literal `true` is \"on\", so `null → true` is an enter and `true → null` an exit (a backfill boundary reads as an edge). Edges need a prior observation — on the oldest page of a walk the first row has no predecessor and yields no edge. A flip is dated by the state table and does not move with the column list: one recorded on a non-trading carry row keeps that date, with any bar columns `null` on that row (no bar exists there).",
"type": "boolean"
}
},
"type": "object"
},
"name": "tickerbot_get_series",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Per-column `sources` (`bars`, `state`, or `custom` for your own signals; `earnings`/`statements` at 1q) and per-ticker `coverage`, plus `non_trading_days_dropped` / `transitions_only` / `from_defaulted` when they apply. `deprecated_columns` lists any column you named under its pre-2026-09-07 spelling that was served under its current name (`requested`, `use`, `note`).",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"columns": {
"description": "Columns in the response, echoed.",
"items": {
"type": "string"
},
"type": "array"
},
"count": {
"description": "Rows per ticker in this page.",
"type": "number"
},
"interval": {
"description": "The grid granularity served.",
"type": "string"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"series": {
"description": "Keyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for.",
"type": "object"
},
"tickers": {
"description": "Symbols in the response, echoed.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"as_of",
"interval",
"tickers",
"columns",
"count",
"next_cursor",
"_meta",
"series"
],
"type": "object"
}
},
{
"description": "The state of a signal is the set of tickers matching it right now, or with `asof`, as of any moment. One name, the whole market, one call. Booleans need no `condition`; every other type needs one, typed like the signal: \">70\" (numeric), \"<YYYY-MM-DDTHH:MM:SSZ\" (timestamp), \">=YYYY-MM-DD\" (date), \"=ETF\" (string). Sorted by signal value desc for non-booleans.",
"inputSchema": {
"properties": {
"asof": {
"description": "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment; daily-only signals then carry the previous session's close, never that day's) — the same read as it stood then, unlimited depth. Full contract under As of a past date.",
"type": "string"
},
"condition": {
"description": "Required for every non-boolean signal; the shape follows the signal's `type` in the catalog. Single bound, `<op><value>`. numeric: `>70`, `<=200`, `!=0` (operators `>`, `>=`, `=`, `!=`, `<`, `<=`). timestamp: an ISO instant, `<YYYY-MM-DDTHH:MM:SSZ` or `>=YYYY-MM-DD` (a bare date is midnight UTC). date: `>=YYYY-MM-DD` or `=YYYY-MM-DD`. string: `=ETF` or `!=ETF` (`=` and `!=` only; quotes optional). A relative window (\"older than 15 minutes\") is a `/v2/scan` query: `price_asof < now() - interval '15 minutes'`. Sending a condition with a boolean or custom signal returns 400 (it does not apply).",
"type": "string"
},
"cursor": {
"description": "Opaque cursor from the previous response.",
"type": "string"
},
"include_active_since": {
"default": false,
"description": "Built-in booleans only: adds `active_since` and `days_live` per row — the first day of the current true streak, from daily state (the day after the last false day; if the boolean has never been false since it first computed, the first true day). Looks back five years, so a boolean true for longer reports the window edge as a lower bound. Live only — a 400 with `asof`.",
"type": "boolean"
},
"interval": {
"default": "auto",
"description": "Grain the past state is read at: `1m`, `1h`, `1d`, or `auto` (default). `auto` blends: each signal comes from its freshest grain at or before the instant (minute, then hourly, then the last closed daily session), so nothing is refused for grain and no ticker is dropped; `_meta.blended` and `_meta.intervals_present` say what contributed. Pin a grain for the fastest response: one grain is read instead of three, and every ticker is captured on the same clock. A signal the pinned grain does not store is a `400 interval_unavailable` naming the grains that carry it (`rsi_14`, fundamentals and valuation ratios are `1d`-only). Only valid alongside `asof`: a live read with `interval` is a 400.",
"enum": [
"1m",
"1h",
"1d",
"auto"
],
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 200.",
"type": "integer"
},
"signal": {
"description": "A signal name. Booleans (e.g. `golden_cross`, `above_sma_50`) are detected automatically; every other type (numeric `rsi_14`, timestamp `price_asof`, date `earnings_date`, string `asset_class`) requires a `condition`.",
"type": "string"
},
"sort_by": {
"default": "default",
"description": "Row order: `default` (alphabetic for booleans, highest-value-first for numerics) or `market_cap` (desc NULLS LAST; adds `market_cap` to each row). Live only — with `asof` it is a 400 (the snapshot's order is fixed).",
"enum": [
"default",
"market_cap"
],
"type": "string"
},
"universe": {
"description": "Optional. Scope to a system or caller-owned universe slug.",
"type": "string"
}
},
"required": [
"signal"
],
"type": "object"
},
"name": "tickerbot_get_signal",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "With `asof`: how the read was resolved — interval served and requested, blending, sources, frozen fields (see the as-of read below). On any read, `deprecated_columns` when you named the signal under its pre-2026-09-07 spelling and it was served under its current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"condition": {
"description": "The bound you passed, echoed; `null` for boolean and custom signals.",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"results": {
"description": "Matching tickers with the signal value.",
"items": {
"type": "string"
},
"type": "array"
},
"signal": {
"description": "The signal you asked for.",
"type": "string"
},
"universe": {
"description": "The universe you scoped to, echoed; `null` when unscoped.",
"type": "string"
}
},
"required": [
"as_of",
"signal",
"condition",
"universe",
"count",
"next_cursor",
"results"
],
"type": "object"
}
},
{
"description": "The full ticker row, every signal on the schema page, for one symbol or a comma list of up to 50. Right now, or with `asof`, as of any past date. Pass a comma list of up to 50 symbols for a batch (`data` keyed by symbol plus `not_found`). Crypto is the X-prefixed pair (X:BTCUSD) — bare BTC/ETH are US-listed ETFs.",
"inputSchema": {
"properties": {
"asof": {
"description": "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment; daily-only signals then carry the previous session's close, never that day's) — the same read as it stood then, unlimited depth. Full contract under As of a past date.",
"type": "string"
},
"interval": {
"default": "auto",
"description": "Grain the past state is read at: `1m`, `1h`, `1d`, or `auto` (default). `auto` blends: each signal comes from its freshest grain at or before the instant (minute, then hourly, then the last closed daily session), so nothing is refused for grain and no ticker is dropped; `_meta.blended` and `_meta.intervals_present` say what contributed. Pin a grain for the fastest response: one grain is read instead of three. On this single-ticker read a pinned grain returns the columns that grain stores (`rsi_14`, fundamentals and valuation ratios are `1d`-only, so they are absent under `1h`/`1m` rather than refused), and a grain with no row for the ticker within its 5-day window is a `404 interval_unavailable`. Only valid alongside `asof`: a live read with `interval` is a 400.",
"enum": [
"1m",
"1h",
"1d",
"auto"
],
"type": "string"
},
"ticker": {
"description": "One symbol, or a comma-separated list of up to 50 for a batch response keyed by symbol. Case-insensitive. Equities are bare symbols (`AAPL`); every other class carries a prefix — rates (`R:SOFR`), crypto (`X:BTCUSD`), fx (`X:EURUSD`). Bare `BTC`/`ETH` are US-listed ETFs, not spot crypto. See Tickers.",
"type": "string"
}
},
"required": [
"ticker"
],
"type": "object"
},
"name": "tickerbot_get_ticker",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "With `asof` only: how the read was resolved — the interval served and requested, whether rows blend intervals, sources and frozen fields. See the as-of read below.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "List form only — how many of `requested` were found.",
"type": "number"
},
"data": {
"description": "The full ticker row — every signal on the schema page. On the list form, an object keyed by symbol, one full row each.",
"type": "object"
},
"not_found": {
"description": "List form only — the requested symbols we do not track, in request order. An empty array when every symbol was found.",
"type": "string"
},
"requested": {
"description": "List form only — the canonical symbols asked for, de-duplicated, in request order.",
"type": "string"
},
"ticker": {
"description": "The symbol you asked for, normalised. Single form only.",
"type": "string"
}
},
"required": [
"as_of",
"data"
],
"type": "object"
}
},
{
"description": "For one ticker, what we hold and how far back — so an empty result is never ambiguous. Ask this before treating a gap in bars or series as an outage. An absent `minute` span is not a gap — sub-hour bars load on the first request for a symbol.",
"inputSchema": {
"properties": {
"ticker": {
"description": "Case-insensitive. Equities are bare symbols (`AAPL`); every other class carries a prefix — rates (`R:SOFR`), crypto (`X:BTCUSD`), fx (`X:EURUSD`). Bare `BTC`/`ETH` are US-listed ETFs, not spot crypto. See Tickers.",
"type": "string"
}
},
"required": [
"ticker"
],
"type": "object"
},
"name": "tickerbot_get_ticker_coverage",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"measured_fields": {
"description": "Per-field measured depth where the backfill engine has probed — `field`, the grain it was measured at (`daily`, `hourly`, `minute`), `first_date`, `last_date`, `pct_complete`. Capped at 1500 rows.",
"items": {
"type": "string"
},
"type": "array"
},
"minute_tier": {
"description": "Deprecated (see the changelog): an internal precompute roster that has no bearing on what bars you can read — sub-hour bars are available for every active symbol. Do not branch on it.",
"type": "object"
},
"name": {
"description": "Company or instrument name.",
"type": "string"
},
"spans": {
"description": "Per resolution — `oldest`, `newest`, `rows`.",
"type": "object"
},
"ticker": {
"description": "The symbol you asked for.",
"type": "string"
}
},
"required": [
"as_of",
"ticker",
"name",
"minute_tier",
"spans",
"measured_fields"
],
"type": "object"
}
},
{
"description": "Returns the universe doc. Use `top_10`/`top_100` to fetch a system universe; any other slug must be one your account owns.",
"inputSchema": {
"properties": {
"id": {
"description": "Universe slug.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_get_universe",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"description": {
"description": "Free-form notes; `\"\"` when unset.",
"type": "string"
},
"effective_at": {
"description": "System universes only: when this membership took effect.",
"type": "number"
},
"id": {
"description": "The slug — the universe's handle in `?universe=`.",
"type": "string"
},
"name": {
"description": "Display label.",
"type": "string"
},
"next_rebalance_at": {
"description": "System universes only: when membership is next recomputed.",
"type": "number"
},
"rebalance_method": {
"description": "System universes only: how membership is chosen.",
"type": "string"
},
"size": {
"description": "Member count.",
"type": "number"
},
"system": {
"description": "`true` for a built-in universe, `false` for one you created.",
"type": "boolean"
},
"tickers": {
"description": "Members.",
"items": {
"type": "string"
},
"type": "array"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"description",
"tickers",
"size",
"system",
"created_at",
"updated_at"
],
"type": "object"
}
},
{
"description": "The current state of one webhook subscription.",
"inputSchema": {
"properties": {
"id": {
"description": "Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.).",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_get_webhook",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at"
],
"type": "object"
}
},
{
"description": "One timeline across every ticker: earnings, dividends, splits, insider filings, analyst actions, plus opt-in signal firings and news. Requires at least one bound: a ticker scope (ticker/tickers/universe), a time window (from/to), or firm/action — `q` alone is not a bound. `firm`/`action` match case-insensitively; a `q` payload match is case-sensitive. `join: state` attaches the ticker state as of each event.",
"inputSchema": {
"properties": {
"action": {
"description": "Analyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions.",
"enum": [
"upgrades",
"downgrades",
"initiates_coverage_on",
"maintains",
"reiterates",
"assumes",
"reinstates",
"suspends",
"terminates_coverage_on"
],
"type": "string"
},
"cursor": {
"description": "Opaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Aggregate-mode sort direction.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"firm": {
"description": "Analyst-only structured filter — requires `kind=analyst` alone (`400` otherwise). Exact firm-name match on the ratings feed.",
"type": "string"
},
"from": {
"description": "Events at/after this instant — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. A bare `YYYY-MM-DD` means from the start of that day. (`since` accepted as an alias.)",
"type": "string"
},
"group_by": {
"description": "Comma list of rollup keys — switches the response to aggregate rows. Columns (`kind`, `ticker`), payload fields (`firm`, or the explicit `payload->>'firm'`), and expressions over them all roll up. Name a key with `AS` to choose its JSON key: `payload->>'firm' AS firm`. Un-named keys are named for you — a payload read takes its key (`payload->>'firm'` → `firm`), a function keeps the function's name (`lower(ticker)` → `lower`), and anything else falls back to `group_1`, `group_2`.",
"type": "string"
},
"having": {
"description": "Post-aggregation filter. Requires `group_by`.",
"type": "string"
},
"interval": {
"default": "auto",
"description": "Grain the per-event state is read at, when `join=state`: `1m`, `1h`, `1d`, or `auto` (default). Under `auto`, row reads blend each signal at its freshest grain at or before the event across hourly and the last closed daily session, and `group_by` reads use the daily session; events join state once per event, so the minute grain is reached by pinning `1m`. A pinned grain reads that grain only: events on tickers absent from a pinned `1m`/`1h` join to `null`, and a column the grain does not store is a `400`. The names accepted are the daily table's columns at every grain (an hourly- or minute-only signal is unknown here even pinned to `1m`). Reported as `_meta.state_interval` (`auto` with `_meta.state_blended: true`, or the pinned grain).",
"enum": [
"1m",
"1h",
"1d",
"auto"
],
"type": "string"
},
"join": {
"description": "Set to `state` to allow ticker-state signals in `q`/`select`/`group_by`/`having`, evaluated as of each event's timestamp at daily resolution: an event on day D sees the daily state through D-1, never D's close.",
"enum": [
"state"
],
"type": "string"
},
"kind": {
"description": "Comma list of kinds. Omitted → the five corporate kinds; `signal`, `news` and `estimate_revision` join only when named here.",
"enum": [
"dividend",
"split",
"insider",
"analyst",
"earnings",
"signal",
"news",
"estimate_revision"
],
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size (row modes) / max rollup rows (aggregate mode). Max 1000.",
"type": "integer"
},
"order": {
"description": "Aggregate-mode sort — a bare column name or an output name only (put expressions in `select` and sort by their alias). A group key's name works too, whether you aliased it or it was named for you: `group_by=payload->>'firm' AS firm&order=firm`. Default: `events`. (Row mode is always newest-first.)",
"type": "string"
},
"q": {
"description": "SQL WHERE over the projection — `ticker`, `ts`, `kind`, `payload` (plus ticker-state signals when `join=state`). When exactly ONE `kind` is named, that kind's payload fields are additionally first-class typed columns (`amount > 1`, `firm = 'Goldman Sachs'` — see each kind page for its list); multi-kind requests use `payload->>'…'`. Max 4000 chars. ANDs with the filter params.",
"type": "string"
},
"select": {
"description": "Aggregate-mode output columns (requires `group_by`). Default: group keys + `COUNT(*) AS events`. Same naming rule as `group_by` — alias with `AS`, or take the name derived for you.",
"type": "string"
},
"signal": {
"description": "Signal-only filter — requires `kind=signal` alone (`400` otherwise). One built-in boolean signal; REQUIRED with `q` or `join=state` on that kind. See Signal firings.",
"type": "string"
},
"ticker": {
"description": "Single-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins.",
"type": "string"
},
"tickers": {
"description": "Comma list of tickers (max 50). Mutually exclusive with `universe`.",
"type": "string"
},
"to": {
"description": "Window end — same strict ISO subset. A bare `YYYY-MM-DD` means through the end of that day, matching bars/series/spans; a timestamp is exclusive (events strictly before it). (`until` accepted as an alias.)",
"type": "string"
},
"transition": {
"description": "Signal-only filter — requires `kind=signal` alone. `enter` (false→true) or `exit` (true→false).",
"enum": [
"enter",
"exit"
],
"type": "string"
},
"universe": {
"description": "Universe slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`.",
"type": "string"
}
},
"type": "object"
},
"name": "tickerbot_list_events",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "With `join=state` only: `state_interval`, the grain the joined state was read at, and `deprecated_columns` when a state column was named under its pre-2026-09-07 spelling and served under its current name (`requested`, `use`, `note`).",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Carries `q_truncated: true` alongside it when an oversized `q` could not ride the token — resend `q` on later pages.",
"type": "string"
},
"query": {
"description": "Your filters, echoed exactly as you sent them — `q`, `select`, `group_by` and `having` come back in your spelling, not the SQL they compile to — including `join` and its grain when you passed `join=state`.",
"type": "object"
},
"results": {
"description": "One row per event (`ticker`, `ts`, `kind`, `payload`), or rollup rows plus `truncated: true` when an aggregate exceeds `limit`.",
"items": {
"type": "string"
},
"type": "array"
},
"truncated": {
"description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.",
"type": "boolean"
}
},
"required": [
"as_of",
"query",
"count",
"results"
],
"type": "object"
}
},
{
"description": "Every signal you can name in a query — the built-in signals and your own custom signals, in one catalog. Use to discover the signal names and `q` vocabulary before composing a scan; custom signals appear with `kind: custom`.",
"inputSchema": {
"properties": {
"cursor": {
"description": "Opaque cursor from a prior response.",
"type": "string"
},
"kind": {
"description": "Filter by kind (`expression` accepted as a legacy alias for `custom`). Omit to return both.",
"enum": [
"builtin",
"custom"
],
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size for the custom-signal slice. Max 200.",
"type": "integer"
}
},
"type": "object"
},
"name": "tickerbot_list_signals",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page — the canonical count, equal to `count_builtin` + `count_custom`.",
"type": "number"
},
"count_builtin": {
"description": "Built-in signals in the catalog.",
"type": "number"
},
"count_custom": {
"description": "Your custom signals.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"signals": {
"description": "The catalog, your custom signals first, then built-ins. Built-ins carry `kind: builtin` plus their taxonomy membership — `category`/`category_label`/`group`/`group_label` (slugs are stable, switch on those; labels are display strings) — yours carry `kind: custom` with the `expr`.",
"items": {
"type": "string"
},
"type": "array"
},
"taxonomy": {
"description": "Absent when the page holds no built-ins (`kind=custom`). The signal taxonomy tree, once per response: `groups[]` in derivation-ladder order (record → behavior → indicator → company side), each with `slug`, `label`, `derivation`, `description`, and its `categories[]` (`slug`, `label`, `description`). Definitions live here and only here — rows carry pointers, never the descriptions. Omitted on `kind=custom`.",
"type": "object"
}
},
"required": [
"as_of",
"count",
"count_builtin",
"count_custom",
"next_cursor",
"signals"
],
"type": "object"
}
},
{
"description": "Every symbol we track, active or delisted, as one identity row each. Use `/v2/tickers/{ticker}` for the full row.",
"inputSchema": {
"properties": {
"asset_class": {
"description": "Filter by asset class — `stocks`, `rates`, `crypto`, `fx`, or a comma-separated list (the live classes today; validated for shape, not against a fixed list, so a well-formed class we don't track simply matches nothing — same contract as scan). Omit for every class. This is the class of INSTRUMENT, distinct from `asset_type` below (the instrument type within equities). Every row carries its `asset_class`, so a non-equity row identifies itself.",
"type": "string"
},
"asset_type": {
"description": "Filter by instrument type WITHIN equities — the stored `asset_type` value (`CS`, `ETF`, `ADRC`, `PFD`, `FUND`, `UNIT`, `SP`, `ETS`, `WARRANT`, `RIGHT`, `ETN`, `ETV`), matched case-insensitively. `equity` is a convenience value expanding to the equity-like set. This is NOT an asset class: `asset_type=crypto` is rejected — use `asset_class=crypto`.",
"enum": [
"CS",
"ETF",
"ADRC",
"PFD",
"FUND",
"UNIT",
"SP",
"ETS",
"WARRANT",
"RIGHT",
"ETN",
"ETV",
"equity"
],
"type": "string"
},
"cursor": {
"description": "Opaque cursor from the previous response's `next_cursor` field. Continues the walk from after that page. A cursor minted under `search` only resumes the same search.",
"type": "string"
},
"exchange": {
"description": "Filter by exchange name — the value rows carry in their `exchange` field. MIC codes (`XNAS`, `XNYS`, `BATS`) are also accepted and match `exchange_mic`. A malformed value (non-letters, over 16 chars) is a 400.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 1000.",
"type": "integer"
},
"search": {
"description": "Case-insensitive match on `ticker` or `name`, max 64 characters (longer is a 400). Results are ranked: an exact ticker match first, then symbols that start with the term, then name matches — alphabetical within each rank. The cursor carries the rank, so paging a search never repeats or skips.",
"type": "string"
}
},
"type": "object"
},
"name": "tickerbot_list_tickers",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "With `asset_class` only: `scope.asset_class` lists the classes the page was filtered to.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page.",
"type": "string"
},
"results": {
"description": "One identity row per symbol — the thirteen signals named above, nothing else. `active: false` rows carry `delisted_utc`; they are still addressable on the state route with `asof`.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"as_of",
"count",
"next_cursor",
"results"
],
"type": "object"
}
},
{
"description": "Every universe you can reference: your own named ticker lists and the built-in ones. `owner: system` lists the built-in universes (top_10, top_100); `all` lists both. Use a slug as `universe` on scan, signal, and subscribe tools.",
"inputSchema": {
"properties": {
"cursor": {
"description": "Opaque cursor from the previous response.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size (applies to your own). Max 100.",
"type": "integer"
},
"owner": {
"default": "me",
"description": "Which universes to list: `me` (your own), `system` (built-ins), or `all` (both).",
"enum": [
"me",
"system",
"all"
],
"type": "string"
}
},
"type": "object"
},
"name": "tickerbot_list_universes",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"universes": {
"description": "The universes in scope. Every row carries `system: true|false`.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"as_of",
"count",
"next_cursor",
"universes"
],
"type": "object"
}
},
{
"description": "Recent deliveries for one webhook — what was sent, and what came back.",
"inputSchema": {
"properties": {
"cursor": {
"description": "Opaque cursor.",
"type": "string"
},
"from": {
"description": "Only deliveries created at/after this moment — epoch seconds, epoch milliseconds (13+ digits), or an ISO datetime (`since` is accepted as an alias). Delivery history is retained for 90 days; deleting a webhook deletes its delivery history with it.",
"type": "string"
},
"id": {
"description": "Webhook id.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 100.",
"type": "integer"
},
"status": {
"description": "Filter by delivery status.",
"enum": [
"pending",
"delivered",
"permanent_failure"
],
"type": "string"
},
"to": {
"description": "Only deliveries created at/before this moment — same value grammar as `from`. A date-only value means through the end of that UTC day. `from` after `to` is a 400.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_list_webhook_deliveries",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"deliveries": {
"description": "Attempts, newest first: status, attempt, response code, error, and `body_string` — the exact JSON POSTed.",
"items": {
"type": "string"
},
"type": "array"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
}
},
"required": [
"as_of",
"count",
"next_cursor",
"deliveries"
],
"type": "object"
}
},
{
"description": "Every webhook subscription on this account, newest first.",
"inputSchema": {
"properties": {
"cursor": {
"description": "Opaque cursor from the previous response.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 100.",
"type": "integer"
},
"status": {
"description": "Filter by status: `active` or `disabled` — the only two states a webhook has (`disabled` covers both a user pause and the automatic disable after repeated delivery failures; `consecutive_failures`/`last_error` on each record say which). Omit for all.",
"enum": [
"active",
"disabled"
],
"type": "string"
}
},
"type": "object"
},
"name": "tickerbot_list_webhooks",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"webhooks": {
"description": "Your subscriptions, newest first, each with its `subscription_origin` and health fields. `signing_secret` is stripped — it is shown only on create.",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"as_of",
"count",
"next_cursor",
"webhooks"
],
"type": "object"
}
},
{
"description": "Every ticker matching a SQL WHERE clause. Right now, or with `asof`, as of any past date. The `q` grammar is a flat SQL WHERE over signal names: AND/OR/NOT, comparisons, numeric and string literals, custom signals by name. No JOIN or subqueries. With `group_by` the result is rollup rows, not tickers. Example: `gap_up AND market_cap < 2000000000 AND NOT earnings_this_week`.",
"inputSchema": {
"properties": {
"asof": {
"description": "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment; daily-only signals then carry the previous session's close, never that day's) — the same read as it stood then, unlimited depth. Full contract under As of a past date.",
"type": "string"
},
"asset_class": {
"description": "One or more asset classes — slug or comma-separated list (`stocks`, `rates`, `crypto`, `fx`). Validated for shape, not against a fixed list, so a well-formed class we don't track simply matches nothing. Echoed in `query`.",
"type": "string"
},
"columns": {
"description": "Extra signals per row, ADDITIVE — the defaults are always present (ticker, name, asset_class, asset_type, price, change_1d_pct, gap_pct, relative_volume, market_cap). `fields` accepted as an alias.",
"type": "string"
},
"cursor": {
"description": "Opaque cursor from the previous response's `next_cursor`. Row mode only.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"full": {
"default": false,
"description": "Return every signal instead of the default set. Mutually exclusive with `columns` — passing both is a 400.",
"type": "boolean"
},
"group_by": {
"description": "AGGREGATE MODE: 1–6 group keys (signals, expressions, or one of your custom signals as a boolean key). Results become rollup rows. Name a key with `AS` to choose its JSON key (`market_cap > 1e11 AS mega`); an un-named expression is named for you rather than returned as `?column?`. Incompatible with `columns`/`full`/`cursor`; works with `asof`.",
"type": "string"
},
"having": {
"description": "Filter the aggregate rows (requires `group_by`). Custom signals are valid here too.",
"type": "string"
},
"interval": {
"default": "auto",
"description": "Grain the past state is read at: `1m`, `1h`, `1d`, or `auto` (default). `auto` blends: each signal comes from its freshest grain at or before the instant (minute, then hourly, then the last closed daily session), so nothing is refused for grain and no ticker is dropped; `_meta.blended` and `_meta.intervals_present` say what contributed. Pin a grain for the fastest response: one grain is read instead of three, and every ticker is captured on the same clock. A signal the pinned grain does not store is a `400 interval_unavailable` naming the grains that carry it (`rsi_14`, fundamentals and valuation ratios are `1d`-only). Only valid alongside `asof`: a live read with `interval` is a 400.",
"enum": [
"1m",
"1h",
"1d",
"auto"
],
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 100. Aggregate mode does not paginate — it sets `truncated: true` when groups were cut, so sort with `order` to keep the ones you want.",
"type": "integer"
},
"order": {
"default": "change_1d_pct",
"description": "Signal to sort by. In aggregate mode the default is the count alias `tickers` — or, with a custom `select`, the last item's alias — sorted NULLS LAST with the group keys as tiebreak.",
"type": "string"
},
"q": {
"description": "SQL WHERE expression. Max 4000 chars; semicolons, comments and write keywords are rejected. Your custom signals are valid here — each expands to its SQL at run time.",
"type": "string"
},
"select": {
"description": "Aggregate output items (requires `group_by`). Default: the group keys + `COUNT(*) AS tickers`. Supports count/avg/sum/min/max/stddev/string_agg/bool_and/bool_or plus `FILTER (WHERE …)`, and your custom signals inside expressions. Alias with `AS`; a last item without one is a 400.",
"type": "string"
},
"universe": {
"description": "Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~23,212 tracked tickers.",
"type": "string"
}
},
"required": [
"q"
],
"type": "object"
},
"name": "tickerbot_scan",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "`null_coverage` reports, per signal in the predicate, how many in-scope rows are NULL and therefore never evaluated — absence from `results` means \"no value\", not \"did not match\". `scope` additionally describes an explicit `universe`. `deprecated_columns` lists any column you named under its pre-2026-09-07 spelling that was served under its current name (`requested`, `use`, `note`).",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"query": {
"description": "Your query, echoed — `q`, `order`, `dir`, `limit`, and any scope.",
"type": "object"
},
"results": {
"description": "One row per match — every signal on the schema page, plus any you named.",
"items": {
"type": "string"
},
"type": "array"
},
"truncated": {
"description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.",
"type": "boolean"
}
},
"required": [
"as_of",
"query",
"count",
"results"
],
"type": "object"
}
},
{
"description": "SQL query over the news archive. Article rows, or rollups when you group them. Filter to a ticker with the `ticker` param, or in `q` via the auto-unnest alias `tk = 'NVDA'`. `search` is full-text over title and summary. Rollups with group_by/having return `truncated: true` instead of paging.",
"inputSchema": {
"properties": {
"cursor": {
"description": "Opaque pagination cursor from a prior response's `next_cursor`.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"from": {
"description": "Earliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. (`since` accepted as an alias.)",
"type": "string"
},
"group_by": {
"description": "AGGREGATE MODE: comma-separated group keys, 1-6 (max 1000 chars). Switches the response to rollup rows. Use `tk` to roll up per ticker without writing the UNNEST. Name a key with `AS` to choose its JSON key; an un-named expression is named for you rather than returned as `?column?`.",
"type": "string"
},
"having": {
"description": "HAVING clause on the aggregate (max 1000 chars). Requires `group_by`.",
"type": "string"
},
"limit": {
"default": 50,
"description": "Page size. Max 1000.",
"type": "integer"
},
"order": {
"description": "Sort — a bare column name or SELECT alias only (put expressions in `select` and order by their alias). Defaults to `time_published` (article rows) or `volume` (aggregate rows).",
"type": "string"
},
"q": {
"description": "WHERE clause over the news_article table. Max 4000 chars. Required UNLESS `search` or a scoping param (`ticker`/`tickers`/`universe`/`from`/`to`) is present — the simplest call needs no SQL. Queryable columns: `time_published`, `title`, `summary`, `source`, `source_domain`, `category`, `authors`, `topics`, `overall_sentiment_score`, `overall_sentiment_label`, `tickers`, `ticker_data`, `banner_image`, `url`, `id`, `created_at` — plus `tk`, the per-ticker UNNEST alias. Signal/state columns are not joinable here.",
"type": "string"
},
"search": {
"description": "Full-text search over `title` + `summary` — websearch grammar: `apple earnings` (all words), `\"price target\"` (phrase), `chips OR semiconductors`, `-crypto` (negation). Max 200 chars. ANDs with `q` and the scoping params. Language-stemmed English.",
"type": "string"
},
"select": {
"description": "Columns/expressions to return (max 2000 chars). Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`).",
"type": "string"
},
"ticker": {
"description": "Articles mentioning this symbol (ANDed with `q`).",
"type": "string"
},
"tickers": {
"description": "Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.",
"type": "string"
},
"to": {
"description": "Articles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.)",
"type": "string"
},
"universe": {
"description": "Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.",
"type": "string"
}
},
"type": "object"
},
"name": "tickerbot_search_news",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"count": {
"description": "Rows in this page.",
"type": "number"
},
"next_cursor": {
"description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.",
"type": "string"
},
"query": {
"description": "Your filters, echoed.",
"type": "object"
},
"results": {
"description": "Article rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list.",
"items": {
"type": "string"
},
"type": "array"
},
"truncated": {
"description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap.",
"type": "boolean"
}
},
"required": [
"as_of",
"query",
"count",
"next_cursor",
"results"
],
"type": "object"
}
},
{
"description": "Push new events: we POST your endpoint when events of the kinds you chose land in the archives. Webhooks need a paid plan (Free has no webhook slots). `q` filters the ticker STATE; `event_q` filters the EVENT payload in the /v2/events grammar. Latency is the ingest cadence (analyst ≤1h, corporate kinds daily), not sub-minute.",
"inputSchema": {
"properties": {
"channel": {
"description": "Delivery channel. `slack` is reserved and returns `501`.",
"enum": [
"webhook",
"discord",
"in_app",
"mobile_push"
],
"type": "string"
},
"device_id": {
"description": "Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.",
"type": "string"
},
"discord_url": {
"description": "Discord incoming-webhook URL. Required when `channel` is `discord`. Stored as a posting credential: the create response echoes it back under `channel_config`, but every later read (list, get, deliveries) strips it and sets `channel_config_present: true` instead.",
"type": "string"
},
"event_q": {
"description": "Optional event-CONTENT filter in the `/v2/events` grammar — only `ticker`, `ts`, `kind`, `payload` may appear. Composes with `q`.",
"type": "string"
},
"kinds": {
"description": "Event kinds to fire on — array or comma list.",
"enum": [
"dividend",
"split",
"insider",
"analyst",
"earnings"
],
"type": "string"
},
"name": {
"description": "Display name. Defaults to `events: <kinds> · <scope>`.",
"type": "string"
},
"q": {
"description": "Optional row-STATE filter evaluated against the event's ticker at fire time. Same grammar as scan `q`; custom signals are expanded and frozen at creation.",
"type": "string"
},
"target_url": {
"description": "HTTPS delivery URL; or use `channel` + `discord_url`/`device_id`. Omit for in-app.",
"type": "string"
},
"ticker": {
"description": "Single-symbol shorthand for `tickers`.",
"type": "string"
},
"tickers": {
"description": "Scope to specific tickers (max 50). Mutually exclusive with `universe` — and with the singular alias `ticker` (sending both is a 400). Omit both for all tickers.",
"type": "string"
},
"universe": {
"description": "Scope to a universe slug (`top_10`, `top_100`, or one of yours). `universe_id` accepted as an alias.",
"type": "string"
}
},
"required": [
"kinds"
],
"type": "object"
},
"name": "tickerbot_subscribe_events",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Returned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"channel_config": {
"description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).",
"type": "object"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"signing_secret": {
"description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"test_url": {
"description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at",
"channel_config",
"signing_secret"
],
"type": "object"
}
},
{
"description": "Push a whole query: we POST your endpoint every time the match set changes. Webhooks need a paid plan (Free has no webhook slots). Use for \"alert me when this happens\" requests.",
"inputSchema": {
"properties": {
"cadence": {
"description": "`realtime` (the default) is evaluated on every data refresh (~1×/min); `hourly` and `nyse_open` throttle to a batch schedule. `1m` is a deprecated alias for `realtime`.",
"enum": [
"realtime",
"hourly",
"nyse_open"
],
"type": "string"
},
"channel": {
"description": "Delivery channel. `webhook` (POST to `target_url`), `discord` (embed to `discord_url`), `in_app` (dashboard only), or `mobile_push` (requires a `device_id` from `POST /v2/devices/register`). Inferred when omitted: `webhook` if `target_url` is set, `discord` if `discord_url` is set, else `in_app`. `slack` is reserved and returns `501`.",
"enum": [
"webhook",
"discord",
"in_app",
"mobile_push"
],
"type": "string"
},
"columns": {
"description": "Extra signals per fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; unknown ones are rejected at creation. `fields` accepted as an alias — and the RESPONSE reports them under `fields`, as an array.",
"type": "string"
},
"device_id": {
"description": "Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction for `order`.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"discord_url": {
"description": "Discord incoming-webhook URL. Required when `channel` is `discord`. Stored as a posting credential: the create response echoes it back under `channel_config`, but every later read (list, get, deliveries) strips it and sets `channel_config_present: true` instead.",
"type": "string"
},
"name": {
"description": "Human-readable label (up to 80 chars). Defaults to `scan: <q>`.",
"type": "string"
},
"order": {
"default": "market_cap",
"description": "Signal the payload's match lists are sorted by before the 100-row cap applies, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real signal (validated at creation).",
"type": "string"
},
"q": {
"description": "WHERE-clause expression using signal names — the same grammar and the same 4000-char cap as `POST /v2/scan`, so anything scannable is subscribable. Custom signals are expanded and frozen in at creation.",
"type": "string"
},
"target_url": {
"description": "https:// URL to POST when the match set changes. Omit for in-app delivery.",
"type": "string"
},
"universe": {
"description": "System or user-owned universe to scope the scan. `universe_id` accepted as an alias. Unknown universes are a 404 `universe_not_found`.",
"type": "string"
}
},
"required": [
"q"
],
"type": "object"
},
"name": "tickerbot_subscribe_scan",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Returned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"channel_config": {
"description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).",
"type": "object"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"signing_secret": {
"description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"test_url": {
"description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at",
"channel_config",
"signing_secret"
],
"type": "object"
}
},
{
"description": "Push one signal: we POST your endpoint whenever any ticker starts matching it. Webhooks need a paid plan (Free has no webhook slots). Omit `ticker` to watch the whole universe.",
"inputSchema": {
"properties": {
"cadence": {
"description": "`realtime` (the default) is evaluated on every data refresh (~1×/min); `hourly` and `nyse_open` throttle to a batch schedule. `1m` is a deprecated alias for `realtime`.",
"enum": [
"realtime",
"hourly",
"nyse_open"
],
"type": "string"
},
"channel": {
"description": "Delivery channel. `webhook` (POST to `target_url`), `discord` (post an embed to `discord_url`), `in_app` (dashboard only), or `mobile_push` (notify a phone signed in to the Tickerbot mobile app; requires a `device_id` from `POST /v2/devices/register`). Inferred when omitted: `webhook` if `target_url` is set, `discord` if `discord_url` is set, else `in_app`. `slack` is reserved and returns `501`. See the Delivery channels guide.",
"enum": [
"webhook",
"discord",
"in_app",
"mobile_push"
],
"type": "string"
},
"columns": {
"description": "Comma-separated extra signals to include in each fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; an unknown signal is rejected at creation. `fields` accepted as an alias — and note the RESPONSE reports them under `fields`, as an array.",
"type": "string"
},
"condition": {
"description": "Required for every non-boolean signal; the shape follows the signal's `type`. Numeric: `\">70\"`, `\"<30\"`, `\">=100\"`. Timestamp: `\"<YYYY-MM-DDTHH:MM:SSZ\"` (or a bare date). Date: `\">=YYYY-MM-DD\"`. String: `\"=ETF\"` or `\"!=ETF\"`. Sending one with a boolean or custom signal returns 400 (it does not apply).",
"type": "string"
},
"device_id": {
"description": "Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction for `order`.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"discord_url": {
"description": "Discord incoming-webhook URL (`https://discord.com/api/webhooks/…`). Required when `channel` is `discord`. Stored as a posting credential: the create response echoes it back under `channel_config`, but every later read (list, get, deliveries) strips it and sets `channel_config_present: true` instead.",
"type": "string"
},
"name": {
"description": "Human-readable label (up to 80 chars). Defaults to the predicate — `at_52w_high` for a boolean, `rsi_14 > 70` for a numeric, prefixed with `<TICKER>: ` when `ticker` scopes it.",
"type": "string"
},
"order": {
"default": "market_cap",
"description": "Signal 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 rather than an arbitrary sample. Must be a real signal (validated at creation).",
"type": "string"
},
"signal": {
"description": "Signal name from the schema (case-insensitive).",
"type": "string"
},
"target_url": {
"description": "https:// URL to POST when fired. Omit for in-app delivery.",
"type": "string"
},
"ticker": {
"description": "Restrict to a single ticker. Default: any ticker.",
"type": "string"
},
"universe": {
"description": "Restrict to a system or user-owned universe (e.g. `top_100`). `universe_id` accepted as an alias. Unknown universes are a 404 `universe_not_found`.",
"type": "string"
}
},
"required": [
"signal"
],
"type": "object"
},
"name": "tickerbot_subscribe_signal",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Returned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"channel_config": {
"description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).",
"type": "object"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"signing_secret": {
"description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"test_url": {
"description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at",
"channel_config",
"signing_secret"
],
"type": "object"
}
},
{
"description": "Push one ticker: we POST your endpoint whenever it matches the condition you give. Webhooks need a paid plan (Free has no webhook slots). Omit `target_url` for in-app delivery.",
"inputSchema": {
"properties": {
"cadence": {
"description": "How often to evaluate. `realtime` (the default) is evaluated on every data refresh (~1×/min); `hourly` and `nyse_open` throttle to a batch schedule. `1m` is a deprecated alias for `realtime`.",
"enum": [
"realtime",
"hourly",
"nyse_open"
],
"type": "string"
},
"channel": {
"description": "Delivery channel. `webhook` (POST to `target_url`), `discord` (post an embed to `discord_url`), `in_app` (dashboard only), or `mobile_push` (notify a phone signed in to the Tickerbot mobile app; requires a `device_id` from `POST /v2/devices/register`). Inferred when omitted: `webhook` if `target_url` is set, `discord` if `discord_url` is set, else `in_app`. `slack` is reserved and returns `501`. See the Delivery channels guide.",
"enum": [
"webhook",
"discord",
"in_app",
"mobile_push"
],
"type": "string"
},
"columns": {
"description": "Comma-separated extra signals to include in each fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; an unknown signal is rejected at creation. `fields` accepted as an alias — and note the RESPONSE reports them under `fields`, as an array.",
"type": "string"
},
"condition": {
"description": "Original name for `q` — accepted as well. The same WHERE-clause fragment; send either spelling.",
"type": "string"
},
"device_id": {
"description": "Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.",
"type": "string"
},
"dir": {
"default": "desc",
"description": "Sort direction for `order`.",
"enum": [
"asc",
"desc"
],
"type": "string"
},
"discord_url": {
"description": "Discord incoming-webhook URL (`https://discord.com/api/webhooks/…`). Required when `channel` is `discord`. Stored as a posting credential: the create response echoes it back under `channel_config`, but every later read (list, get, deliveries) strips it and sets `channel_config_present: true` instead.",
"type": "string"
},
"name": {
"description": "Human-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`.",
"type": "string"
},
"order": {
"default": "market_cap",
"description": "Signal 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 rather than an arbitrary sample. Must be a real signal (validated at creation).",
"type": "string"
},
"q": {
"description": "WHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.)",
"type": "string"
},
"target_url": {
"description": "https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard).",
"type": "string"
},
"ticker": {
"description": "Case-insensitive. Equities are bare symbols (`AAPL`); every other class carries a prefix — rates (`R:SOFR`), crypto (`X:BTCUSD`), fx (`X:EURUSD`). Bare `BTC`/`ETH` are US-listed ETFs, not spot crypto. See Tickers.",
"type": "string"
}
},
"required": [
"ticker",
"q"
],
"type": "object"
},
"name": "tickerbot_subscribe_ticker",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Returned on create only, and only when the rule or `columns` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored rule carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"channel_config": {
"description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).",
"type": "object"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"signing_secret": {
"description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"test_url": {
"description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at",
"channel_config",
"signing_secret"
],
"type": "object"
}
},
{
"description": "Send a real-shape test POST to your endpoint, instantly. One-shot: a failed test never retries and never auto-disables the webhook. 400 when the webhook has no target_url.",
"inputSchema": {
"properties": {
"id": {
"description": "Webhook id.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_test_webhook",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"created_at": {
"description": "When the ping was sent (ISO 8601).",
"type": "string"
},
"delivered": {
"description": "Whether your endpoint accepted the ping (2xx within the timeout).",
"type": "boolean"
},
"elapsed_ms": {
"description": "Round-trip time of the ping.",
"type": "number"
},
"error": {
"description": "Why delivery failed, in words; `null` on success.",
"type": "string"
},
"http_status": {
"description": "The status your endpoint returned; `null` when it could not be reached.",
"type": "number"
},
"id": {
"description": "Delivery id of the test — `dl_…`, the same shape as a real delivery on the deliveries route.",
"type": "string"
},
"status": {
"description": "The delivery record's status: `delivered` or `permanent_failure` (a test is never retried).",
"type": "string"
},
"test": {
"description": "`true` — this delivery was a synthetic ping, not a trigger firing.",
"type": "boolean"
},
"webhook_id": {
"description": "The webhook that was tested.",
"type": "string"
}
},
"required": [
"as_of",
"id",
"webhook_id",
"test",
"delivered",
"http_status",
"elapsed_ms",
"error",
"status",
"created_at"
],
"type": "object"
}
},
{
"description": "Update a custom signal you own — its expression, description, or name.",
"inputSchema": {
"properties": {
"description": {
"description": "New description. Not derived from `expr` — change both if the prose describes a threshold you are moving.",
"type": "string"
},
"expr": {
"description": "New SQL expression. Re-validated and re-inlined against your other custom signals. Same strict grammar as create — no `LIKE`/`ILIKE`, `CASE`, `::` casts, or functions beyond `abs`/`coalesce`/`round`/`least`/`greatest`. The response echoes your expression as stored, not its expansion (a pre-2026-09-07 column spelling is stored under its current name and reported in `_meta.deprecated_columns`).",
"type": "string"
},
"new_name": {
"description": "New slug — renames the signal and changes its API handle everywhere (same validation as create). Refused while other custom signals reference the current name. `name` is accepted as an alias (new_name wins when both are sent), but new_name is the unambiguous spelling since the URL already carries the current name.",
"type": "string"
},
"signal": {
"description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.",
"type": "string"
}
},
"required": [
"signal"
],
"type": "object"
},
"name": "tickerbot_update_custom_signal",
"outputSchema": {
"additionalProperties": false,
"properties": {
"_meta": {
"description": "Only when `expr` named a column under its pre-2026-09-07 spelling: `deprecated_columns` lists each one (`requested`, `use`, `note`). The stored `expr` carries the current name.",
"type": "object"
},
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"signal": {
"description": "The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.",
"type": "object"
}
},
"required": [
"as_of",
"signal"
],
"type": "object"
}
},
{
"description": "Update one of your universes: its name, description, or members. `tickers` replaces the whole list; `add`/`remove` adjust it. System universes cannot be edited.",
"inputSchema": {
"properties": {
"add": {
"description": "Add these tickers (deduplicated).",
"items": {
"type": "string"
},
"type": "array"
},
"description": {
"description": "New notes. Max 500 characters.",
"type": "string"
},
"id": {
"description": "Universe slug.",
"type": "string"
},
"name": {
"description": "New label. Non-empty, max 80 characters.",
"type": "string"
},
"remove": {
"description": "Remove these tickers.",
"items": {
"type": "string"
},
"type": "array"
},
"tickers": {
"description": "Replace the full ticker list (up to 10,000; validated against the active universe). Does not combine with `add`/`remove` (400).",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_update_universe",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"description": {
"description": "Free-form notes; `\"\"` when unset.",
"type": "string"
},
"effective_at": {
"description": "System universes only; absent on yours.",
"type": "number"
},
"id": {
"description": "The slug — the universe's handle in `?universe=`.",
"type": "string"
},
"name": {
"description": "Display label.",
"type": "string"
},
"next_rebalance_at": {
"description": "System universes only; absent on yours.",
"type": "number"
},
"rebalance_method": {
"description": "System universes only; absent on yours.",
"type": "string"
},
"size": {
"description": "Member count.",
"type": "number"
},
"system": {
"description": "`false` — this is your universe.",
"type": "boolean"
},
"tickers": {
"description": "Members, after this call.",
"items": {
"type": "string"
},
"type": "array"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"description",
"tickers",
"size",
"system",
"created_at",
"updated_at"
],
"type": "object"
}
},
{
"description": "Edit a webhook in place — send only the fields you want to change. The trigger and channel are immutable — delete and re-create to change what fires or where it delivers. Unknown fields are a 400.",
"inputSchema": {
"properties": {
"cadence": {
"description": "Evaluation cadence. A user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).",
"enum": [
"realtime",
"hourly",
"nyse_open"
],
"type": "string"
},
"enabled": {
"description": "`false` disables the webhook (status → `disabled`). `true` is a no-op unless disabled, in which case use `POST /v2/webhooks/{id}/enable` instead.",
"type": "boolean"
},
"id": {
"description": "Webhook id.",
"type": "string"
},
"name": {
"description": "New display name. Non-empty, max 80 characters.",
"type": "string"
},
"target_url": {
"description": "New https:// delivery URL (webhook channel only — a Discord/mobile subscription 400s here). `null` or empty switches to in-app delivery; `status` is untouched — a disabled webhook stays disabled until `POST /v2/webhooks/{id}/enable` (the only path that re-checks your account's webhook cap).",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"name": "tickerbot_update_webhook",
"outputSchema": {
"additionalProperties": false,
"properties": {
"as_of": {
"description": "Server time this response was assembled (ISO 8601).",
"type": "string"
},
"cadence": {
"description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.",
"type": "string"
},
"channel": {
"description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.",
"type": "string"
},
"created_at": {
"description": "Creation timestamp.",
"type": "number"
},
"delivery": {
"description": "Legacy alias of `channel`, kept aligned for older readers.",
"type": "string"
},
"dir": {
"description": "Sort direction for that list; `null` means the default (`desc`).",
"type": "string"
},
"event_kinds": {
"description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).",
"items": {
"type": "string"
},
"type": "array"
},
"event_q": {
"description": "Event-trigger webhooks only: the payload filter, or `null`.",
"type": "string"
},
"event_tickers": {
"description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.",
"items": {
"type": "string"
},
"type": "array"
},
"fields": {
"description": "Extra signals carried on each fired match row; `null` means the standard set.",
"type": "string"
},
"id": {
"description": "The webhook id — `wh_…`, the handle for every other call on this record.",
"type": "string"
},
"last_error": {
"description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".",
"type": "string"
},
"last_eval_error_at": {
"description": "When the last evaluation error happened; `null` on a healthy hook.",
"type": "number"
},
"last_evaluated_at": {
"description": "When it was last evaluated; `null` until the first run.",
"type": "number"
},
"last_fired": {
"description": "When a delivery last went out; `null` if it never has.",
"type": "number"
},
"last_match_set": {
"description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"last_predicate_value": {
"description": "The trigger's value at the last evaluation; `null` until it has run.",
"type": "string"
},
"name": {
"description": "Your label for the subscription.",
"type": "string"
},
"next_eval_at": {
"description": "When the evaluator will next consider this subscription.",
"type": "number"
},
"order": {
"description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).",
"type": "string"
},
"q": {
"description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.",
"type": "string"
},
"rule_id": {
"description": "Legacy link to a v1 alert rule; `null` on everything created through v2.",
"type": "string"
},
"source": {
"description": "Which API version created the record; `v2` for anything you create today.",
"type": "string"
},
"status": {
"description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.",
"type": "string"
},
"subscription_origin": {
"description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.",
"type": "object"
},
"target_url": {
"description": "Your HTTPS endpoint; `null` on every channel except `webhook`.",
"type": "string"
},
"trigger_kind": {
"description": "Event-trigger webhooks only: `event`.",
"type": "string"
},
"universe_id": {
"description": "Universe the trigger is scoped to, or `null` for the whole market.",
"type": "string"
},
"updated_at": {
"description": "Last modification timestamp.",
"type": "number"
}
},
"required": [
"as_of",
"id",
"name",
"q",
"rule_id",
"fields",
"order",
"dir",
"universe_id",
"cadence",
"channel",
"target_url",
"delivery",
"status",
"source",
"subscription_origin",
"last_predicate_value",
"created_at",
"updated_at",
"last_fired",
"last_match_set",
"next_eval_at",
"last_evaluated_at"
],
"type": "object"
}
}
]
}Verify it yourself
curl -s https://api.teppi.xyz/v1/evidence/sha256:f62638754741c8da9c5ee84237afa96ecb504df782950779fa14539aa00fc555 | sha256sum