MCP 서버

mcp-server

io.github.tickerbot/mcp-server
데이터 및 분석 금융 및 투자 공개 · 연결 가능 MCP 2026-07-28

이 MCP로 할 수 있는 일

Provides SQL-based market scans, historical price and indicator data, news and event timelines, portfolio-related signals, and webhook alerts across stocks and cryptocurrencies.

tickerbot_create_custom_signal
A named boolean predicate you can reference anywhere a built-in signal goes.
입력 스키마
{'type': 'object', 'required': ['name', 'expr'], 'properties': {'expr': {'type': 'string', '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`.'}, 'name': {'type': 'string', '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."}, 'description': {'type': 'string', 'description': 'Free-form notes. Max 500 chars. Absent or empty comes back as `""` rather than null.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'signal'], 'properties': {'_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'signal': {'type': 'object', 'description': 'The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.'}}, 'additionalProperties': False}
tickerbot_create_universe
Create a named ticker list owned by your account.
입력 스키마
{'type': 'object', 'required': ['name', 'tickers'], 'properties': {'id': {'type': 'string', '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."}, 'name': {'type': 'string', 'description': 'Human-readable label, up to 80 characters. Display-only — never used to reference the universe.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Ticker symbols, up to 10,000. Validated against the active universe. `[]` is accepted — a shell universe you can fill later via PATCH.'}, 'description': {'type': 'string', 'description': 'Free-form notes, up to 500 characters. Stored as `""` when omitted.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'id', 'name', 'description', 'tickers', 'size', 'system', 'created_at', 'updated_at'], 'properties': {'id': {'type': 'string', 'description': "The slug — the universe's handle in `?universe=`."}, 'name': {'type': 'string', 'description': 'Display label.'}, 'size': {'type': 'number', 'description': 'Member count.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'system': {'type': 'boolean', 'description': '`false` — this is your universe.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Members, after this call.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'description': {'type': 'string', 'description': 'Free-form notes; `""` when unset.'}, 'effective_at': {'type': 'number', 'description': 'System universes only; absent on yours.'}, 'rebalance_method': {'type': 'string', 'description': 'System universes only; absent on yours.'}, 'next_rebalance_at': {'type': 'number', 'description': 'System universes only; absent on yours.'}}, 'additionalProperties': False}
tickerbot_create_webhook
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).
입력 스키마
{'type': 'object', 'required': ['trigger'], 'properties': {'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction for `order`. Not accepted on `event` triggers (400).'}, 'name': {'type': 'string', 'description': 'Display name, max 80 characters. Defaults to an auto-generated one from the trigger.'}, 'order': {'type': 'string', '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)."}, 'cadence': {'enum': ['realtime', 'hourly', 'nyse_open'], 'type': 'string', 'default': 'realtime', 'description': 'Evaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).'}, 'channel': {'enum': ['webhook', 'discord', 'in_app', 'mobile_push'], 'type': 'string', 'description': 'Delivery channel. See Delivery channels.'}, 'columns': {'type': 'string', '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."}, 'trigger': {'type': 'object', 'required': ['type'], 'properties': {'q': {'type': 'string', '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': {'enum': ['scan', 'ticker', 'signal', 'event'], 'type': 'string', 'description': 'Which trigger shape the rest of the object uses.'}, 'kinds': {'type': 'string', 'description': 'event: required — event kinds to fire on, array or comma list (e.g. `split,analyst`).'}, 'signal': {'type': 'string', '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).'}, 'ticker': {'type': 'string', 'description': 'ticker: required — the symbol to watch (e.g. `NVDA`). signal: optional — restrict the signal to one symbol (omit to watch the whole universe).'}, 'event_q': {'type': 'string', '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`."}, 'tickers': {'type': 'string', 'description': 'event: optional symbol list, max 50 (e.g. `AAPL,NVDA`). Mutually exclusive with `trigger.universe`.'}, 'universe': {'type': 'string', '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.'}, 'condition': {'type': 'string', '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`.'}}, '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`.'}, 'device_id': {'type': 'string', 'description': 'Registered device id (channel `mobile_push`, see /v2/devices).'}, 'target_url': {'type': 'string', '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.'}, 'discord_url': {'type': 'string', 'description': 'Discord webhook URL (channel `discord`).'}}}
출력 스키마
{'type': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'test_url': {'type': 'string', 'description': 'Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'channel_config': {'type': 'object', 'description': 'Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'signing_secret': {'type': 'string', 'description': 'Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_delete_custom_signal
Delete one of your custom signals. Refused by default if another of your signals references it.
입력 스키마
{'type': 'object', 'required': ['signal'], 'properties': {'force': {'type': 'boolean', 'default': False, 'description': 'When `true`, skip the reference check and delete. References will break on next recompile.'}, 'signal': {'type': 'string', 'description': 'Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.'}}}
tickerbot_delete_universe
Permanently delete one of your universes. Webhooks that reference the deleted universe fail on their next eval — clean those up first.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Universe slug.'}, 'force': {'type': 'boolean', '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.'}}}
tickerbot_delete_webhook
Delete a webhook subscription, and its delivery history with it.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id.'}}}
tickerbot_enable_webhook
Re-enable a disabled webhook and start it clean. Clears match-state, so the next eval treats every currently-matching ticker as new.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id.'}}}
출력 스키마
{'type': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_get_bars
OHLCV bars from 1-second through monthly. The prices underneath the table.
입력 스키마
{'type': 'object', 'required': ['ticker', 'interval'], 'properties': {'to': {'type': 'string', '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).'}, 'asof': {'type': 'string', '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. Returns one bar unless you also pass `limit`, which gives the last `limit` closed bars. Mutually exclusive with `before`/`cursor` (400). Unlimited depth."}, 'from': {'type': 'string', '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).'}, 'limit': {'type': 'integer', 'default': 100, 'description': 'Most-recent N bars. Max 1000 — an over-cap value is clamped, not an error.'}, 'before': {'type': 'string', '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).'}, 'cursor': {'type': 'string', '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."}, 'ticker': {'type': 'string', 'description': 'Ticker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol.'}, 'session': {'enum': ['all', 'regular'], 'type': 'string', '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.'}, 'adjusted': {'type': 'boolean', '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.'}, 'interval': {'enum': ['1s', '1m', '5m', '15m', '30m', '1h', '2h', '4h', '1d', '1w', '1mo'], 'type': 'string', '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."}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'interval', 'adjusted', 'session', 'count', 'coverage', 'bars'], 'properties': {'bars': {'type': 'array', 'items': {'type': 'string'}, 'description': 'OHLCV bars, chronological, in the compact array shape. Bulk requests key this by symbol instead.'}, 'note': {'type': 'string', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Bars returned in single-symbol mode; the number of SYMBOLS in bulk mode.'}, 'notes': {'type': 'object', 'description': 'Bulk (comma-list) requests only: the per-symbol disclosures, keyed by symbol, in place of `note`.'}, 'ticker': {'type': 'string', 'description': 'The symbol you asked for.'}, 'session': {'type': 'string', 'description': 'The session filter applied: `all` (default) or `regular` (09:30–16:00 ET, sub-hour intervals only).'}, 'adjusted': {'type': 'boolean', 'description': 'Whether the bars are split-adjusted — `true` unless you passed `adjusted=false`.'}, 'coverage': {'type': 'string', '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`.'}, 'interval': {'type': 'string', 'description': 'The bar size served.'}, 'next_cursor': {'type': 'string', '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.'}}, 'additionalProperties': False}
tickerbot_get_etf_holdings
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.
입력 스키마
{'type': 'object', 'required': ['ticker'], 'properties': {'limit': {'type': 'integer', '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."}, 'ticker': {'type': 'string', 'description': 'ETF symbol. Case-insensitive.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'ticker', 'is_etf', 'count', 'holdings'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Holdings in this page.'}, 'total': {'type': 'number', 'description': 'Total constituents held, before `limit`.'}, 'is_etf': {'type': 'boolean', 'description': 'Whether the symbol is an ETF, from the instrument type on its ticker record.'}, 'ticker': {'type': 'string', 'description': 'The ETF you asked for.'}, 'holdings': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Constituents, heaviest first, each with its weight.'}, 'truncated': {'type': 'boolean', 'description': '`true` when `limit` cut the list short.'}}, 'additionalProperties': False}
tickerbot_get_etf_sectors
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.
입력 스키마
{'type': 'object', 'required': ['ticker'], 'properties': {'ticker': {'type': 'string', 'description': 'ETF symbol. Case-insensitive.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'ticker', 'is_etf', 'count', 'sectors'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Sectors returned.'}, 'is_etf': {'type': 'boolean', 'description': 'Whether the symbol is an ETF, from the instrument type on its ticker record.'}, 'ticker': {'type': 'string', 'description': 'The ETF you asked for.'}, 'sectors': {'type': 'array', 'items': {'type': 'string'}, 'description': "Sector weights, heaviest first, in the vendor's ETF-profile vocabulary."}}, 'additionalProperties': False}
tickerbot_get_series
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.
입력 스키마
{'type': 'object', 'properties': {'to': {'type': 'string', 'description': 'Latest timestamp (inclusive), `YYYY-MM-DD` or ISO.'}, 'asof': {'type': 'string', '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. 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."}, 'from': {'type': 'string', '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.'}, 'limit': {'type': 'integer', '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.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates.'}, 'ticker': {'type': 'string', 'description': 'Single-symbol form — `/v2/series?ticker=AAPL` is ticker history in its canonical spelling. Exactly one of `ticker` or `tickers` is required.'}, 'columns': {'type': 'string', '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.'}, 'tickers': {'type': 'string', '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.'}, 'interval': {'enum': ['1m', '1h', '1d', '1w', '1q'], 'type': 'string', 'default': '1d', 'description': 'Grid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid.'}, 'transitions_only': {'type': 'boolean', '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': 'object', 'required': ['as_of', 'interval', 'tickers', 'columns', 'count', 'next_cursor', '_meta', 'series'], 'properties': {'_meta': {'type': 'object', '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`).'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows per ticker in this page.'}, 'series': {'type': 'object', 'description': 'Keyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for.'}, 'columns': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Columns in the response, echoed.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Symbols in the response, echoed.'}, 'interval': {'type': 'string', 'description': 'The grid granularity served.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_get_signal
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.
입력 스키마
{'type': 'object', 'required': ['signal'], 'properties': {'asof': {'type': 'string', 'description': "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment) — the same read as it stood then, unlimited depth. Full contract under As of a past date."}, 'limit': {'type': 'integer', 'default': 50, 'description': 'Page size. Max 200.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous response.'}, 'signal': {'type': 'string', '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`.'}, 'sort_by': {'enum': ['default', 'market_cap'], 'type': 'string', '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)."}, 'interval': {'enum': ['1m', '1h', '1d', 'auto'], 'type': 'string', 'default': 'auto', 'description': 'Grain the past state is reconstructed at: `1m`, `1h`, `1d`, or `auto` (default). Only valid alongside `asof` — a live read with `interval` is a 400. Details under As of a past date.'}, 'universe': {'type': 'string', 'description': 'Optional. Scope to a system or caller-owned universe slug.'}, 'condition': {'type': 'string', '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).'}, 'include_active_since': {'type': 'boolean', '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': 'object', 'required': ['as_of', 'signal', 'condition', 'universe', 'count', 'next_cursor', 'results'], 'properties': {'_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'signal': {'type': 'string', 'description': 'The signal you asked for.'}, 'results': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Matching tickers with the signal value.'}, 'universe': {'type': 'string', 'description': 'The universe you scoped to, echoed; `null` when unscoped.'}, 'condition': {'type': 'string', 'description': 'The bound you passed, echoed; `null` for boolean and custom signals.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_get_ticker
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.
입력 스키마
{'type': 'object', 'required': ['ticker'], 'properties': {'asof': {'type': 'string', 'description': "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment) — the same read as it stood then, unlimited depth. Full contract under As of a past date."}, 'ticker': {'type': 'string', '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.'}, 'interval': {'enum': ['1m', '1h', '1d', 'auto'], 'type': 'string', 'default': 'auto', 'description': 'Grain the past state is reconstructed at: `1m`, `1h`, `1d`, or `auto` (default). Only valid alongside `asof` — a live read with `interval` is a 400. Details under As of a past date.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'data'], 'properties': {'data': {'type': 'object', 'description': 'The full ticker row — every signal on the schema page. On the list form, an object keyed by symbol, one full row each.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'List form only — how many of `requested` were found.'}, 'ticker': {'type': 'string', 'description': 'The symbol you asked for, normalised. Single form only.'}, 'not_found': {'type': 'string', 'description': 'List form only — the requested symbols we do not track, in request order. An empty array when every symbol was found.'}, 'requested': {'type': 'string', 'description': 'List form only — the canonical symbols asked for, de-duplicated, in request order.'}}, 'additionalProperties': False}
tickerbot_get_ticker_coverage
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.
입력 스키마
{'type': 'object', 'required': ['ticker'], 'properties': {'ticker': {'type': 'string', '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': 'object', 'required': ['as_of', 'ticker', 'name', 'minute_tier', 'spans', 'measured_fields'], 'properties': {'name': {'type': 'string', 'description': 'Company or instrument name.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'spans': {'type': 'object', 'description': 'Per resolution — `oldest`, `newest`, `rows`.'}, 'ticker': {'type': 'string', 'description': 'The symbol you asked for.'}, 'minute_tier': {'type': 'object', '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.'}, 'measured_fields': {'type': 'array', 'items': {'type': 'string'}, '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.'}}, 'additionalProperties': False}
tickerbot_get_universe
Returns the universe doc. Use `top_10`/`top_100` to fetch a system universe; any other slug must be one your account owns.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Universe slug.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'id', 'name', 'description', 'tickers', 'size', 'system', 'created_at', 'updated_at'], 'properties': {'id': {'type': 'string', 'description': "The slug — the universe's handle in `?universe=`."}, 'name': {'type': 'string', 'description': 'Display label.'}, 'size': {'type': 'number', 'description': 'Member count.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'system': {'type': 'boolean', 'description': '`true` for a built-in universe, `false` for one you created.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Members.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'description': {'type': 'string', 'description': 'Free-form notes; `""` when unset.'}, 'effective_at': {'type': 'number', 'description': 'System universes only: when this membership took effect.'}, 'rebalance_method': {'type': 'string', 'description': 'System universes only: how membership is chosen.'}, 'next_rebalance_at': {'type': 'number', 'description': 'System universes only: when membership is next recomputed.'}}, 'additionalProperties': False}
tickerbot_get_webhook
The current state of one webhook subscription.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.).'}}}
출력 스키마
{'type': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_list_events
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.
입력 스키마
{'type': 'object', 'properties': {'q': {'type': 'string', '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."}, 'to': {'type': 'string', '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.)'}, 'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Aggregate-mode sort direction.'}, 'firm': {'type': 'string', 'description': 'Analyst-only structured filter — requires `kind=analyst` alone (`400` otherwise). Exact firm-name match on the ratings feed.'}, 'from': {'type': 'string', '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.)'}, 'join': {'enum': ['state'], 'type': 'string', 'description': "Set to `state` to allow ticker-state signals in `q`/`select`/`group_by`/`having`, evaluated as of each event's timestamp (daily resolution)."}, 'kind': {'enum': ['dividend', 'split', 'insider', 'analyst', 'earnings', 'signal', 'news', 'estimate_revision'], 'type': 'string', 'description': 'Comma list of kinds. Omitted → the five corporate kinds; `signal`, `news` and `estimate_revision` join only when named here.'}, 'limit': {'type': 'integer', 'default': 50, 'description': 'Page size (row modes) / max rollup rows (aggregate mode). Max 1000.'}, 'order': {'type': 'string', '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.)"}, 'action': {'enum': ['upgrades', 'downgrades', 'initiates_coverage_on', 'maintains', 'reiterates', 'assumes', 'reinstates', 'suspends', 'terminates_coverage_on'], 'type': 'string', 'description': 'Analyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`.'}, 'having': {'type': 'string', 'description': 'Post-aggregation filter. Requires `group_by`.'}, 'select': {'type': 'string', '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.'}, 'signal': {'type': 'string', '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.'}, 'ticker': {'type': 'string', 'description': 'Single-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins.'}, 'tickers': {'type': 'string', 'description': 'Comma list of tickers (max 50). Mutually exclusive with `universe`.'}, 'group_by': {'type': 'string', '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`."}, 'interval': {'enum': ['1m', '1h', '1d', 'auto'], 'type': 'string', 'default': 'auto', 'description': "Grain the per-event state is reconstructed at, when `join=state`: `1m`, `1h`, `1d`, or `auto` (default). `auto` resolves to `1d` — the event set's tickers are not known before the query runs, and `1d` is the only tier covering the whole universe, so it is the only grain guaranteed to satisfy every event. An explicit `1m`/`1h` trades coverage for precision: events on tickers absent from that tier join to `null`. A referenced column the grain does not store is a `400`. Reported back as `_meta.state_interval`."}, 'universe': {'type': 'string', 'description': 'Universe slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`.'}, 'transition': {'enum': ['enter', 'exit'], 'type': 'string', 'description': 'Signal-only filter — requires `kind=signal` alone. `enter` (false→true) or `exit` (true→false).'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'query', 'count', 'results'], 'properties': {'_meta': {'type': 'object', '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`).'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'query': {'type': 'object', '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`.'}, 'results': {'type': 'array', 'items': {'type': 'string'}, 'description': 'One row per event (`ticker`, `ts`, `kind`, `payload`), or rollup rows plus `truncated: true` when an aggregate exceeds `limit`.'}, 'truncated': {'type': 'boolean', '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.'}, 'next_cursor': {'type': 'string', '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.'}}, 'additionalProperties': False}
tickerbot_list_signals
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`.
입력 스키마
{'type': 'object', 'properties': {'kind': {'enum': ['builtin', 'custom'], 'type': 'string', 'description': 'Filter by kind (`expression` accepted as a legacy alias for `custom`). Omit to return both.'}, 'limit': {'type': 'integer', 'default': 50, 'description': 'Page size for the custom-signal slice. Max 200.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from a prior response.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'count', 'count_builtin', 'count_custom', 'next_cursor', 'signals'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page — the canonical count, equal to `count_builtin` + `count_custom`.'}, 'signals': {'type': 'array', 'items': {'type': 'string'}, '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`.'}, 'taxonomy': {'type': 'object', '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`.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}, 'count_custom': {'type': 'number', 'description': 'Your custom signals.'}, 'count_builtin': {'type': 'number', 'description': 'Built-in signals in the catalog.'}}, 'additionalProperties': False}
tickerbot_list_tickers
Every symbol we track, active or delisted, as one identity row each. Use `/v2/tickers/{ticker}` for the full row.
입력 스키마
{'type': 'object', 'properties': {'limit': {'type': 'integer', 'default': 50, 'description': 'Page size. Max 1000.'}, 'cursor': {'type': 'string', '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."}, 'search': {'type': 'string', '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.'}, 'exchange': {'type': 'string', '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.'}, 'asset_type': {'enum': ['CS', 'ETF', 'ADRC', 'PFD', 'FUND', 'UNIT', 'SP', 'ETS', 'WARRANT', 'RIGHT', 'ETN', 'ETV', 'equity'], 'type': 'string', '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`.'}, 'asset_class': {'type': 'string', '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': 'object', 'required': ['as_of', 'count', 'next_cursor', 'results'], 'properties': {'_meta': {'type': 'object', 'description': 'With `asset_class` only: `scope.asset_class` lists the classes the page was filtered to.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'results': {'type': 'array', 'items': {'type': 'string'}, '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`.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page.'}}, 'additionalProperties': False}
tickerbot_list_universes
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.
입력 스키마
{'type': 'object', 'properties': {'limit': {'type': 'integer', 'default': 50, 'description': 'Page size (applies to your own). Max 100.'}, 'owner': {'enum': ['me', 'system', 'all'], 'type': 'string', 'default': 'me', 'description': 'Which universes to list: `me` (your own), `system` (built-ins), or `all` (both).'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous response.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'count', 'next_cursor', 'universes'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'universes': {'type': 'array', 'items': {'type': 'string'}, 'description': 'The universes in scope. Every row carries `system: true|false`.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_list_webhook_deliveries
Recent deliveries for one webhook — what was sent, and what came back.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id.'}, 'to': {'type': 'string', '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.'}, 'from': {'type': 'string', '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.'}, 'limit': {'type': 'integer', 'default': 50, 'description': 'Page size. Max 100.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor.'}, 'status': {'enum': ['pending', 'delivered', 'permanent_failure'], 'type': 'string', 'description': 'Filter by delivery status.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'count', 'next_cursor', 'deliveries'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'deliveries': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Attempts, newest first: status, attempt, response code, error, and `body_string` — the exact JSON POSTed.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_list_webhooks
Every webhook subscription on this account, newest first.
입력 스키마
{'type': 'object', 'properties': {'limit': {'type': 'integer', 'default': 50, 'description': 'Page size. Max 100.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous response.'}, 'status': {'enum': ['active', 'disabled'], 'type': 'string', '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.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'count', 'next_cursor', 'webhooks'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'webhooks': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Your subscriptions, newest first, each with its `subscription_origin` and health fields. `signing_secret` is stripped — it is shown only on create.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_scan
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`.
입력 스키마
{'type': 'object', 'required': ['q'], 'properties': {'q': {'type': 'string', '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.'}, 'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction.'}, 'asof': {'type': 'string', 'description': "Optional. Target moment as `YYYY-MM-DD` (that day's close) or an ISO timestamp (that intraday moment) — the same read as it stood then, unlimited depth. Full contract under As of a past date."}, 'full': {'type': 'boolean', 'default': False, 'description': 'Return every signal instead of the default set. Mutually exclusive with `columns` — passing both is a 400.'}, 'limit': {'type': 'integer', '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.'}, 'order': {'type': 'string', '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."}, 'cursor': {'type': 'string', 'description': "Opaque cursor from the previous response's `next_cursor`. Row mode only."}, 'having': {'type': 'string', 'description': 'Filter the aggregate rows (requires `group_by`). Custom signals are valid here too.'}, 'select': {'type': 'string', '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.'}, 'columns': {'type': 'string', '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.'}, 'group_by': {'type': 'string', '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`.'}, 'interval': {'enum': ['1m', '1h', '1d', 'auto'], 'type': 'string', 'default': 'auto', 'description': 'Grain the past state is reconstructed at: `1m`, `1h`, `1d`, or `auto` (default). Only valid alongside `asof` — a live read with `interval` is a 400. Details under As of a past date.'}, 'universe': {'type': 'string', 'description': 'Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,963 tracked tickers.'}, 'asset_class': {'type': 'string', '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': 'object', 'required': ['as_of', 'query', 'count', 'results'], 'properties': {'_meta': {'type': 'object', '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`).'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'query': {'type': 'object', 'description': 'Your query, echoed — `q`, `order`, `dir`, `limit`, and any scope.'}, 'results': {'type': 'array', 'items': {'type': 'string'}, 'description': 'One row per match — every signal on the schema page, plus any you named.'}, 'truncated': {'type': 'boolean', '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.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_search_news
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.
입력 스키마
{'type': 'object', 'properties': {'q': {'type': 'string', '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.'}, 'to': {'type': 'string', 'description': 'Articles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.)'}, 'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction.'}, 'from': {'type': 'string', 'description': 'Earliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. (`since` accepted as an alias.)'}, 'limit': {'type': 'integer', 'default': 50, 'description': 'Page size. Max 1000.'}, 'order': {'type': 'string', '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).'}, 'cursor': {'type': 'string', 'description': "Opaque pagination cursor from a prior response's `next_cursor`."}, 'having': {'type': 'string', 'description': 'HAVING clause on the aggregate (max 1000 chars). Requires `group_by`.'}, 'search': {'type': 'string', '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.'}, 'select': {'type': 'string', '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`).'}, 'ticker': {'type': 'string', 'description': 'Articles mentioning this symbol (ANDed with `q`).'}, 'tickers': {'type': 'string', 'description': 'Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.'}, 'group_by': {'type': 'string', '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?`.'}, 'universe': {'type': 'string', 'description': 'Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'query', 'count', 'next_cursor', 'results'], 'properties': {'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'count': {'type': 'number', 'description': 'Rows in this page.'}, 'query': {'type': 'object', 'description': 'Your filters, echoed.'}, 'results': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Article rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list.'}, 'truncated': {'type': 'boolean', 'description': 'Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap.'}, 'next_cursor': {'type': 'string', 'description': 'Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.'}}, 'additionalProperties': False}
tickerbot_subscribe_events
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.
입력 스키마
{'type': 'object', 'required': ['kinds'], 'properties': {'q': {'type': 'string', '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."}, 'name': {'type': 'string', 'description': 'Display name. Defaults to `events: <kinds> · <scope>`.'}, 'kinds': {'enum': ['dividend', 'split', 'insider', 'analyst', 'earnings'], 'type': 'string', 'description': 'Event kinds to fire on — array or comma list.'}, 'ticker': {'type': 'string', 'description': 'Single-symbol shorthand for `tickers`.'}, 'channel': {'enum': ['webhook', 'discord', 'in_app', 'mobile_push'], 'type': 'string', 'description': 'Delivery channel. `slack` is reserved and returns `501`.'}, 'event_q': {'type': 'string', 'description': 'Optional event-CONTENT filter in the `/v2/events` grammar — only `ticker`, `ts`, `kind`, `payload` may appear. Composes with `q`.'}, 'tickers': {'type': 'string', '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.'}, 'universe': {'type': 'string', 'description': 'Scope to a universe slug (`top_10`, `top_100`, or one of yours). `universe_id` accepted as an alias.'}, 'device_id': {'type': 'string', 'description': 'Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.'}, 'target_url': {'type': 'string', 'description': 'HTTPS delivery URL; or use `channel` + `discord_url`/`device_id`. Omit for in-app.'}, 'discord_url': {'type': 'string', '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': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'test_url': {'type': 'string', 'description': 'Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'channel_config': {'type': 'object', 'description': 'Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'signing_secret': {'type': 'string', 'description': 'Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_subscribe_scan
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.
입력 스키마
{'type': 'object', 'required': ['q'], 'properties': {'q': {'type': 'string', '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.'}, 'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction for `order`.'}, 'name': {'type': 'string', 'description': 'Human-readable label (up to 80 chars). Defaults to `scan: <q>`.'}, 'order': {'type': 'string', '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)."}, 'cadence': {'enum': ['realtime', 'hourly', 'nyse_open'], 'type': 'string', '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`.'}, 'channel': {'enum': ['webhook', 'discord', 'in_app', 'mobile_push'], 'type': 'string', '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`.'}, 'columns': {'type': 'string', '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.'}, 'universe': {'type': 'string', 'description': 'System or user-owned universe to scope the scan. `universe_id` accepted as an alias. Unknown universes are a 404 `universe_not_found`.'}, 'device_id': {'type': 'string', 'description': 'Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.'}, 'target_url': {'type': 'string', 'description': 'https:// URL to POST when the match set changes. Omit for in-app delivery.'}, 'discord_url': {'type': 'string', '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': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'test_url': {'type': 'string', 'description': 'Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'channel_config': {'type': 'object', 'description': 'Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'signing_secret': {'type': 'string', 'description': 'Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_subscribe_signal
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.
입력 스키마
{'type': 'object', 'required': ['signal'], 'properties': {'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction for `order`.'}, 'name': {'type': 'string', '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.'}, 'order': {'type': 'string', '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)."}, 'signal': {'type': 'string', 'description': 'Signal name from the schema (case-insensitive).'}, 'ticker': {'type': 'string', 'description': 'Restrict to a single ticker. Default: any ticker.'}, 'cadence': {'enum': ['realtime', 'hourly', 'nyse_open'], 'type': 'string', '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`.'}, 'channel': {'enum': ['webhook', 'discord', 'in_app', 'mobile_push'], 'type': 'string', '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.'}, 'columns': {'type': 'string', '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.'}, 'universe': {'type': 'string', '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`.'}, 'condition': {'type': 'string', '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).'}, 'device_id': {'type': 'string', 'description': 'Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.'}, 'target_url': {'type': 'string', 'description': 'https:// URL to POST when fired. Omit for in-app delivery.'}, 'discord_url': {'type': 'string', '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': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'test_url': {'type': 'string', 'description': 'Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'channel_config': {'type': 'object', 'description': 'Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'signing_secret': {'type': 'string', 'description': 'Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_subscribe_ticker
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.
입력 스키마
{'type': 'object', 'required': ['ticker', 'q'], 'properties': {'q': {'type': 'string', 'description': 'WHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.)'}, 'dir': {'enum': ['asc', 'desc'], 'type': 'string', 'default': 'desc', 'description': 'Sort direction for `order`.'}, 'name': {'type': 'string', 'description': 'Human-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`.'}, 'order': {'type': 'string', '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)."}, 'ticker': {'type': 'string', '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.'}, 'cadence': {'enum': ['realtime', 'hourly', 'nyse_open'], 'type': 'string', '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`.'}, 'channel': {'enum': ['webhook', 'discord', 'in_app', 'mobile_push'], 'type': 'string', '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.'}, 'columns': {'type': 'string', '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.'}, 'condition': {'type': 'string', 'description': 'Original name for `q` — accepted as well. The same WHERE-clause fragment; send either spelling.'}, 'device_id': {'type': 'string', 'description': 'Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.'}, 'target_url': {'type': 'string', 'description': 'https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard).'}, 'discord_url': {'type': 'string', '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': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, '_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'test_url': {'type': 'string', 'description': 'Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'channel_config': {'type': 'object', 'description': 'Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'signing_secret': {'type': 'string', 'description': 'Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
tickerbot_test_webhook
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.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'id', 'webhook_id', 'test', 'delivered', 'http_status', 'elapsed_ms', 'error', 'status', 'created_at'], 'properties': {'id': {'type': 'string', 'description': 'Delivery id of the test — `dl_…`, the same shape as a real delivery on the deliveries route.'}, 'test': {'type': 'boolean', 'description': '`true` — this delivery was a synthetic ping, not a trigger firing.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'error': {'type': 'string', 'description': 'Why delivery failed, in words; `null` on success.'}, 'status': {'type': 'string', 'description': "The delivery record's status: `delivered` or `permanent_failure` (a test is never retried)."}, 'delivered': {'type': 'boolean', 'description': 'Whether your endpoint accepted the ping (2xx within the timeout).'}, 'created_at': {'type': 'string', 'description': 'When the ping was sent (ISO 8601).'}, 'elapsed_ms': {'type': 'number', 'description': 'Round-trip time of the ping.'}, 'webhook_id': {'type': 'string', 'description': 'The webhook that was tested.'}, 'http_status': {'type': 'number', 'description': 'The status your endpoint returned; `null` when it could not be reached.'}}, 'additionalProperties': False}
tickerbot_update_custom_signal
Update a custom signal you own — its expression, description, or name.
입력 스키마
{'type': 'object', 'required': ['signal'], 'properties': {'expr': {'type': 'string', '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`).'}, 'signal': {'type': 'string', 'description': 'Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.'}, 'new_name': {'type': 'string', '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.'}, 'description': {'type': 'string', 'description': 'New description. Not derived from `expr` — change both if the prose describes a threshold you are moving.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'signal'], 'properties': {'_meta': {'type': 'object', '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.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'signal': {'type': 'object', 'description': 'The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.'}}, 'additionalProperties': False}
tickerbot_update_universe
Update one of your universes: its name, description, or members. `tickers` replaces the whole list; `add`/`remove` adjust it. System universes cannot be edited.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Universe slug.'}, 'add': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Add these tickers (deduplicated).'}, 'name': {'type': 'string', 'description': 'New label. Non-empty, max 80 characters.'}, 'remove': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Remove these tickers.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Replace the full ticker list (up to 10,000; validated against the active universe). Does not combine with `add`/`remove` (400).'}, 'description': {'type': 'string', 'description': 'New notes. Max 500 characters.'}}}
출력 스키마
{'type': 'object', 'required': ['as_of', 'id', 'name', 'description', 'tickers', 'size', 'system', 'created_at', 'updated_at'], 'properties': {'id': {'type': 'string', 'description': "The slug — the universe's handle in `?universe=`."}, 'name': {'type': 'string', 'description': 'Display label.'}, 'size': {'type': 'number', 'description': 'Member count.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'system': {'type': 'boolean', 'description': '`false` — this is your universe.'}, 'tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Members, after this call.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'description': {'type': 'string', 'description': 'Free-form notes; `""` when unset.'}, 'effective_at': {'type': 'number', 'description': 'System universes only; absent on yours.'}, 'rebalance_method': {'type': 'string', 'description': 'System universes only; absent on yours.'}, 'next_rebalance_at': {'type': 'number', 'description': 'System universes only; absent on yours.'}}, 'additionalProperties': False}
tickerbot_update_webhook
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.
입력 스키마
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Webhook id.'}, 'name': {'type': 'string', 'description': 'New display name. Non-empty, max 80 characters.'}, 'cadence': {'enum': ['realtime', 'hourly', 'nyse_open'], 'type': 'string', 'description': 'Evaluation cadence. A user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).'}, 'enabled': {'type': 'boolean', 'description': '`false` disables the webhook (status → `disabled`). `true` is a no-op unless disabled, in which case use `POST /v2/webhooks/{id}/enable` instead.'}, 'target_url': {'type': 'string', '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': 'object', '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'], 'properties': {'q': {'type': 'string', 'description': 'The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.'}, 'id': {'type': 'string', 'description': 'The webhook id — `wh_…`, the handle for every other call on this record.'}, 'dir': {'type': 'string', 'description': 'Sort direction for that list; `null` means the default (`desc`).'}, 'name': {'type': 'string', 'description': 'Your label for the subscription.'}, 'as_of': {'type': 'string', 'description': 'Server time this response was assembled (ISO 8601).'}, 'order': {'type': 'string', 'description': 'Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).'}, 'fields': {'type': 'string', 'description': 'Extra signals carried on each fired match row; `null` means the standard set.'}, 'source': {'type': 'string', 'description': 'Which API version created the record; `v2` for anything you create today.'}, 'status': {'type': 'string', 'description': '`active` or `disabled`. Auto-disable follows repeated delivery failure.'}, 'cadence': {'type': 'string', 'description': 'How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.'}, 'channel': {'type': 'string', 'description': 'Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.'}, 'event_q': {'type': 'string', 'description': 'Event-trigger webhooks only: the payload filter, or `null`.'}, 'rule_id': {'type': 'string', 'description': 'Legacy link to a v1 alert rule; `null` on everything created through v2.'}, 'delivery': {'type': 'string', 'description': 'Legacy alias of `channel`, kept aligned for older readers.'}, 'created_at': {'type': 'number', 'description': 'Creation timestamp.'}, 'last_error': {'type': 'string', 'description': 'The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".'}, 'last_fired': {'type': 'number', 'description': 'When a delivery last went out; `null` if it never has.'}, 'target_url': {'type': 'string', 'description': 'Your HTTPS endpoint; `null` on every channel except `webhook`.'}, 'updated_at': {'type': 'number', 'description': 'Last modification timestamp.'}, 'event_kinds': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).'}, 'universe_id': {'type': 'string', 'description': 'Universe the trigger is scoped to, or `null` for the whole market.'}, 'next_eval_at': {'type': 'number', 'description': 'When the evaluator will next consider this subscription.'}, 'trigger_kind': {'type': 'string', 'description': 'Event-trigger webhooks only: `event`.'}, 'event_tickers': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.'}, 'last_match_set': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.'}, 'last_evaluated_at': {'type': 'number', 'description': 'When it was last evaluated; `null` until the first run.'}, 'last_eval_error_at': {'type': 'number', 'description': 'When the last evaluation error happened; `null` on a healthy hook.'}, 'subscription_origin': {'type': 'object', 'description': 'Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.'}, 'last_predicate_value': {'type': 'string', 'description': "The trigger's value at the last evaluation; `null` until it has run."}}, 'additionalProperties': False}
변경됨
tickerbot_list_events
2026년 10월 1일 2:50 AM
변경됨
tickerbot_scan
2026년 10월 1일 2:50 AM
변경됨
tickerbot_get_bars
2026년 9월 29일 2:58 AM
변경됨
tickerbot_get_ticker_coverage
2026년 9월 29일 2:58 AM
변경됨
tickerbot_create_webhook
2026년 9월 27일 2:49 AM
변경됨
tickerbot_subscribe_events
2026년 9월 27일 2:49 AM
변경됨
tickerbot_list_events
2026년 9월 27일 2:49 AM
변경됨
tickerbot_get_series
2026년 9월 27일 2:49 AM
변경됨
tickerbot_subscribe_scan
2026년 9월 27일 2:49 AM
변경됨
tickerbot_scan
2026년 9월 27일 2:49 AM
변경됨
tickerbot_subscribe_signal
2026년 9월 27일 2:49 AM
변경됨
tickerbot_get_signal
2026년 9월 27일 2:49 AM
변경됨
tickerbot_update_custom_signal
2026년 9월 27일 2:49 AM
변경됨
tickerbot_create_custom_signal
2026년 9월 27일 2:49 AM
변경됨
tickerbot_subscribe_ticker
2026년 9월 27일 2:49 AM
변경됨
tickerbot_create_webhook
2026년 9월 25일 2:58 AM
변경됨
tickerbot_get_etf_holdings
2026년 9월 25일 2:58 AM
변경됨
tickerbot_subscribe_events
2026년 9월 25일 2:58 AM
변경됨
tickerbot_get_bars
2026년 9월 25일 2:58 AM
변경됨
tickerbot_subscribe_scan
2026년 9월 25일 2:58 AM
변경됨
tickerbot_scan
2026년 9월 25일 2:58 AM
변경됨
tickerbot_subscribe_signal
2026년 9월 25일 2:58 AM
변경됨
tickerbot_get_signal
2026년 9월 25일 2:58 AM
변경됨
tickerbot_subscribe_ticker
2026년 9월 25일 2:58 AM
변경됨
tickerbot_list_tickers
2026년 9월 25일 2:58 AM
변경됨
tickerbot_get_bars
2026년 9월 23일 2:49 AM
변경됨
tickerbot_scan
2026년 9월 21일 2:56 AM
삭제됨
tickerbot_patch_webhook
2026년 9월 19일 2:47 AM
삭제됨
tickerbot_list_system_universes
2026년 9월 19일 2:47 AM
삭제됨
tickerbot_list_signals_catalog
2026년 9월 19일 2:47 AM