mcp-server
Server Details
The stock market, in SQL — scan, replay, or subscribe across ~12k US tickers and top 100 cryptos.
- Status
- Healthy
- Uptime
- 99.9% over 38 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-06-18
- URL
- Repository
- tickerbot/tickerbot-mcp
- GitHub Stars
- 0
- Server Listing
- Tickerbot MCP Server
TDQS
Scored across 32 tools
Tools are mostly organized by resource and action, but the subscribe_* helpers overlap conceptually with create_webhook, which is described as the canonical form. Some query tools like scan, get_signal, and get_series also require careful reading to pick correctly, though their descriptions are strong enough to disambiguate.
Every tool follows the same tickerbot_ verb_noun snake_case convention, with clear verbs like create, get, list, update, delete, subscribe, enable, and test. This is highly predictable and makes the large tool surface easier to navigate.
At 32 tools, the surface is beyond the 25+ threshold and feels heavy even for a full market-data/signals/webhook platform. The consistent naming helps, but the sheer number of overlapping query and subscription entry points places a real burden on an agent.
The domain is well covered: full lifecycle for universes, custom signals, and webhooks; rich query tools for bars, signals, tickers, events, and news; plus coverage checks and delivery inspection. Missing capabilities are handled explicitly, such as immutable webhook triggers requiring delete/recreate.
Available Tools
32 toolstickerbot_create_custom_signalBInspect
A named boolean predicate you can reference anywhere a built-in signal goes.
| Name | Required | Description | Default |
|---|---|---|---|
| expr | Yes | 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 VERBATIM, 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. | |
| name | Yes | 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 | No | Free-form notes. Max 500 chars. Absent or empty comes back as `""` rather than null. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| signal | Yes | The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure, but it only defines what a custom signal is. It says nothing about creation semantics such as compile_failed validation, naming conflicts, or how expressions are echoed back; those details live only in parameter descriptions, not the tool description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no filler: it defines the entity and its intended usage in one pass. Information is front-loaded and every word contributes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The one-line definition plus detailed schema properties and an output schema is adequate for an agent to attempt a call, but the tool description alone does not state the create action, error behavior, or permissions. Given the tool has no annotations, some important operational context is missing from the description itself.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already thoroughly documents expr, name, and description. The top-level description adds no parameter-level meaning beyond pointing out that a custom signal can be referenced wherever built-in signals are used, leaving the schema to carry semantic weight.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the created entity as a 'named boolean predicate' and explains its reuse 'anywhere a built-in signal goes,' so an agent can tell it apart from plain scan expressions. It stops short of using an explicit performative verb like 'creates,' relying on the tool name to convey the create action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'reference anywhere a built-in signal goes' implies the main use case: define a custom signal once and reuse it. It does not explicitly say when to choose this over inline expressions, update/delete operations, or the sibling create_universe/create_webhook tools, so guidance is mostly implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_create_universeBInspect
Create a named ticker list owned by your account.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | 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 | Yes | Human-readable label, up to 80 characters. Display-only — never used to reference the universe. | |
| tickers | Yes | Ticker symbols, up to 10,000. Validated against the active universe. `[]` is accepted — a shell universe you can fill later via PATCH. | |
| description | No | Free-form notes, up to 500 characters. Stored as `""` when omitted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | The slug — the universe's handle in `?universe=`. |
| name | Yes | Display label. |
| size | Yes | Member count. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| system | Yes | `false` — this is your universe. |
| tickers | Yes | Members, after this call. |
| created_at | Yes | Creation timestamp. |
| updated_at | Yes | Last modification timestamp. |
| description | Yes | Free-form notes; `""` when unset. |
| effective_at | No | System universes only; absent on yours. |
| rebalance_method | No | System universes only; absent on yours. |
| next_rebalance_at | No | System universes only; absent on yours. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It only states that a list is created and owned by the account; it does not disclose id generation, slug validation, reserved names, ticker validation, empty-list acceptance, persistence behavior, or error semantics. Those details appear in the schema, not in the tool description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence with the verb and direct object front-loaded and no filler. 'Owned by your account' adds relevant context without bloat. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is a straightforward create operation and the input schema richly covers parameters and output schema exists, so the agent can construct a valid call. However, the description itself provides no usage guidance or behavioral context and there are no annotations, leaving some selection and expectation-setting gaps that a fuller description would fill.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%; each of the four parameters is individually documented with constraints like the id pattern, reserved values, length limits, and ticker validation. The tool description adds no parameter-level meaning, but the baseline 3 is appropriate because the schema already does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific action ('Create') and resource ('named ticker list') and clarifies ownership ('owned by your account'). This distinguishes it from sibling create tools like tickerbot_create_custom_signal and tickerbot_create_webhook, and from update/delete universe tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use guidance, no mention of alternatives, and no exclusions such as 'use update_universe for modifications.' The only usage signal is the verb 'Create' and the tool name, so the agent must infer when this tool should be selected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_create_webhookAInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| dir | No | Sort direction for `order`. Not accepted on `event` triggers (400). | desc |
| name | No | Display name, max 80 characters. Defaults to an auto-generated one from the trigger. | |
| order | No | 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). | market_cap |
| cadence | No | Evaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise). | realtime |
| channel | No | Delivery channel. See Delivery channels. | |
| columns | No | 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 | Yes | 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 | No | Registered device id (channel `mobile_push`, see /v2/devices). | |
| target_url | No | 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 | No | Discord webhook URL (channel `discord`). |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| test_url | Yes | Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| channel_config | Yes | Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id). |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| signing_secret | Yes | Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden, and it steps up: it discloses the paid-plan gating (a real behavioral constraint), the canonical-vs-sugar relationship, and how the discriminated trigger selects its shape. It doesn't mention idempotency or failure behavior, but for a create operation with an output schema present, the key disclosures (plan gate, trigger discrimination) are genuinely additive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences with zero waste. Purpose is front-loaded ('Canonical create'), the trigger taxonomy is packed into one sentence, and the sibling-relationship and plan constraints get their own crisp clauses. Every sentence earns its place — this is model concision, not under-specification.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter tool with a nested discriminated union and an output schema, the description covers the conceptual frame, the trigger shapes, the sibling routing, and the plan gate. The output schema covers return values, so the description doesn't need to. Missing minor items like rate limits, but the combination of description plus schema plus output schema is largely sufficient for an agent to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real value beyond the schema: it explains the 'trigger plus delivery' mental model, summarizes each trigger shape's fields (scan/ticker/signal/event), and clarifies how the discriminated object maps to the flat subscribe shortcuts. This conceptual glue is exactly what the raw schema lacks, especially for the nested trigger object.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Canonical create') and defines the core concept ('a webhook is a trigger plus a delivery'). It names sibling subscribe tools and asserts they are 'flat sugar over exactly this', clearly differentiating this canonical tool from those flat variants. The trigger-shape taxonomy is laid out compactly, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes between the canonical create and the subscribe sugar: 'The subscribe tools are flat sugar over exactly this' tells an agent when to prefer which. It also discloses the paid-plan requirement ('Webhooks need a paid plan (Free has no webhook slots)'). It could be slightly more explicit about when-not-to-use, but the subscribe relationship plus plan gating covers the main decisions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_delete_custom_signalAInspect
Delete one of your custom signals. Refused by default if another of your signals references it.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | When `true`, skip the reference check and delete. References will break on next recompile. | |
| signal | Yes | Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral disclosure burden. It explicitly discloses that deletion is refused by default when another signal references it, and that using force will break those references on recompile. This is valuable destructive-behavior context, though it does not additionally state whether deletion is irreversible or what permissions are required.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no filler. The core operation is front-loaded, and the most important caveat about reference checks follows immediately. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with a fully documented schema and no nested objects, the description is nearly complete. It explains the default behavior and the force escape hatch. The only minor gaps are the absence of any mention of output/response shape and explicit irreversibility, but those are not critical for invoking the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents both parameters with 100% coverage, so the baseline is 3. The description adds semantic value by explaining the default refusal consequence, which gives real meaning to the force parameter and the reference-check behavior. The signal parameter details are already well covered in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Delete one of your custom signals.' This clearly separates it from sibling tools like delete_universe and delete_webhook, and the operation is obviously distinct from create/update custom signal tools. The first sentence leaves no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use the tool: when a custom signal needs to be removed. However, it does not explicitly name alternatives or state when not to use it, such as when the signal is a built-in or when the user should choose update instead. The usage guidance is adequate but left mostly implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_delete_universeAInspect
Permanently delete one of your universes. Webhooks that reference the deleted universe fail on their next eval — clean those up first.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Universe slug. | |
| force | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the safety burden; it satisfies this by calling out permanence and the downstream effect on referencing webhooks. It also implies that deletion is not just a soft remove, since webhooks begin failing on eval. This exceeds a simple 'deletes a universe' statement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences with no filler. The action is front-loaded and the important warning is in the second sentence, making the most relevant information immediately visible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the dangerous consequences (permanent deletion and dangling webhooks), and the schema covers id and force behavior. There's no output schema, but for a delete operation the missing return details are less critical; still, no annotation means minor details like ownership/permissions are only implied by 'your universes'.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already describes both parameters at 100% coverage, so the description gets the baseline 3. The only slight added context is 'your universes' for ownership, but it adds nothing about the id slug or force flag that isn't already in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and object — 'Permanently delete one of your universes' — and uses 'permanently' to make the irreversibility explicit. It clearly differentiates from sibling tools like create_universe, update_universe, and get_universe.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the core use case ('delete one of your universes') and provides a concrete precondition: clean up referencing webhooks first, because they 'fail on their next eval.' It doesn't name alternatives or explicit when-not-to-use conditions, but the caution gives clear context for deciding whether to proceed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_delete_webhookAInspect
Delete a webhook subscription, and its delivery history with it.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden for a destructive operation. It explicitly discloses that delivery history is also deleted, which is the most important side effect. It omits other details like idempotency or error behavior, but the key destructive consequence is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, front-loaded with the action and immediately followed by the critical side effect. There is no filler or redundancy, making it highly efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter destructive operation with no output schema, the description plus schema give an agent enough to call it correctly: the resource, the id, and the collateral deletion. Minor gaps like nonexistent-id handling and prerequisites prevent a perfect score, but the tool is simple enough that the description is nearly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: the single 'id' parameter is documented with 'Webhook id.' The tool description adds no additional parameter semantics, so the baseline score of 3 applies since the schema already covers the parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action and resource: 'Delete a webhook subscription'. It also reveals the collateral consequence ('and its delivery history with it'), which clearly distinguishes it from sibling deletes for other resources like tickerbot_delete_custom_signal and tickerbot_delete_universe.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives such as tickerbot_update_webhook or tickerbot_enable_webhook. It does not mention prerequisites, error conditions, or scenarios where deletion should be avoided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_enable_webhookAInspect
Re-enable a disabled webhook and start it clean. Clears match-state, so the next eval treats every currently-matching ticker as new.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full disclosure burden. It openly reveals the non-obvious side effect that match-state is cleared, so the next eval treats matching tickers as new. It stops short of describing behavior for already-enabled webhooks or error cases, but the core destructive side effect is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both information-denseched. The primary action is stated first Ia and the important side effect follows immediately. No filler or repetition exists.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description covers the action and the critical state-reset behavior. It could clarify behavior if the webhook is not currently disabled, but the provided information is otherwise sufficient for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already fully documents the only parameter, 'id', as the webhook id. The description adds no extra semantic detail about the id beyond that, so it does not meaningfully increase parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Re-enable a disabled webhook and start it clean.' This clearly distinguishes it from create/delete/test/update webhook operations, and the state-changing intent is explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use this tool: when re-enabling a disabled webhook. It does not explicitly name alternative tools or state when not to use it, but the use case is unambiguous from the operation described.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_barsDInspect
OHLCV bars from 1-second through monthly. The prices underneath the table.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | 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 | No | 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 | No | 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 | No | Most-recent N bars. Max 1000 — an over-cap value is clamped, not an error. | |
| before | No | 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 | No | 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 | Yes | Ticker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol. | |
| session | No | 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. | all |
| adjusted | No | 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 | Yes | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| bars | Yes | OHLCV bars, chronological, in the compact array shape. Bulk requests key this by symbol instead. |
| note | No | Present only when there is something to disclose about how the page was served: the first on-demand fetch for an untiered symbol (explains the latency; later calls are stored), or a `1s` page served from the local store because the provider could not be reached. Bulk responses carry `notes[symbol]` instead. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Bars returned in single-symbol mode; the number of SYMBOLS in bulk mode. |
| notes | No | Bulk (comma-list) requests only: the per-symbol disclosures, keyed by symbol, in place of `note`. |
| ticker | Yes | The symbol you asked for. |
| session | Yes | The session filter applied: `all` (default) or `regular` (09:30–16:00 ET, sub-hour intervals only). |
| adjusted | Yes | Whether the bars are split-adjusted — `true` unless you passed `adjusted=false`. |
| coverage | Yes | Why the page looks the way it does: `covered` when bars were found, `no_data` when the vendor has none for the window, `not_in_minute_tier` when a sub-hour interval was asked of a symbol the minute store does not carry. |
| interval | Yes | The bar size served. |
| next_cursor | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full burden of behavioral disclosure. The description only states what the tool returns and does not mention any behavioral traits such as side effects, authentication requirements, rate limits, or response format. This is a significant gap for a data retrieval tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely short, but it is under-specified rather than concise. It lacks structure and meaningful content, with a cryptic phrase that does not aid understanding. It is not front-loaded with useful information and reads as an incomplete thought.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 10 parameters and a rich schema, the description is severely inadequate. It does not explain when to use the tool, what to expect in the response, or any operational constraints. The output schema exists but the description does not reference or complement it. This fails to provide the context an agent needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters comprehensively. The description adds little beyond noting interval availability, which is already in the schema. Per the baseline rule for high coverage, a 3 is appropriate since the description does not need to compensate but also provides minimal added value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool returns OHLCV bars across various intervals, which gives a general sense of purpose. However, it lacks specificity about the resource scope and does not differentiate it from siblings like get_series or get_ticker. The phrase 'the prices underneath the table' is vague and doesn't add clarity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given on when to use this tool versus alternatives. There is no mention of conditions, exclusions, or when a different tool would be more appropriate. Given the large set of sibling tools, this is a critical omission.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_etf_holdingsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 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 | Yes | ETF symbol. Case-insensitive. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Holdings in this page. |
| total | Yes | Total constituents held, before `limit`. |
| is_etf | Yes | Whether the symbol is an ETF, from the instrument type on its ticker record. |
| ticker | Yes | The ETF you asked for. |
| holdings | Yes | Constituents, heaviest first, each with its weight. |
| truncated | Yes | `true` when `limit` cut the list short. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It discloses important edge-case behavior: non-ETF tickers return is_etf=false with empty holdings, and is_etf=true with zero holdings means data not yet ingested. It does not explicitly discuss rate limits or mutation, but 'Returns' and the get_ prefix imply a read-only operation, and the edge cases are well covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured: it leads with the core purpose, then explains edge cases, then gives an explicit exclusion. Every sentence adds value and there is no redundant repetition of schema information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich output schema and highly detailed limit parameter description, the tool description covers the main behavior, edge cases, and an important alternative. It is complete enough for an agent to select and invoke the tool correctly without additional context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful parameter-level semantics beyond the schema by explaining what happens when the ticker is not an ETF and clarifying the distinction between empty holdings and not-ingested holdings. This goes beyond the schema's simple 'ETF symbol' description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource—'Returns an ETF's constituents and their weights'—and immediately distinguishes the tool from reverse lookup by stating that 'which ETFs hold NVDA' is not this tool. It also clearly differentiates from sibling tools like get_etf_sectors by focusing on constituents and weights.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool (for ETF holdings) and when not to use it (reverse lookup should be a scan filter on etf_holders). It also covers edge cases like non-ETF tickers, which helps an agent decide when this tool is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_etf_sectorsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | ETF symbol. Case-insensitive. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Sectors returned. |
| is_etf | Yes | Whether the symbol is an ETF, from the instrument type on its ticker record. |
| ticker | Yes | The ETF you asked for. |
| sectors | Yes | Sector weights, heaviest first, in the vendor's ETF-profile vocabulary. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully discloses key behaviors: it guarantees completeness, specifies ordering, and handles non-ETF tickers by returning is_etf=false and empty sectors. This goes beyond the schema and provides essential edge-case context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three short sentences, front-loaded with the main purpose, and includes only necessary behavioral details. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with a single parameter and an existing output schema, the description covers return content, ordering, completeness, and edge-case behavior. Nothing essential is missing for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already describes the ticker parameter as an ETF symbol and case-insensitive, so the description adds no additional parameter semantics. With 100% schema coverage, the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: returns an ETF's sector weights, ordered heaviest first. It also distinguishes itself from the sibling tickerbot_get_etf_holdings by focusing on sectors rather than holdings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied – an agent would infer to use this when needing sector weight data for an ETF. However, the description does not explicitly mention when not to use it or suggest alternative tools like tickerbot_get_etf_holdings, leaving the selection somewhat to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_seriesAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Latest timestamp (inclusive), `YYYY-MM-DD` or ISO. | |
| asof | No | 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 | No | 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 | No | 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 | No | Opaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates. | |
| ticker | No | Single-symbol form — `/v2/series?ticker=AAPL` is ticker history in its canonical spelling. Exactly one of `ticker` or `tickers` is required. | |
| columns | No | 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 | No | 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 | No | Grid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid. | 1d |
| transitions_only | No | 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). |
Output Schema
| Name | Required | Description |
|---|---|---|
| _meta | Yes | 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. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows per ticker in this page. |
| series | Yes | Keyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for. |
| columns | Yes | Columns in the response, echoed. |
| tickers | Yes | Symbols in the response, echoed. |
| interval | Yes | The grid granularity served. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses cursor-paged backward behavior and transitions_only semantics, which are useful. However, it does not explicitly state read-only nature, error handling, rate limits, or other behavioral traits beyond the core retrieval and paging. It covers key behaviors but leaves some gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three concise sentences, front-loading the core capability and then adding the paging and transitions nuance. Every sentence contributes value without redundancy, and it is well-structured for quick comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description gives a strong executive summary of the tool's purpose and key behaviors, while the extensive schema descriptions cover all 10 parameters. The output schema exists to define returns. Some nuances like asof semantics are left to the schema, but the description is complete enough as an overview for a complex tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so each parameter is fully documented in the schema. The description itself adds no parameter-level detail beyond what the schema provides, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource (signals for tickers) and a distinctive capability: retrieving any signals for any tickers on a shared time grid, with paging and transitions. This clearly differentiates it from siblings like get_bars (likely single-ticker) by emphasizing multi-ticker alignment and cross-sectional behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies a multi-ticker signal use case but does not explicitly contrast with alternatives or state when to use this tool versus others. It does not mention 'for single-ticker history, use get_bars' or any exclusion criteria, leaving the when-to-use inference to the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_signalAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| asof | No | 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 | No | Page size. Max 200. | |
| cursor | No | Opaque cursor from the previous response. | |
| signal | Yes | 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 | No | 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). | default |
| interval | No | 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. | auto |
| universe | No | Optional. Scope to a system or caller-owned universe slug. | |
| condition | No | 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 | No | 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`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| _meta | No | With `asof` only: how the read was resolved — interval served and requested, blending, sources, frozen fields. See the as-of read below. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| signal | Yes | The signal you asked for. |
| results | Yes | Matching tickers with the signal value. |
| universe | Yes | The universe you scoped to, echoed; `null` when unscoped. |
| condition | Yes | The bound you passed, echoed; `null` for boolean and custom signals. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses key behaviors: condition requirements for non-boolean signals, sorting by signal value desc for non-booleans, and the asof historical option. It does not mention errors, pagination, or live-only constraints, but those are covered in the schema. It adds value beyond the schema by summarizing condition syntax.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences with no filler. It front-loads the purpose, includes concrete examples, and states the sort behavior. Every sentence earns its place, making it efficient and scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (9 parameters) and the rich input schema, the description provides sufficient context for an agent to understand the tool's core function. It does not cover all edge cases (e.g., live-only restrictions on sort_by and interval), but those are explicitly documented in the parameter descriptions. The output schema further reduces ambiguity. The description is complete enough when combined with the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds meaning beyond the input schema by providing a quick summary of condition types and sorting rules. While the schema has detailed per-parameter descriptions (100% coverage), the description condenses the essential logic into a few examples, making it easier for an agent to grasp the condition parameter without reading the full schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool returns the set of tickers matching a signal, either currently or as of a past moment. It uses a specific verb ('get') and resource ('signal'), and the phrase 'One name, the whole market, one call' conveys scope. It distinguishes itself from siblings like get_bars and get_series by focusing on signal state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage (for retrieving tickers matching a signal) but does not explicitly compare to alternatives or state when not to use it. It does not mention sibling tools like scan or subscribe_signal, so an agent must infer appropriateness. The guidance is implicit rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_tickerAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| asof | No | 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 | Yes | 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 | No | 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. | auto |
Output Schema
| Name | Required | Description |
|---|---|---|
| data | Yes | The full ticker row — every signal on the schema page. On the list form, an object keyed by symbol, one full row each. |
| _meta | No | 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 | Yes | Server time this response was assembled (ISO 8601). |
| count | No | List form only — how many of `requested` were found. |
| ticker | No | The symbol you asked for, normalised. Single form only. |
| not_found | No | List form only — the requested symbols we do not track, in request order. An empty array when every symbol was found. |
| requested | No | List form only — the canonical symbols asked for, de-duplicated, in request order. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden and does it well: it explains live vs. asof reads, batch result keying, the not_found field, and the crypto prefix distinction. It does not discuss rate limits or failure modes, but it covers the most important behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences deliver the core behavior, batch capability, historical mode, and ticker-prefix caveat without filler. Key information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is relatively complex, but the description covers the essential calling contexts: single vs. batch, live vs. asof, and the crypto/ETF distinction. With a full input schema and output schema present, the remaining parameter and return details are adequately covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents all parameters at 100% coverage, including the asof format, interval constraints, and ticker naming conventions. The description adds a small amount of value by mentioning the batch response shape, but mostly restates what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific operation and resource: return the full ticker row with every signal for one symbol or a batch of up to 50. It is clear and specific, but it does not explicitly differentiate itself from sibling tools such as get_signal or get_bars.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use this tool, especially for getting the full current or historical ticker snapshot and for batch symbol lookups. However, it never explicitly contrasts this with alternatives or states when another sibling tool should be preferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_ticker_coverageAInspect
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. minute_tier.included: false with on_demand: true is not a gap — sub-hour bars fetch from the provider on first request.
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| name | Yes | Company or instrument name. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| spans | Yes | Per resolution — `oldest`, `newest`, `rows`. |
| ticker | Yes | The symbol you asked for. |
| minute_tier | Yes | Whether this ticker is in the minute tier (`included`) and its `rank` within it. `included:false` is NOT "no intraday data": the object then carries `on_demand: true`, `first_call_latency` (`"3-10s"`) and `window_days` (31) — sub-hour bars for the symbol are fetched from the provider on first request and stored, so the first call is slow and later ones are sub-second. |
| measured_fields | Yes | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it delivers: it explains that an empty result is meaningful, not ambiguous, and that `minute_tier.included: false` with `on_demand: true` is expected behavior because sub-hour bars fetch on first request. This prevents a caller from misreading the response.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, with the primary purpose front-loaded and no filler. The conditional example earns its place by resolving a likely misreading.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter query with an output schema, the description covers what the tool returns, when to call it, and a non-obvious output field interaction. Nothing essential for invoking it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the ticker schema description is rich (case-insensitivity, prefix conventions, bare BTC/ETH are ETFs). The main description adds no additional parameter semantics, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with 'For one ticker, what we hold and how far back,' which names the resource (one ticker's coverage) and the specific output (coverage depth/history). The empty-result clarification distinguishes it from data-retrieval siblings like get_bars/get_series, so an agent can route correctly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs: 'Ask this before treating a gap in bars or series as an outage,' a concrete when-to-use condition. It doesn't name the sibling tools to avoid, but the reference to bars/series is sufficient context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_universeAInspect
Returns the universe doc. Use top_10/top_100 to fetch a system universe; any other slug must be one your account owns.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Universe slug. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | The slug — the universe's handle in `?universe=`. |
| name | Yes | Display label. |
| size | Yes | Member count. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| system | Yes | `true` for a built-in universe, `false` for one you created. |
| tickers | Yes | Members. |
| created_at | Yes | Creation timestamp. |
| updated_at | Yes | Last modification timestamp. |
| description | Yes | Free-form notes; `""` when unset. |
| effective_at | No | System universes only: when this membership took effect. |
| rebalance_method | No | System universes only: how membership is chosen. |
| next_rebalance_at | No | System universes only: when membership is next recomputed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. 'Returns the universe doc' implies a read-only operation and the ownership constraint is stated, but failure behavior (e.g., what happens for a nonexistent or unauthorized slug), permissions, or side effects are not covered. This is minimally adequate for a simple GET-like tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with no wasted words. The main action ('Returns the universe doc') is front-loaded, and the slug ownership constraint immediately follows. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values are already specified. The description covers the key operational constraint (slug ownership) and the special-case system universes. It could mention using tickerbot_list_universes to discover owned slugs, but that is more of a usage alternative than a missing requirement for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value by explaining that top_10/top_100 are special system slugs and that other slugs must be owned by the account, which goes beyond the schema's bare 'Universe slug' description and helps the agent choose valid inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Returns') and resource ('the universe doc'), and clarifies the two categories of valid slugs (system universes like top_10/top_100 vs. account-owned). This distinguishes the tool from sibling list/create/delete universe tools and gives the agent a clear idea of what to expect.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete guidance on acceptable slug values (system universes vs. account-owned) and implies that only owned slugs are fetchable. However, it does not explicitly name alternative tools (e.g., tickerbot_list_universes for enumeration or tickerbot_create_universe for creating), so the guidance stops short of explicit when/not-alternatives routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_get_webhookCInspect
The current state of one webhook subscription.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.). |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. 'Current state' hints that the operation is read-only, but it does not explicitly state that it makes no modifications, does not require special permissions, or can fail if the webhook does not exist. Error behavior, rate limits, and side effects are entirely undisclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, short, front-loaded sentence with no filler or redundancy. It is structurally concise, though it sacrifices valuable content like a verb and usage context. Given its brevity and lack of waste, a 4 is appropriate.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter getter with an output schema, the description is still incomplete. It omits usage guidance and explicit read-only behavior, and does not explain how this tool fits with related tools. Even with the schema covering parameters and returns, the description fails to provide enough context for an agent to decide when to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The lone parameter 'id' is fully described in the input schema, which already explains that it comes from a subscribe endpoint. With 100% schema description coverage, the description does not need to add parameter details; it only vaguely refers to 'one webhook' without adding meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description identifies the resource (webhook subscription) and singular scope, which helps distinguish it from list_webhooks, but it is phrased as a noun phrase ('The current state...') rather than an action verb like 'Retrieves' or 'Gets'. The intended operation is only implied by the tool name, making the purpose somewhat vague.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus its siblings. It does not mention that it fetches a single webhook by ID, nor does it recommend alternatives like list_webhooks for multiple webhooks. The description gives no contextual signal to help an agent choose this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_eventsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | 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 | No | 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 | No | Aggregate-mode sort direction. | desc |
| firm | No | Analyst-only structured filter — requires `kind=analyst` alone (`400` otherwise). Exact firm-name match on the ratings feed. | |
| from | No | 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 | No | Set to `state` to allow ticker-state signals in `q`/`select`/`group_by`/`having`, evaluated as of each event's timestamp (daily resolution). | |
| kind | No | Comma list of kinds. Omitted → the five corporate kinds; `signal` and `news` join only when named here. | |
| limit | No | Page size (row modes) / max rollup rows (aggregate mode). Max 1000. | |
| order | No | 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 | No | Analyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions. | |
| cursor | No | Opaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`. | |
| having | No | Post-aggregation filter. Requires `group_by`. | |
| select | No | 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 | No | 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 | No | Single-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins. | |
| tickers | No | Comma list of tickers (max 50). Mutually exclusive with `universe`. | |
| group_by | No | 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 | No | 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`. | auto |
| universe | No | Universe slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`. | |
| transition | No | Signal-only filter — requires `kind=signal` alone. `enter` (false→true) or `exit` (true→false). |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| query | Yes | 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 | Yes | One row per event (`ticker`, `ts`, `kind`, `payload`), or rollup rows plus `truncated: true` when an aggregate exceeds `limit`. |
| truncated | No | 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 | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It discloses several important behaviors: case-sensitivity rules ('`firm`/`action` match case-insensitively; a `q` payload match is case-sensitive'), the `join: state` behavior ('attaches the ticker state as of each event'), and the interval resolution behavior ('`auto` resolves to `1d`... events on tickers absent from that tier join to `null`'). It also discloses error conditions (400 for firm without kind=analyst, 400 for signal without kind=signal). This is substantial behavioral context beyond what the schema provides.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact for a tool with 20 parameters. It front-loads the core purpose in the first sentence, then states the critical bound requirement, then adds the case-sensitivity and join behavior. Every sentence adds value. It could be slightly more structured (e.g., separating the bound requirement into its own paragraph), but it's efficient and well-ordered.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (20 parameters, 6 enums, aggregate vs row modes, join=state, signal-only filters), the description covers the most critical cross-cutting behaviors: the bound requirement, case-sensitivity, join semantics, and interval resolution. The output schema exists, so return values are covered. The description doesn't explicitly mention pagination or cursor behavior, but the schema documents the cursor parameter. It's complete enough for an agent to call this tool correctly in most cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 20 parameters thoroughly. The description adds some cross-cutting semantic context: the bound requirement (ticker scope, time window, or firm/action), the case-sensitivity distinction, and the join=state behavior. However, most parameter-level semantics are already in the schema, so the description's incremental value is moderate. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific statement: 'One timeline across every ticker: earnings, dividends, splits, insider filings, analyst actions, plus opt-in signal firings and news.' This names the resource (events), the verb (list), and the scope (across every ticker), and enumerates the event kinds. It distinguishes itself from siblings like get_bars, get_series, and search_news by focusing on the unified event timeline.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states the requirement: '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.' It also explains when to use alternatives implicitly by describing what this tool covers (events) versus siblings like get_bars (price bars) and search_news (news search). The bound requirement is a clear usage rule that prevents invalid calls.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_signalsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Filter by kind (`expression` accepted as a legacy alias for `custom`). Omit to return both. | |
| limit | No | Page size for the custom-signal slice. Max 200. | |
| cursor | No | Opaque cursor from a prior response. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page — the canonical count, equal to `count_builtin` + `count_custom`. |
| signals | Yes | 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 | No | 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 | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
| count_custom | Yes | Your custom signals. |
| count_builtin | Yes | Built-in signals in the catalog. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of behavioral disclosure. It adds useful context by noting that custom signals appear with 'kind: custom' and that the result is a single combined catalog, but it does not explicitly state non-mutating behavior or pagination semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences and front-loads the main purpose before the usage guidance. The first phrase is slightly stylized and partially redundant with 'built-in signals and custom signals', but overall it is compact and not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a simple list tool with an output schema and fully documented optional parameters. The description adds the essential use case and the kind-labeling behavior, making it complete enough for an agent to call correctly without further inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds no significant parameter-level meaning beyond what the schema already provides; mentioning 'kind: custom' merely reflects the existing enum description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as a catalog of every queryable signal, both built-in and custom, which distinguishes it from single-signal tools like tickerbot_get_signal. The phrase 'use to discover the signal names and q vocabulary before composing a scan' gives a specific purpose and resource scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use the tool: before composing a scan, to discover signal names and the q vocabulary. It does not explicitly exclude alternatives or mention when not to use it, but the context is clear enough for an agent to select it appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_tickersAInspect
Every symbol we track, active or delisted, as one identity row each. Use /v2/tickers/{ticker} for the full row.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size. Max 1000. | |
| cursor | No | 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 | No | 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 | No | 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 | No | 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 | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| results | Yes | 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 | Yes | Opaque token for the next page; `null` on the last page. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of behavioral disclosure. It usefully reveals that delisted symbols are included and that rows are identity-only rather than full records. However, it does not mention default page size, pagination, or the read-only nature of the call; the schema covers some of this, but the description itself is minimally transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no redundancy. The core behavior is front-loaded, and the pointer to the full-row endpoint earns its place as a disambiguator.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists and the input schema documents all six optional parameters, the description does not need to restate return values or parameter details. It supplies the missing context: identity-row semantics, delisted coverage, and where to get full data. It is slightly incomplete only in not contrasting this tool with other list/scan siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description adds no parameter-level meaning beyond what the schema already provides. It neither clarifies parameter behavior nor introduces constraints, so it does not deserve a higher score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the resource (every symbol tracked), the scope (active or delisted), and the granularity (one identity row each), which is specific and informative. It also distinguishes this list operation from the full-row retrieval via `/v2/tickers/{ticker}`, matching the sibling `get_ticker` tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use the list: when you want identity rows for tracked symbols, active or delisted. It explicitly points to `/v2/tickers/{ticker}` for the full row, giving a clear alternative for a different need. It does not enumerate other alternatives like `scan` or `get_ticker_coverage`, so it falls just short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_universesAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size (applies to your own). Max 100. | |
| owner | No | Which universes to list: `me` (your own), `system` (built-ins), or `all` (both). | me |
| cursor | No | Opaque cursor from the previous response. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| universes | Yes | The universes in scope. Every row carries `system: true|false`. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It explains the meaning of `owner: system` and `all`, and notes that `limit` applies to your own universes. However, it does not disclose pagination behavior beyond the cursor parameter, or whether the response includes metadata like total counts. The description is honest and consistent, but not deeply behavioral.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loads the core purpose, and packs the key distinctions (`owner: system`, `all`, slug usage) into a compact space. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema, so return values are already documented. The description covers the purpose, the owner semantics, and the downstream use of the result. It could mention pagination behavior more explicitly, but the cursor parameter and output schema cover the essentials.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters. The description adds context by explaining the semantic difference between `me`, `system`, and `all`, and by noting that `limit` applies to your own universes. This is useful but not a major addition beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists universes (both user-owned and built-in), distinguishes the `owner` values, and explains the relationship to other tools. It uses a specific verb ('list') and resource ('universes'), and differentiates from siblings like `tickerbot_get_universe` by describing the enumeration behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly explains when to use this tool: to reference universes, and how the `owner` parameter selects between `me`, `system`, and `all`. It also tells the agent that the resulting slug is used as `universe` on scan, signal, and subscribe tools, which is actionable guidance for downstream usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_webhook_deliveriesAInspect
Recent deliveries for one webhook — what was sent, and what came back.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id. | |
| to | No | 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 | No | 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 | No | Page size. Max 100. | |
| cursor | No | Opaque cursor. | |
| status | No | Filter by delivery status. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| deliveries | Yes | Attempts, newest first: status, attempt, response code, error, and `body_string` — the exact JSON POSTed. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden. It communicates a read-style listing operation and that each delivery includes the sent payload and the response, which is useful. It does not disclose side effects, auth requirements, rate limits, or retention details, though 'recent' hints at a time-bounded result.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler. Every phrase earns its place: 'recent', 'for one webhook', and 'what was sent, and what came back' all help an agent select and expect the tool's behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The rich input schema covers the date filters, pagination, default, and status enum, and the output schema covers return shape, so the short description does not need to restate those. A brief note about read-only semantics or when to first call list_webhooks would round it out, but the core invocation context is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema already documents every parameter. The description adds little parameter-specific meaning beyond 'one webhook' mapping to the required id. This is the baseline case where the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the resource as 'deliveries for one webhook', which distinguishes it from sibling list tools such as tickerbot_list_webhooks. The phrase 'what was sent, and what came back' also sets expectations about the returned delivery history. It is clear but does not explicitly state the 'list' verb or name an alternative tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrasing 'for one webhook' implies the correct use case: an agent should call this when it already has a webhook id and wants delivery history. However, the description does not provide explicit when-to-use or when-not-to-use guidance, nor does it mention alternative tools for inspecting webhook configuration.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_list_webhooksBInspect
Every webhook subscription on this account, newest first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size. Max 100. | |
| cursor | No | Opaque cursor from the previous response. | |
| status | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| webhooks | Yes | Your subscriptions, newest first, each with its `subscription_origin` and health fields. `signing_secret` is stripped — it is shown only on create. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the behavioral disclosure burden. It does disclose two genuine behaviors: account-level scope and newest-first ordering. However, it says nothing about pagination mechanics (limit/cursor loop), the default page size of 50, or the read-only safety profile, and the nuanced 'disabled covers user pause and auto-disable' semantics live only inside the schema's status parameter, not the description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Seven words in a single sentence with zero filler. The resource, scope, and ordering are all front-loaded, and every word earns its place. This is appropriately concise rather than under-specified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and 100% parameter schema coverage, return values and inputs are already handled. The remaining gaps are routing guidance (when to choose this over list_webhook_deliveries or get_webhook) and explicit pagination-loop behavior, though 'Opaque cursor from the previous response' implies it. For a simple list endpoint this is adequate but leaves clear decision-making gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline of 3 applies. All three parameters (limit, cursor, status) are well-documented in the schema, including the opaque-cursor contract and the nuanced status enum semantics. The description itself adds nothing about parameters, but the schema fully compensates, so no deduction is warranted.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource precisely ('webhook subscription'), its scope ('on this account'), and its ordering ('newest first'), which clearly communicates what the tool returns. It is implicitly distinguishable from siblings: get_webhook (singular), list_webhook_deliveries (delivery records, not subscriptions), and the create/delete/update/enable/test mutation tools. A small deduction because it is a noun phrase rather than an explicit verb phrase ('Returns every webhook…'), and it does not name the sibling distinctions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance. The description never mentions alternatives, exclusions, or the condition that would select this tool over get_webhook or list_webhook_deliveries. The appropriate use case is only implied by the tool name and the nouns in the description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_scanAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | 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 | No | Sort direction. | desc |
| asof | No | 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 | No | Return every signal instead of the default set. Mutually exclusive with `columns` — passing both is a 400. | |
| limit | No | 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 | No | 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. | change_1d_pct |
| cursor | No | Opaque cursor from the previous response's `next_cursor`. Row mode only. | |
| having | No | Filter the aggregate rows (requires `group_by`). Custom signals are valid here too. | |
| select | No | 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 | No | 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 | No | 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 | No | 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. | auto |
| universe | No | Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,072 tracked tickers. | |
| asset_class | No | 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`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| _meta | No | `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`. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| query | Yes | Your query, echoed — `q`, `order`, `dir`, `limit`, and any scope. |
| results | Yes | One row per match — every signal on the schema page, plus any you named. |
| truncated | No | 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 | No | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does disclose meaningful behavior: temporal reads via asof, the flat/restricted grammar (no JOIN or subqueries), and the mode shift to rollup rows with group_by. It does not explicitly state that this is a read-only operation, nor cover side effects, rate limits, or error behavior, though the schema covers most error contracts.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Five tight sentences with no filler: the core purpose is front-loaded first, followed by grammar constraints, the group_by caveat, and a concrete example. Every sentence carries distinct information and none repeats the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 14-parameter tool with two major modes (row and aggregate) plus asof, the description covers the headline semantics well: the query grammar, temporal reads, and aggregate-mode behavior. With a rich output schema present and 100% parameter coverage, the remaining concepts (pagination, having/select, universe scoping) are adequately handled by the schema, so the description selects the right high-value concepts to explain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description clearly exceeds it: it explains the q grammar in detail (AND/OR/NOT, comparisons, literals, custom signal expansion, no JOIN/subqueries) and adds meaning to group_by and asof that the schema's short field descriptions only hint at. The example ties q, signals, and semantics together in a way the schema alone cannot.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear resource (tickers) and action (return every ticker matching a SQL WHERE clause), with a concrete example. It distinguishes itself behaviorally by noting that group_by produces rollup rows instead of tickers, which separates it from listing/getting tools among the siblings, though it never names a sibling explicitly and the verb is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied through the grammar explanation and the concrete example (`gap_up AND market_cap < 2000000000 AND NOT earnings_this_week`), which conveys that this tool screens tickers by signal conditions. However, there is no explicit when-to-use versus when-not-to-use guidance, no mention of alternatives like subscribe_scan or list_tickers, and no exclusions stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_search_newsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | 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 | No | Articles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.) | |
| dir | No | Sort direction. | desc |
| from | No | Earliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. (`since` accepted as an alias.) | |
| limit | No | Page size. Max 1000. | |
| order | No | 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 | No | Opaque pagination cursor from a prior response's `next_cursor`. | |
| having | No | HAVING clause on the aggregate (max 1000 chars). Requires `group_by`. | |
| search | No | 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 | No | 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 | No | Articles mentioning this symbol (ANDed with `q`). | |
| tickers | No | Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`. | |
| group_by | No | 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 | No | Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| count | Yes | Rows in this page. |
| query | Yes | Your filters, echoed. |
| results | Yes | Article rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list. |
| truncated | No | Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. |
| next_cursor | Yes | Opaque token for the next page; `null` on the last page. Pass it back as `cursor`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses that rollups with group_by/having return `truncated: true` instead of paging, and it exposes the SQL-query nature and the `tk` auto-unnest alias. This goes beyond the name and schema, giving the agent a real sense of how the tool behaves, though it does not mention whether the operation is read-only or any performance considerations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is five short, information-dense sentences with the core resource and operation front-loaded. Every sentence contributes a distinct fact: SQL querying, row/rollup modes, ticker filtering, full-text search, and truncation behavior. There is no filler or repetition of the detailed schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the input schema is exhaustive with 100% coverage, an output schema exists, and the description covers the non-obvious behaviors an agent must know before calling it. The key modes and the truncation-vs-paging distinction are explained, and the schema handles parameter-level details, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds some cross-parameter guidance, such as filtering via the `ticker` param or the `tk` alias in `q`, and notes that `search` is full-text over title and summary. However, most of this information is already present in the parameter schemas, so the description adds marginal semantic value beyond them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "SQL query over the news archive." It clearly states the main modes (article rows vs. rollups), full-text search behavior, and ticker filtering, which distinguishes it from sibling tools like tickerbot_list_events or tickerbot_get_series.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description strongly implies usage by explaining how to filter by ticker, use full-text search, and opt into rollups, but it never explicitly contrasts this tool with alternatives or states when not to use it. An agent can infer the use case but is not directly routed away from similar news/event/listing tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_subscribe_eventsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | 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 | No | Display name. Defaults to `events: <kinds> · <scope>`. | |
| kinds | Yes | Event kinds to fire on — array or comma list. | |
| ticker | No | Single-symbol shorthand for `tickers`. | |
| channel | No | Delivery channel. `slack` is reserved and returns `501`. | |
| event_q | No | Optional event-CONTENT filter in the `/v2/events` grammar — only `ticker`, `ts`, `kind`, `payload` may appear. Composes with `q`. | |
| tickers | No | 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 | No | Scope to a universe slug (`top_10`, `top_100`, or one of yours). `universe_id` accepted as an alias. | |
| device_id | No | Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`. | |
| target_url | No | HTTPS delivery URL; or use `channel` + `discord_url`/`device_id`. Omit for in-app. | |
| discord_url | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| test_url | Yes | Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| channel_config | Yes | Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id). |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| signing_secret | Yes | Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden. It discloses the push model, paid-plan restriction, the distinction between ticker STATE and EVENT payload filters, and that latency follows the ingest cadence rather than being sub-minute. Retries, auth, and rate limits are not covered, but the key operational traits are present.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences with the core push behavior front-loaded, followed by the pricing constraint and the subtle q/event_q and latency distinctions. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter subscription tool with a full input schema and an output schema, the description covers the non-obvious behavior an agent would need: push semantics, paid-plan requirements, filter semantics, and delivery latency. Nothing needed to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value by disambiguating `q` ('filters the ticker STATE') from `event_q` ('filters the EVENT payload in the /v2/events grammar') and by explaining latency in a way the schema does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with 'Push new events: we POST your endpoint when events of the kinds you chose land in the archives,' which names a specific verb, resource, and triggering condition. This clearly distinguishes it from sibling scan/signal/ticker subscription tools by focusing on event-kind delivery.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description sets a concrete precondition with 'Webhooks need a paid plan (Free has no webhook slots)' and clarifies two filtering modes via `q` vs `event_q`. It does not explicitly name sibling subscribe_scan/subscribe_signal/subscribe_ticker, so exclusions remain implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_subscribe_scanAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | 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 | No | Sort direction for `order`. | desc |
| name | No | Human-readable label (up to 80 chars). Defaults to `scan: <q>`. | |
| order | No | 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). | market_cap |
| cadence | No | `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 | No | 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 | No | 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 | No | 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 | No | Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`. | |
| target_url | No | https:// URL to POST when the match set changes. Omit for in-app delivery. | |
| discord_url | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| test_url | Yes | Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| channel_config | Yes | Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id). |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| signing_secret | Yes | Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden. It discloses the core behavior—every match-set change triggers a POST to the endpoint—and the paid-plan gating for webhooks. However, it does not mention authentication requirements, rate limits, how to cancel the subscription, or the fact that non-webhook channels are also supported. The description adds some behavioral context but leaves several operational traits undisclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two tight sentences with no filler. It front-loads the primary behavior, then adds the critical plan constraint and usage phrase. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity—11 parameters, full schema coverage, enums, aliases, and an output schema—the description does enough to orient the agent around the subscription model and the paid-plan constraint. It could mention lifecycle management or alternative delivery channels, but the schema already covers channel mechanics and the output schema handles return values, so the remaining gaps are minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds the high-level concept that the `q` parameter is a full query and that the tool pushes results, but it does not add parameter-level detail beyond the schema. The schema itself provides thorough explanations for q, channel, cadence, ordering, and aliases.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's action: it pushes a full query and POSTs to the user's endpoint whenever the match set changes. The phrase 'whole query' distinguishes it from single-signal or ticker subscriptions, and 'Use for alert me when this happens' reinforces the use case. It does not explicitly name sibling tools, but the intent is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a clear usage context: use this for alert-style requests that fire when scan results change. It also gives a concrete prerequisite/limitation: webhooks require a paid plan, and Free has no webhook slots. It stops short of explicitly saying when not to use it or naming alternatives like subscribe_signal, so it misses the top tier.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_subscribe_signalAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| dir | No | Sort direction for `order`. | desc |
| name | No | 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 | No | 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). | market_cap |
| signal | Yes | Signal name from the schema (case-insensitive). | |
| ticker | No | Restrict to a single ticker. Default: any ticker. | |
| cadence | No | `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 | No | 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 | No | 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 | No | 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 | No | 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 | No | Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`. | |
| target_url | No | https:// URL to POST when fired. Omit for in-app delivery. | |
| discord_url | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| test_url | Yes | Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| channel_config | Yes | Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id). |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| signing_secret | Yes | Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the burden and does state the key behavioral effects: the system POSTs to the user's endpoint whenever a ticker starts matching the signal. It also names the delivery mechanism and the paid-plan limitation for webhooks. It could add persistence/lifecycle details, but the core trigger-and-deliver behavior is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with no filler. The core behavior is front-loaded, followed by the most important constraint and a useful usage tip.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich 100%-covered input schema and presence of an output schema, the description does not need to re-explain every parameter. It covers the high-level purpose, a key constraint, and the universe-scoping default, which is sufficient context for a 13-parameter tool whose details live in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and most parameter meaning comes from the schema. The description adds only two small param-related notes—webhook plans and the ticker-omission default—neither of which materially changes parameter understanding beyond schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening 'Push one signal: we POST your endpoint whenever any ticker starts matching it' clearly identifies the verb (subscribe/push), resource (a signal), and trigger condition. It also distinguishes this from sibling ticker/scan/event subscription tools by emphasizing signal matching rather than a fixed ticker or scan.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives useful context by warning that webhooks require a paid plan and explaining how to broaden scope with 'Omit `ticker` to watch the whole universe.' It does not explicitly say when to choose this tool over subscribe_ticker, subscribe_scan, or subscribe_events, so the usage guidance is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_subscribe_tickerAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | WHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.) | |
| dir | No | Sort direction for `order`. | desc |
| name | No | Human-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`. | |
| order | No | 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). | market_cap |
| ticker | Yes | 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 | No | 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 | No | 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 | No | 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 | No | Original name for `q` — accepted as well. The same WHERE-clause fragment; send either spelling. | |
| device_id | No | Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`. | |
| target_url | No | https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard). | |
| discord_url | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| test_url | Yes | Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| channel_config | Yes | Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id). |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| signing_secret | Yes | Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It discloses the POST-on-match behavior, the paid-plan webhook restriction, and the in-app delivery fallback. It doesn't mention rate limits, idempotency, or what happens on duplicate subscriptions, but for a subscription-creation tool the disclosed behaviors are the critical ones.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, zero filler. The first sentence states the action and trigger, the second gives the key constraint (paid plan), and the third gives the alternative path (in-app). Every sentence earns its place and the most important behavioral facts are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter tool with 100% schema coverage and an output schema, the description covers the essential behavioral context: what triggers a POST, the paid-plan gate, and the in-app fallback. It doesn't discuss idempotency or duplicate handling, but the schema's rich parameter documentation and output schema carry the remaining load. A 4 is appropriate given the complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining the core delivery contract (POST when condition matches), clarifying the target_url omission behavior, and noting the paid-plan constraint. It doesn't restate every parameter, but it adds meaning to the most important ones (target_url, q, ticker) and the schema already covers the rest.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Push one ticker') and immediately states the core behavior: POST to your endpoint when the condition matches. It distinguishes itself from siblings like subscribe_scan and subscribe_events by being ticker-specific, and the webhook vs in-app delivery distinction adds further clarity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool (push one ticker with a condition), and provides clear guidance on alternatives: omit target_url for in-app delivery, and the paid-plan webhook requirement tells the agent when this tool is not viable. The schema further clarifies channel inference rules, but the description itself already gives actionable usage direction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_test_webhookAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Delivery id of the test — `dl_…`, the same shape as a real delivery on the deliveries route. |
| test | Yes | `true` — this delivery was a synthetic ping, not a trigger firing. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| error | Yes | Why delivery failed, in words; `null` on success. |
| status | Yes | The delivery record's status: `delivered` or `permanent_failure` (a test is never retried). |
| delivered | Yes | Whether your endpoint accepted the ping (2xx within the timeout). |
| created_at | Yes | When the ping was sent (ISO 8601). |
| elapsed_ms | Yes | Round-trip time of the ping. |
| webhook_id | Yes | The webhook that was tested. |
| http_status | Yes | The status your endpoint returned; `null` when it could not be reached. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the disclosure burden and does so well: it states the request is a real POST, is one-shot, never retries, never auto-disables the webhook, and fails with 400 if target_url is missing. These details go beyond the schema and name, giving an agent accurate side-effect expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with the main action front-loaded and each subsequence sentence adding a distinct behavioral detail. There is no redundant phrasing or unnecessary repetition of schema information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with an output schema, the description covers the core purpose, the error condition, and important side-effect behavior. It does not mention whether the webhook must be enabled or permissions required, but these are minor gaps given the tool's low complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The sole required parameter 'id' is already fully described in the schema as 'Webhook id' with 100% schema coverage, so the description does not need to repeat it. The description adds no additional paramter semantics, which matches the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description leads with a specific action ('Send a real-shape test POST') against a specific resource ('your endpoint'), making it clear this is a webhook testing tool. It also distinguishes itself from sibling webhook tools by emphasizing it is a one-shot live test rather than configuration or inspection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The tool implies it should be used when you want to immediately validate a webhook endpoint, but it never explicitly states when to use it instead of alternatives like list_webhook_deliveries or create_webhook. It gives clear context in the first sentence but omits when-not-to-use guidance and alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_update_custom_signalAInspect
Update a custom signal you own — its expression, description, or name.
| Name | Required | Description | Default |
|---|---|---|---|
| expr | No | 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 verbatim, not its expansion. | |
| signal | Yes | Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only. | |
| new_name | No | 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 | No | New description. Not derived from `expr` — change both if the prose describes a threshold you are moving. |
Output Schema
| Name | Required | Description |
|---|---|---|
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| signal | Yes | The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the definition carries full behavioral burden. The parameter descriptions disclose important side effects: expression re-validation and re-inlining, rename changing the API handle everywhere, refusal while other signals reference the current name, and built-ins answering 404. This is strong behavioral disclosure for a mutating tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening sentence is concise and front-loaded with the action, resource, and editable fields. Each parameter description earns its place with a meaningful rule or caveat, with no filler or redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich parameter descriptions and an output schema, the definition covers ownership, built-in read-only behavior, rename side effects, expression grammar restrictions, and the relationship between description and expression. An agent has the needed information to call this tool correctly and safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the parameter descriptions add substantial extra meaning: allowed SQL grammar, forbidden constructs, exact allowed functions, alias behavior for 'name', and rename/reference constraints. These go well beyond basic type or format documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description specifies a clear action ('Update'), a specific resource ('a custom signal you own'), and the mutable fields (expression, description, or name). This clearly distinguishes it from create/delete variants and from update_universe/update_webhook without requiring the agent to open the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'you own' implies the tool is only for user-created custom signals, and the schema adds a built-in read-only exclusion via 404 behavior. However, it does not explicitly say when to prefer this over create/delete or mention alternatives for updating other resource types, leaving some usage routing implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_update_universeAInspect
Update one of your universes: its name, description, or members. tickers replaces the whole list; add/remove adjust it. System universes cannot be edited.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Universe slug. | |
| add | No | Add these tickers (deduplicated). | |
| name | No | New label. Non-empty, max 80 characters. | |
| remove | No | Remove these tickers. | |
| tickers | No | Replace the full ticker list (up to 10,000; validated against the active universe). Does not combine with `add`/`remove` (400). | |
| description | No | New notes. Max 500 characters. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | The slug — the universe's handle in `?universe=`. |
| name | Yes | Display label. |
| size | Yes | Member count. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| system | Yes | `false` — this is your universe. |
| tickers | Yes | Members, after this call. |
| created_at | Yes | Creation timestamp. |
| updated_at | Yes | Last modification timestamp. |
| description | Yes | Free-form notes; `""` when unset. |
| effective_at | No | System universes only; absent on yours. |
| rebalance_method | No | System universes only; absent on yours. |
| next_rebalance_at | No | System universes only; absent on yours. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden. It does disclose important behavior: system universes are locked from editing, and `tickers` overwrites the full list while `add`/`remove` adjust it incrementally. Missing are mutation side effects, permission requirements, or error behavior on invalid or protected universes; for a write tool this is only partial transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, no filler. The first sentence front-loads the action and scope; the second packs the mutation modes and the hard system-universe restriction. Every clause contributes meaning, making this an efficient definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the core operations, parameter semantics, and a critical business rule (system universes cannot be edited). With full parameter schema coverage and an output schema available, the remaining gaps—like explicit error handling or alternative routing—are minor but keep it from being fully complete for a mutation tool with no annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents every parameter (100% coverage), so the baseline is 3. The description adds value by grouping parameters into names/description vs members and explicitly clarifying the semantic relationship between `tickers` (replace) and `add`/`remove` (adjust), which is not obvious from individual parameter descriptions alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Update one of your universes', and enumerates the editable aspects: name, description, or members. This clearly distinguishes it from sibling create/delete/get universe tools and from update_custom_signal/update_webhook by resource type. The scoping is precise and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context on when to use the tool: updating existing universes, and provides an explicit exclusion: 'System universes cannot be edited'. It also explains the internal choice between `tickers` (replace all) and `add`/`remove` (adjust). However, it does not explicitly name alternatives like create_universe for new universes, so it stops short of full route-guiding.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tickerbot_update_webhookAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Webhook id. | |
| name | No | New display name. Non-empty, max 80 characters. | |
| cadence | No | Evaluation cadence. A user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise). | |
| enabled | No | `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 | No | 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). |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | The stored predicate. Custom signals appear expanded: the SQL is frozen at creation. |
| id | Yes | The webhook id — `wh_…`, the handle for every other call on this record. |
| dir | Yes | Sort direction for that list; `null` means the default (`desc`). |
| name | Yes | Your label for the subscription. |
| as_of | Yes | Server time this response was assembled (ISO 8601). |
| order | Yes | Sort signal for the payload row list; `null` means the evaluator default (`market_cap`). |
| fields | Yes | Extra signals carried on each fired match row; `null` means the standard set. |
| source | Yes | Which API version created the record; `v2` for anything you create today. |
| status | Yes | `active` or `disabled`. Auto-disable follows repeated delivery failure. |
| cadence | Yes | How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`. |
| channel | Yes | Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`. |
| event_q | No | Event-trigger webhooks only: the payload filter, or `null`. |
| rule_id | Yes | Legacy link to a v1 alert rule; `null` on everything created through v2. |
| delivery | Yes | Legacy alias of `channel`, kept aligned for older readers. |
| created_at | Yes | Creation timestamp. |
| last_error | No | The last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?". |
| last_fired | Yes | When a delivery last went out; `null` if it never has. |
| target_url | Yes | Your HTTPS endpoint; `null` on every channel except `webhook`. |
| updated_at | Yes | Last modification timestamp. |
| event_kinds | No | Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`). |
| universe_id | Yes | Universe the trigger is scoped to, or `null` for the whole market. |
| next_eval_at | Yes | When the evaluator will next consider this subscription. |
| trigger_kind | No | Event-trigger webhooks only: `event`. |
| event_tickers | No | Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market. |
| last_match_set | Yes | Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered. |
| last_evaluated_at | Yes | When it was last evaluated; `null` until the first run. |
| last_eval_error_at | No | When the last evaluation error happened; `null` on a healthy hook. |
| subscription_origin | Yes | Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form. |
| last_predicate_value | Yes | The trigger's value at the last evaluation; `null` until it has run. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well by disclosing partial-update semantics, immutability of trigger/channel, and the 400 on unknown fields. It could add more about auth or side effects, but the key behavioral traps are explicitly surfaced.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences with no filler. The main usage instruction is front-loaded, followed by the most important constraint and error behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the main gotchas (immutability, partial update, unknown-field rejection) and the richly described schema plus output schema fill in parameter and return details. A few edge cases like idempotency or permission requirements are not mentioned, but nothing essential is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the description does not need to explain individual parameters. The description adds general context about partial updates and unknown fields, but no specific parameter meaning beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource ('Edit a webhook in place'), and immediately defines the update scope ('send only the fields you want to change'). It also distinguishes itself from sibling tools by calling out immutable trigger/channel and directing to delete/re-create instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: partial updates are expected, and users are directed to delete and re-create when changing immutable trigger or channel fields. It does not explicitly mention alternatives like enable_webhook, but the guidance it provides is clear and actionable for the main decision.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
- Changed
tickerbot_create_webhook1 field changed- changed
Input schema / properties / trigger / properties / condition / descriptionPrevious value: -"signal: required for numeric signals — a single bound like `>70`; sending one with a boolean or custom signal returns 400 (it does not apply). ticker: accepted as the original alias of `trigger.q`."New value: +"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`."
- Changed
tickerbot_get_signal2 fields changed- changed
Input schema / properties / condition / descriptionPrevious value: -"Required for numeric signals. Single bound, format `<op><value>`. Operators: `>`, `>=`, `=`, `!=`, `<`, `<=`. Examples: `>70`, `<=200`, `!=0`. Sending one with a boolean or custom signal returns 400 (it does not apply)."New value: +"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)." - changed
Input schema / properties / signal / descriptionPrevious value: -"A signal name. Booleans (e.g. `golden_cross`, `above_sma_50`) are detected automatically; numerics (e.g. `rsi_14`, `market_cap`, `pe_ratio`) require a condition."New value: +"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`."
- Changed
tickerbot_subscribe_signal1 field changed- changed
Input schema / properties / condition / descriptionPrevious value: -"Required for numeric signals. Shape: `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`. Sending one with a boolean or custom signal returns 400 (it does not apply)."New value: +"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)."
1 tool update
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / universe / descriptionPrevious value: -"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,066 tracked tickers."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,072 tracked tickers."
1 tool update
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / universe / descriptionPrevious value: -"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,053 tracked tickers."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,066 tracked tickers."
1 tool update
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / universe / descriptionPrevious value: -"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,040 tracked tickers."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,053 tracked tickers."
29 tool updates
- Changed
tickerbot_create_custom_signal1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "signal": { + "description": "The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.", + "type": "object" + } + }, + "required": [ + "as_of", + "signal" + ], + "type": "object" +}
- Changed
tickerbot_create_universe1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "description": { + "description": "Free-form notes; `\"\"` when unset.", + "type": "string" + }, + "effective_at": { + "description": "System universes only; absent on yours.", + "type": "number" + }, + "id": { + "description": "The slug — the universe's handle in `?universe=`.", + "type": "string" + }, + "name": { + "description": "Display label.", + "type": "string" + }, + "next_rebalance_at": { + "description": "System universes only; absent on yours.", + "type": "number" + }, + "rebalance_method": { + "description": "System universes only; absent on yours.", + "type": "string" + }, + "size": { + "description": "Member count.", + "type": "number" + }, + "system": { + "description": "`false` — this is your universe.", + "type": "boolean" + }, + "tickers": { + "description": "Members, after this call.", + "items": { + "type": "string" + }, + "type": "array" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "description", + "tickers", + "size", + "system", + "created_at", + "updated_at" + ], + "type": "object" +}
- Changed
tickerbot_create_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "channel_config": { + "description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).", + "type": "object" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "signing_secret": { + "description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "test_url": { + "description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at", + "channel_config", + "signing_secret", + "test_url" + ], + "type": "object" +}
- Changed
tickerbot_enable_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at" + ], + "type": "object" +}
- Changed
tickerbot_get_bars1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "adjusted": { + "description": "Whether the bars are split-adjusted — `true` unless you passed `adjusted=false`.", + "type": "boolean" + }, + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "bars": { + "description": "OHLCV bars, chronological, in the compact array shape. Bulk requests key this by symbol instead.", + "items": { + "type": "string" + }, + "type": "array" + }, + "count": { + "description": "Bars returned in single-symbol mode; the number of SYMBOLS in bulk mode.", + "type": "number" + }, + "coverage": { + "description": "Why the page looks the way it does: `covered` when bars were found, `no_data` when the vendor has none for the window, `not_in_minute_tier` when a sub-hour interval was asked of a symbol the minute store does not carry.", + "type": "string" + }, + "interval": { + "description": "The bar size served.", + "type": "string" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Absent on bulk (comma-list) requests, which are unpaged. Absent on bulk requests — page bulk symbol-by-symbol.", + "type": "string" + }, + "note": { + "description": "Present only when there is something to disclose about how the page was served: the first on-demand fetch for an untiered symbol (explains the latency; later calls are stored), or a `1s` page served from the local store because the provider could not be reached. Bulk responses carry `notes[symbol]` instead.", + "type": "string" + }, + "notes": { + "description": "Bulk (comma-list) requests only: the per-symbol disclosures, keyed by symbol, in place of `note`.", + "type": "object" + }, + "session": { + "description": "The session filter applied: `all` (default) or `regular` (09:30–16:00 ET, sub-hour intervals only).", + "type": "string" + }, + "ticker": { + "description": "The symbol you asked for.", + "type": "string" + } + }, + "required": [ + "as_of", + "ticker", + "interval", + "adjusted", + "session", + "count", + "coverage", + "bars" + ], + "type": "object" +}
- Changed
tickerbot_get_etf_holdings1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Holdings in this page.", + "type": "number" + }, + "holdings": { + "description": "Constituents, heaviest first, each with its weight.", + "items": { + "type": "string" + }, + "type": "array" + }, + "is_etf": { + "description": "Whether the symbol is an ETF, from the instrument type on its ticker record.", + "type": "boolean" + }, + "ticker": { + "description": "The ETF you asked for.", + "type": "string" + }, + "total": { + "description": "Total constituents held, before `limit`.", + "type": "number" + }, + "truncated": { + "description": "`true` when `limit` cut the list short.", + "type": "boolean" + } + }, + "required": [ + "as_of", + "ticker", + "is_etf", + "count", + "truncated", + "total", + "holdings" + ], + "type": "object" +}
- Changed
tickerbot_get_etf_sectors1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Sectors returned.", + "type": "number" + }, + "is_etf": { + "description": "Whether the symbol is an ETF, from the instrument type on its ticker record.", + "type": "boolean" + }, + "sectors": { + "description": "Sector weights, heaviest first, in the vendor's ETF-profile vocabulary.", + "items": { + "type": "string" + }, + "type": "array" + }, + "ticker": { + "description": "The ETF you asked for.", + "type": "string" + } + }, + "required": [ + "as_of", + "ticker", + "is_etf", + "count", + "sectors" + ], + "type": "object" +}
- Changed
tickerbot_get_series1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "_meta": { + "description": "Per-column `sources` (`bars`, `state`, or `custom` for your own signals; `earnings`/`statements` at 1q) and per-ticker `coverage`, plus `non_trading_days_dropped` / `transitions_only` / `from_defaulted` when they apply.", + "type": "object" + }, + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "columns": { + "description": "Columns in the response, echoed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "count": { + "description": "Rows per ticker in this page.", + "type": "number" + }, + "interval": { + "description": "The grid granularity served.", + "type": "string" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "series": { + "description": "Keyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for.", + "type": "object" + }, + "tickers": { + "description": "Symbols in the response, echoed.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "as_of", + "interval", + "tickers", + "columns", + "count", + "next_cursor", + "_meta", + "series" + ], + "type": "object" +}
- Changed
tickerbot_get_signal1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "_meta": { + "description": "With `asof` only: how the read was resolved — interval served and requested, blending, sources, frozen fields. See the as-of read below.", + "type": "object" + }, + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "condition": { + "description": "The bound you passed, echoed; `null` for boolean and custom signals.", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "results": { + "description": "Matching tickers with the signal value.", + "items": { + "type": "string" + }, + "type": "array" + }, + "signal": { + "description": "The signal you asked for.", + "type": "string" + }, + "universe": { + "description": "The universe you scoped to, echoed; `null` when unscoped.", + "type": "string" + } + }, + "required": [ + "as_of", + "signal", + "condition", + "universe", + "count", + "next_cursor", + "results" + ], + "type": "object" +}
- Changed
tickerbot_get_ticker1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "_meta": { + "description": "With `asof` only: how the read was resolved — the interval served and requested, whether rows blend intervals, sources and frozen fields. See the as-of read below.", + "type": "object" + }, + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "List form only — how many of `requested` were found.", + "type": "number" + }, + "data": { + "description": "The full ticker row — every signal on the schema page. On the list form, an object keyed by symbol, one full row each.", + "type": "object" + }, + "not_found": { + "description": "List form only — the requested symbols we do not track, in request order. An empty array when every symbol was found.", + "type": "string" + }, + "requested": { + "description": "List form only — the canonical symbols asked for, de-duplicated, in request order.", + "type": "string" + }, + "ticker": { + "description": "The symbol you asked for, normalised. Single form only.", + "type": "string" + } + }, + "required": [ + "as_of", + "data" + ], + "type": "object" +}
- Changed
tickerbot_get_ticker_coverage1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "measured_fields": { + "description": "Per-field measured depth where the backfill engine has probed — `field`, the grain it was measured at (`daily`, `hourly`, `minute`), `first_date`, `last_date`, `pct_complete`. Capped at 1500 rows.", + "items": { + "type": "string" + }, + "type": "array" + }, + "minute_tier": { + "description": "Whether this ticker is in the minute tier (`included`) and its `rank` within it. `included:false` is NOT \"no intraday data\": the object then carries `on_demand: true`, `first_call_latency` (`\"3-10s\"`) and `window_days` (31) — sub-hour bars for the symbol are fetched from the provider on first request and stored, so the first call is slow and later ones are sub-second.", + "type": "object" + }, + "name": { + "description": "Company or instrument name.", + "type": "string" + }, + "spans": { + "description": "Per resolution — `oldest`, `newest`, `rows`.", + "type": "object" + }, + "ticker": { + "description": "The symbol you asked for.", + "type": "string" + } + }, + "required": [ + "as_of", + "ticker", + "name", + "minute_tier", + "spans", + "measured_fields" + ], + "type": "object" +}
- Changed
tickerbot_get_universe1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "description": { + "description": "Free-form notes; `\"\"` when unset.", + "type": "string" + }, + "effective_at": { + "description": "System universes only: when this membership took effect.", + "type": "number" + }, + "id": { + "description": "The slug — the universe's handle in `?universe=`.", + "type": "string" + }, + "name": { + "description": "Display label.", + "type": "string" + }, + "next_rebalance_at": { + "description": "System universes only: when membership is next recomputed.", + "type": "number" + }, + "rebalance_method": { + "description": "System universes only: how membership is chosen.", + "type": "string" + }, + "size": { + "description": "Member count.", + "type": "number" + }, + "system": { + "description": "`true` for a built-in universe, `false` for one you created.", + "type": "boolean" + }, + "tickers": { + "description": "Members.", + "items": { + "type": "string" + }, + "type": "array" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "description", + "tickers", + "size", + "system", + "created_at", + "updated_at" + ], + "type": "object" +}
- Changed
tickerbot_get_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at" + ], + "type": "object" +}
- Changed
tickerbot_list_events1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Carries `q_truncated: true` alongside it when an oversized `q` could not ride the token — resend `q` on later pages.", + "type": "string" + }, + "query": { + "description": "Your filters, echoed exactly as you sent them — `q`, `select`, `group_by` and `having` come back in your spelling, not the SQL they compile to — including `join` and its grain when you passed `join=state`.", + "type": "object" + }, + "results": { + "description": "One row per event (`ticker`, `ts`, `kind`, `payload`), or rollup rows plus `truncated: true` when an aggregate exceeds `limit`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "truncated": { + "description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.", + "type": "boolean" + } + }, + "required": [ + "as_of", + "query", + "count", + "results" + ], + "type": "object" +}
- Changed
tickerbot_list_signals1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page — the canonical count, equal to `count_builtin` + `count_custom`.", + "type": "number" + }, + "count_builtin": { + "description": "Built-in signals in the catalog.", + "type": "number" + }, + "count_custom": { + "description": "Your custom signals.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "signals": { + "description": "The catalog, your custom signals first, then built-ins. Built-ins carry `kind: builtin` plus their taxonomy membership — `category`/`category_label`/`group`/`group_label` (slugs are stable, switch on those; labels are display strings) — yours carry `kind: custom` with the `expr`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "taxonomy": { + "description": "Absent when the page holds no built-ins (`kind=custom`). The signal taxonomy tree, once per response: `groups[]` in derivation-ladder order (record → behavior → indicator → company side), each with `slug`, `label`, `derivation`, `description`, and its `categories[]` (`slug`, `label`, `description`). Definitions live here and only here — rows carry pointers, never the descriptions. Omitted on `kind=custom`.", + "type": "object" + } + }, + "required": [ + "as_of", + "count", + "count_builtin", + "count_custom", + "next_cursor", + "signals" + ], + "type": "object" +}
- Changed
tickerbot_list_tickers1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page.", + "type": "string" + }, + "results": { + "description": "One identity row per symbol — the thirteen signals named above, nothing else. `active: false` rows carry `delisted_utc`; they are still addressable on the state route with `asof`.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "as_of", + "count", + "next_cursor", + "results" + ], + "type": "object" +}
- Changed
tickerbot_list_universes1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "universes": { + "description": "The universes in scope. Every row carries `system: true|false`.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "as_of", + "count", + "next_cursor", + "universes" + ], + "type": "object" +}
- Changed
tickerbot_list_webhook_deliveries1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "deliveries": { + "description": "Attempts, newest first: status, attempt, response code, error, and `body_string` — the exact JSON POSTed.", + "items": { + "type": "string" + }, + "type": "array" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + } + }, + "required": [ + "as_of", + "count", + "next_cursor", + "deliveries" + ], + "type": "object" +}
- Changed
tickerbot_list_webhooks1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "webhooks": { + "description": "Your subscriptions, newest first, each with its `subscription_origin` and health fields. `signing_secret` is stripped — it is shown only on create.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "as_of", + "count", + "next_cursor", + "webhooks" + ], + "type": "object" +}
- Changed
tickerbot_scan1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "_meta": { + "description": "`null_coverage` reports, per signal in the predicate, how many in-scope rows are NULL and therefore never evaluated — absence from `results` means \"no value\", not \"did not match\". `scope` additionally describes an explicit `universe`.", + "type": "object" + }, + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "query": { + "description": "Your query, echoed — `q`, `order`, `dir`, `limit`, and any scope.", + "type": "object" + }, + "results": { + "description": "One row per match — every signal on the schema page, plus any you named.", + "items": { + "type": "string" + }, + "type": "array" + }, + "truncated": { + "description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.", + "type": "boolean" + } + }, + "required": [ + "as_of", + "query", + "count", + "results" + ], + "type": "object" +}
- Changed
tickerbot_search_news1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "count": { + "description": "Rows in this page.", + "type": "number" + }, + "next_cursor": { + "description": "Opaque token for the next page; `null` on the last page. Pass it back as `cursor`.", + "type": "string" + }, + "query": { + "description": "Your filters, echoed.", + "type": "object" + }, + "results": { + "description": "Article rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list.", + "items": { + "type": "string" + }, + "type": "array" + }, + "truncated": { + "description": "Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap.", + "type": "boolean" + } + }, + "required": [ + "as_of", + "query", + "count", + "next_cursor", + "results" + ], + "type": "object" +}
- Changed
tickerbot_subscribe_events1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "channel_config": { + "description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).", + "type": "object" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "signing_secret": { + "description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "test_url": { + "description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at", + "channel_config", + "signing_secret", + "test_url" + ], + "type": "object" +}
- Changed
tickerbot_subscribe_scan1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "channel_config": { + "description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).", + "type": "object" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "signing_secret": { + "description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "test_url": { + "description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at", + "channel_config", + "signing_secret", + "test_url" + ], + "type": "object" +}
- Changed
tickerbot_subscribe_signal1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "channel_config": { + "description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).", + "type": "object" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "signing_secret": { + "description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "test_url": { + "description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at", + "channel_config", + "signing_secret", + "test_url" + ], + "type": "object" +}
- Changed
tickerbot_subscribe_ticker1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "channel_config": { + "description": "Returned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).", + "type": "object" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "signing_secret": { + "description": "Returned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "test_url": { + "description": "Returned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at", + "channel_config", + "signing_secret", + "test_url" + ], + "type": "object" +}
- Changed
tickerbot_test_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "created_at": { + "description": "When the ping was sent (ISO 8601).", + "type": "string" + }, + "delivered": { + "description": "Whether your endpoint accepted the ping (2xx within the timeout).", + "type": "boolean" + }, + "elapsed_ms": { + "description": "Round-trip time of the ping.", + "type": "number" + }, + "error": { + "description": "Why delivery failed, in words; `null` on success.", + "type": "string" + }, + "http_status": { + "description": "The status your endpoint returned; `null` when it could not be reached.", + "type": "number" + }, + "id": { + "description": "Delivery id of the test — `dl_…`, the same shape as a real delivery on the deliveries route.", + "type": "string" + }, + "status": { + "description": "The delivery record's status: `delivered` or `permanent_failure` (a test is never retried).", + "type": "string" + }, + "test": { + "description": "`true` — this delivery was a synthetic ping, not a trigger firing.", + "type": "boolean" + }, + "webhook_id": { + "description": "The webhook that was tested.", + "type": "string" + } + }, + "required": [ + "as_of", + "id", + "webhook_id", + "test", + "delivered", + "http_status", + "elapsed_ms", + "error", + "status", + "created_at" + ], + "type": "object" +}
- Changed
tickerbot_update_custom_signal1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "signal": { + "description": "The stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.", + "type": "object" + } + }, + "required": [ + "as_of", + "signal" + ], + "type": "object" +}
- Changed
tickerbot_update_universe1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "description": { + "description": "Free-form notes; `\"\"` when unset.", + "type": "string" + }, + "effective_at": { + "description": "System universes only; absent on yours.", + "type": "number" + }, + "id": { + "description": "The slug — the universe's handle in `?universe=`.", + "type": "string" + }, + "name": { + "description": "Display label.", + "type": "string" + }, + "next_rebalance_at": { + "description": "System universes only; absent on yours.", + "type": "number" + }, + "rebalance_method": { + "description": "System universes only; absent on yours.", + "type": "string" + }, + "size": { + "description": "Member count.", + "type": "number" + }, + "system": { + "description": "`false` — this is your universe.", + "type": "boolean" + }, + "tickers": { + "description": "Members, after this call.", + "items": { + "type": "string" + }, + "type": "array" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "description", + "tickers", + "size", + "system", + "created_at", + "updated_at" + ], + "type": "object" +}
- Changed
tickerbot_update_webhook1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "as_of": { + "description": "Server time this response was assembled (ISO 8601).", + "type": "string" + }, + "cadence": { + "description": "How often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.", + "type": "string" + }, + "channel": { + "description": "Where deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.", + "type": "string" + }, + "created_at": { + "description": "Creation timestamp.", + "type": "number" + }, + "delivery": { + "description": "Legacy alias of `channel`, kept aligned for older readers.", + "type": "string" + }, + "dir": { + "description": "Sort direction for that list; `null` means the default (`desc`).", + "type": "string" + }, + "event_kinds": { + "description": "Event-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).", + "items": { + "type": "string" + }, + "type": "array" + }, + "event_q": { + "description": "Event-trigger webhooks only: the payload filter, or `null`.", + "type": "string" + }, + "event_tickers": { + "description": "Event-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.", + "items": { + "type": "string" + }, + "type": "array" + }, + "fields": { + "description": "Extra signals carried on each fired match row; `null` means the standard set.", + "type": "string" + }, + "id": { + "description": "The webhook id — `wh_…`, the handle for every other call on this record.", + "type": "string" + }, + "last_error": { + "description": "The last evaluation error; `null` on a healthy hook. The answer to \"why is my webhook not firing?\".", + "type": "string" + }, + "last_eval_error_at": { + "description": "When the last evaluation error happened; `null` on a healthy hook.", + "type": "number" + }, + "last_evaluated_at": { + "description": "When it was last evaluated; `null` until the first run.", + "type": "number" + }, + "last_fired": { + "description": "When a delivery last went out; `null` if it never has.", + "type": "number" + }, + "last_match_set": { + "description": "Tickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "last_predicate_value": { + "description": "The trigger's value at the last evaluation; `null` until it has run.", + "type": "string" + }, + "name": { + "description": "Your label for the subscription.", + "type": "string" + }, + "next_eval_at": { + "description": "When the evaluator will next consider this subscription.", + "type": "number" + }, + "order": { + "description": "Sort signal for the payload row list; `null` means the evaluator default (`market_cap`).", + "type": "string" + }, + "q": { + "description": "The stored predicate. Custom signals appear expanded: the SQL is frozen at creation.", + "type": "string" + }, + "rule_id": { + "description": "Legacy link to a v1 alert rule; `null` on everything created through v2.", + "type": "string" + }, + "source": { + "description": "Which API version created the record; `v2` for anything you create today.", + "type": "string" + }, + "status": { + "description": "`active` or `disabled`. Auto-disable follows repeated delivery failure.", + "type": "string" + }, + "subscription_origin": { + "description": "Which door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.", + "type": "object" + }, + "target_url": { + "description": "Your HTTPS endpoint; `null` on every channel except `webhook`.", + "type": "string" + }, + "trigger_kind": { + "description": "Event-trigger webhooks only: `event`.", + "type": "string" + }, + "universe_id": { + "description": "Universe the trigger is scoped to, or `null` for the whole market.", + "type": "string" + }, + "updated_at": { + "description": "Last modification timestamp.", + "type": "number" + } + }, + "required": [ + "as_of", + "id", + "name", + "q", + "rule_id", + "fields", + "order", + "dir", + "universe_id", + "cadence", + "channel", + "target_url", + "delivery", + "status", + "source", + "subscription_origin", + "last_predicate_value", + "created_at", + "updated_at", + "last_fired", + "last_match_set", + "next_eval_at", + "last_evaluated_at" + ], + "type": "object" +}
4 tool updates
- Changed
tickerbot_list_events1 field changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "earnings", - "dividend", - "split", - "insider", - "analyst", - "news", - "signal" -]New value: +[ + "dividend", + "split", + "insider", + "analyst", + "earnings", + "signal", + "news" +]
- Changed
tickerbot_list_tickers1 field changed- removed
Input schema / properties / exchange / enumRemoved value: -[ - "NASDAQ", - "NYSE", - "NYSE Arca", - "Cboe BZX", - "NYSE American", - "OTC Link" -]
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / universe / descriptionPrevious value: -"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,033 tracked tickers."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,040 tracked tickers."
- Changed
tickerbot_subscribe_events1 field changed- changed
Input schema / properties / kinds / enumPrevious value: -[ - "earnings", - "dividend", - "split", - "insider", - "analyst" -]New value: +[ + "dividend", + "split", + "insider", + "analyst", + "earnings" +]
1 tool update
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / universe / descriptionPrevious value: -"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,008 tracked tickers."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,033 tracked tickers."
37 tool updates
- Changed
tickerbot_create_custom_signal3 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"Optional human description."New value: +"Free-form notes. Max 500 chars. Absent or empty comes back as `\"\"` rather than null." - changed
Input schema / properties / expr / descriptionPrevious value: -"SQL WHERE expression. Same grammar as scan `q`."New value: +"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 VERBATIM, 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." - changed
Input schema / properties / name / descriptionPrevious value: -"Snake_case identifier."New value: +"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."
- Changed
tickerbot_create_universe4 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"Optional free-form notes."New value: +"Free-form notes, up to 500 characters. Stored as `\"\"` when omitted." - changed
Input schema / properties / id / descriptionPrevious value: -"Optional slug (lowercase letters, digits, underscore). Auto-generated from name if omitted. Must be unique within the account."New value: +"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." - changed
Input schema / properties / name / descriptionPrevious value: -"Human-readable name."New value: +"Human-readable label, up to 80 characters. Display-only — never used to reference the universe." - changed
Input schema / properties / tickers / descriptionPrevious value: -"List of ticker symbols."New value: +"Ticker symbols, up to 10,000. Validated against the active universe. `[]` is accepted — a shell universe you can fill later via PATCH."
- Changed
tickerbot_create_webhook13 fields changed- added
Input schema / properties / cadence / defaultAdded value: +"realtime" - changed
Input schema / properties / cadence / descriptionPrevious value: -"Evaluation cadence. Default realtime. (`1m` accepted as a deprecated alias of realtime.)"New value: +"Evaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise)." - changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel. Inferred from the URL you pass if omitted."New value: +"Delivery channel. See Delivery channels." - changed
Input schema / properties / columns / descriptionPrevious value: -"Comma list of extra columns to include with each delivered match (`fields` accepted as alias)."New value: +"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." - changed
Input schema / properties / device_id / descriptionPrevious value: -"Registered device id from the mobile app (channel mobile_push)."New value: +"Registered device id (channel `mobile_push`, see /v2/devices)." - added
Input schema / properties / dirAdded value: +{ + "default": "desc", + "description": "Sort direction for `order`. Not accepted on `event` triggers (400).", + "enum": [ + "asc", + "desc" + ], + "type": "string" +} - changed
Input schema / properties / discord_url / descriptionPrevious value: -"Discord incoming-webhook URL (channel discord)."New value: +"Discord webhook URL (channel `discord`)." - changed
Input schema / properties / name / descriptionPrevious value: -"Display name. Defaults from the trigger."New value: +"Display name, max 80 characters. Defaults to an auto-generated one from the trigger." - added
Input schema / properties / orderAdded value: +{ + "default": "market_cap", + "description": "Signal the fired payload's match lists are sorted by before the 100-row cap is applied — so a truncated list is the deterministic top 100, not an arbitrary sample. Same contract as `POST /v2/scan`. Not accepted on `event` triggers (they deliver one event at a time).", + "type": "string" +} - changed
Input schema / properties / target_url / descriptionPrevious value: -"HTTPS delivery URL (`webhook` channel). Omit for in-app."New value: +"HTTPS delivery URL (the `webhook` channel), max 1024 characters. Omit for in-app delivery, or use `channel` + `discord_url`/`device_id` for other channels." - changed
Input schema / properties / trigger / descriptionPrevious value: -"What fires the webhook: { type: \"scan\" | \"ticker\" | \"signal\" | \"event\", … } — see the tool description for each shape."New value: +"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`." - added
Input schema / properties / trigger / propertiesAdded value: +{ + "condition": { + "description": "signal: required for numeric signals — a single bound like `>70`; sending one with a boolean or custom signal returns 400 (it does not apply). ticker: accepted as the original alias of `trigger.q`.", + "type": "string" + }, + "event_q": { + "description": "event: optional event-CONTENT filter in the `/v2/events` grammar over `(ticker, ts, kind, payload)` — e.g. `payload->>'firm' = 'Goldman Sachs'`. Composes with `trigger.q`.", + "type": "string" + }, + "kinds": { + "description": "event: required — event kinds to fire on, array or comma list (e.g. `split,analyst`).", + "type": "string" + }, + "q": { + "description": "scan: required — the SQL WHERE any ticker must match to fire. ticker: required — WHERE fragment evaluated for that ticker (auto-scoped; don't add `ticker = …` yourself; `trigger.condition` accepted as an alias). event: optional row-STATE filter on the event's ticker at fire time (`market_cap > 1e10`).", + "type": "string" + }, + "signal": { + "description": "signal: required — a built-in signal name (e.g. `rsi_14`) or one of your custom signals (custom SQL is expanded and frozen at creation).", + "type": "string" + }, + "ticker": { + "description": "ticker: required — the symbol to watch (e.g. `NVDA`). signal: optional — restrict the signal to one symbol (omit to watch the whole universe).", + "type": "string" + }, + "tickers": { + "description": "event: optional symbol list, max 50 (e.g. `AAPL,NVDA`). Mutually exclusive with `trigger.universe`.", + "type": "string" + }, + "type": { + "description": "Which trigger shape the rest of the object uses.", + "enum": [ + "scan", + "ticker", + "signal", + "event" + ], + "type": "string" + }, + "universe": { + "description": "scan / signal / event: optional universe slug (`top_10`, `top_100`, or one of yours) scoping which tickers can fire. Mutually exclusive with `trigger.tickers` on event.", + "type": "string" + } +} - added
Input schema / properties / trigger / requiredAdded value: +[ + "type" +]
- Changed
tickerbot_delete_custom_signal5 fields changed- added
Input schema / properties / force / defaultAdded value: +false - changed
Input schema / properties / force / descriptionPrevious value: -"When true, skip the reference check and delete anyway. Default false."New value: +"When `true`, skip the reference check and delete. References will break on next recompile." - removed
Input schema / properties / nameRemoved value: -{ - "description": "Custom signal slug.", - "type": "string" -} - added
Input schema / properties / signalAdded value: +{ + "description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "name" -]New value: +[ + "signal" +]
- Changed
tickerbot_delete_universe1 field changed- added
Input schema / properties / forceAdded value: +{ + "default": false, + "description": "A universe still referenced by live webhooks refuses to delete with `409 universe_referenced`. Pass `force=true` to delete anyway — those webhooks will match nothing until re-pointed or deleted.", + "type": "boolean" +}
- Changed
tickerbot_delete_webhook1 field changed- changed
Input schema / properties / id / descriptionPrevious value: -"Webhook id (looks like `wh_…`)."New value: +"Webhook id."
- Added
tickerbot_get_bars - Added
tickerbot_get_etf_holdings - Added
tickerbot_get_etf_sectors - Changed
tickerbot_get_series13 fields changed- added
Input schema / properties / asofAdded value: +{ + "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.", + "type": "string" +} - changed
Input schema / properties / columns / descriptionPrevious value: -"Comma list of columns (max 25). `fields` is a permanent alias. Defaults to a small set intersected with the interval's schema (intraday tiers carry fewer columns than daily — e.g. market_cap is daily-only)."New value: +"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." - changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor from a prior response — pages older."New value: +"Opaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates." - changed
Input schema / properties / from / descriptionPrevious value: -"Earliest timestamp (inclusive), YYYY-MM-DD or ISO."New value: +"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." - added
Input schema / properties / interval / defaultAdded value: +"1d" - changed
Input schema / properties / interval / descriptionPrevious value: -"Grid granularity. `1w` weekly, `1q` fiscal-quarterly (fundamentals)."New value: +"Grid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid." - added
Input schema / properties / limit / defaultAdded value: +252 - changed
Input schema / properties / limit / descriptionPrevious value: -"Rows per page. Max 1000. Default 252."New value: +"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." - changed
Input schema / properties / ticker / descriptionPrevious value: -"Single symbol (alias of `tickers`, wins when both are set). One of ticker/tickers is required."New value: +"Single-symbol form — `/v2/series?ticker=AAPL` is ticker history in its canonical spelling. Exactly one of `ticker` or `tickers` is required." - changed
Input schema / properties / tickers / descriptionPrevious value: -"Comma-separated symbols, max 50, all sharing one time grid. One of ticker/tickers is required."New value: +"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." - changed
Input schema / properties / to / descriptionPrevious value: -"Latest timestamp (inclusive; a bare date means through that day)."New value: +"Latest timestamp (inclusive), `YYYY-MM-DD` or ISO." - changed
Input schema / properties / transitions_only / descriptionPrevious value: -"Only rows where a boolean signal changed value (requires at least one boolean signal). Each row carries `transition_drivers` naming the booleans that flipped."New value: +"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)." - removed
Input schema / requiredRemoved value: -[]
- Added
tickerbot_get_signal - Removed
tickerbot_get_signals_match - Changed
tickerbot_get_ticker3 fields changed- changed
Input schema / properties / asof / descriptionPrevious value: -"Optional YYYY-MM-DD or ISO timestamp. Date-only returns the row at close of that day; a timestamp returns the row as of that moment (finest tier covering each column). Same meaning for one symbol or a list."New value: +"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." - added
Input schema / properties / intervalAdded value: +{ + "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.", + "enum": [ + "1m", + "1h", + "1d", + "auto" + ], + "type": "string" +} - changed
Input schema / properties / ticker / descriptionPrevious value: -"Symbol, or a comma-separated list of up to 50 (AAPL,MSFT,NVDA — a list answers `data` keyed by symbol). Case-insensitive. Equities: bare symbol (AAPL). Crypto: X-prefixed pair (X:BTCUSD) — bare BTC/ETH are US-listed ETFs, not spot crypto."New value: +"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."
- Removed
tickerbot_get_ticker_bars - Changed
tickerbot_get_ticker_coverage1 field changed- changed
Input schema / properties / ticker / descriptionPrevious value: -"Symbol."New value: +"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."
- Removed
tickerbot_get_ticker_history - Removed
tickerbot_get_ticker_holdings - Removed
tickerbot_get_ticker_sectors - Changed
tickerbot_get_webhook1 field changed- changed
Input schema / properties / id / descriptionPrevious value: -"Webhook id (looks like `wh_…`)."New value: +"Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.)."
- Changed
tickerbot_list_events25 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"kind=analyst only. Exact rating-action filter."New value: +"Analyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions." - changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor from a prior response — carries the original filters, pass it alone (long q values must be resent alongside it)."New value: +"Opaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`." - added
Input schema / properties / dir / defaultAdded value: +"desc" - changed
Input schema / properties / firm / descriptionPrevious value: -"kind=analyst only. Case-insensitive analyst-firm filter (e.g. \"Goldman Sachs\" matches \"goldman sachs\") — prefer this over a q payload match, which is case-sensitive."New value: +"Analyst-only structured filter — requires `kind=analyst` alone (`400` otherwise). Exact firm-name match on the ratings feed." - changed
Input schema / properties / from / descriptionPrevious value: -"Events at or after this ISO date/datetime (inclusive; a bare YYYY-MM-DD means from the start of that day). `since` accepted as alias."New value: +"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.)" - changed
Input schema / properties / group_by / descriptionPrevious value: -"Comma list of rollup keys — switches to aggregate rows, e.g. payload->>'firm' AS firm, or kind. `AS` names the JSON key; an un-named payload read is keyed by its payload key (payload->>'firm' -> firm)."New value: +"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`." - changed
Input schema / properties / having / descriptionPrevious value: -"Post-aggregation filter (requires group_by), e.g. COUNT(*) > 5."New value: +"Post-aggregation filter. Requires `group_by`." - added
Input schema / properties / interval / defaultAdded value: +"auto" - changed
Input schema / properties / interval / descriptionPrevious value: -"Grain for join=state replay (finest covering tier by default)."New value: +"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`." - changed
Input schema / properties / interval / enumPrevious value: -[ - "1m", - "1h", - "1d" -]New value: +[ + "1m", + "1h", + "1d", + "auto" +] - changed
Input schema / properties / join / descriptionPrevious value: -"join=state attaches each event's ticker STATE as of that event's moment (the replay join) under a `state` key — \"downgrades where rsi_14 was already under 40\" composes with q. Free on every plan."New value: +"Set to `state` to allow ticker-state signals in `q`/`select`/`group_by`/`having`, evaluated as of each event's timestamp (daily resolution)." - changed
Input schema / properties / kind / descriptionPrevious value: -"Comma list of kinds to include. Default is the four corporate kinds: dividend, split, insider, analyst. Two more are opt-in and join only when named: `signal` (boolean firings) and `news`."New value: +"Comma list of kinds. Omitted → the five corporate kinds; `signal` and `news` join only when named here." - added
Input schema / properties / kind / enumAdded value: +[ + "earnings", + "dividend", + "split", + "insider", + "analyst", + "news", + "signal" +] - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size. Max 1000."New value: +"Page size (row modes) / max rollup rows (aggregate mode). Max 1000." - removed
Input schema / properties / merge_gap_secondsRemoved value: -{ - "description": "kind=signal only, with `signal` AND `tickers` set (filter mode — no q/join/group_by). Interval-union before unpivoting: runs of the boolean separated by ≤ N seconds collapse into one, so a boolean with thousands of per-tick fragments yields one enter and one exit per real run (3600 for hourly booleans, 86400 for daily). Default 0 = raw fragments. A cursor pins this; resend it unchanged when paging.", - "type": "integer" -} - changed
Input schema / properties / order / descriptionPrevious value: -"Aggregate-mode sort column/alias. Default: events."New value: +"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.)" - changed
Input schema / properties / q / descriptionPrevious value: -"SQL WHERE over (ticker, ts, kind, payload jsonb) — ONLY those four identifiers. Payload fields via jsonb operators: payload->>'firm' = 'Goldman Sachs', (payload->>'shares')::numeric > 1e6. On kind=signal this requires `signal` (the firing log is ~175M rows); kind=signal takes no group_by."New value: +"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." - changed
Input schema / properties / select / descriptionPrevious value: -"Aggregate-mode output columns (requires group_by). Default: group keys + COUNT(*) AS events."New value: +"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." - changed
Input schema / properties / signal / descriptionPrevious value: -"kind=signal only. One built-in boolean signal (e.g. golden_cross). REQUIRED to use `q` or `join` on kind=signal — naming the signal is what keeps the query on an index; optional otherwise."New value: +"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." - changed
Input schema / properties / ticker / descriptionPrevious value: -"Single-ticker filter, e.g. AAPL."New value: +"Single-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins." - changed
Input schema / properties / tickers / descriptionPrevious value: -"Comma-separated tickers, max 50. Mutually exclusive with `universe`."New value: +"Comma list of tickers (max 50). Mutually exclusive with `universe`." - changed
Input schema / properties / to / descriptionPrevious value: -"Window end: 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 alias."New value: +"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.)" - changed
Input schema / properties / transition / descriptionPrevious value: -"kind=signal only. `enter` (false->true) or `exit` (true->false). Always optional — an ordinary filter."New value: +"Signal-only filter — requires `kind=signal` alone. `enter` (false→true) or `exit` (true→false)." - changed
Input schema / properties / universe / descriptionPrevious value: -"Universe slug (top_10, top_100, or a saved one) to scope the stream. Mutually exclusive with `tickers`."New value: +"Universe slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`."
- Added
tickerbot_list_signals - Removed
tickerbot_list_signals_catalog - Removed
tickerbot_list_system_universes - Changed
tickerbot_list_tickers9 fields changed- changed
Input schema / properties / asset_class / descriptionPrevious value: -"Filter by asset class — `stocks`, `rates`, `crypto`, `fx`, or a comma-separated list. Omit for every class. Distinct from asset_type (the instrument type within equities)."New value: +"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." - changed
Input schema / properties / asset_type / descriptionPrevious value: -"Filter by INSTRUMENT TYPE within equities: the stored value (CS, ETF, ADRC, PFD, FUND, UNIT, SP, ETS, WARRANT, RIGHT, ETN, ETV), or \"equity\" for the equity-like set. This is not an asset class — `asset_type=crypto` is rejected; use asset_class=crypto."New value: +"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`." - added
Input schema / properties / asset_type / enumAdded value: +[ + "CS", + "ETF", + "ADRC", + "PFD", + "FUND", + "UNIT", + "SP", + "ETS", + "WARRANT", + "RIGHT", + "ETN", + "ETV", + "equity" +] - changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor from a prior response."New value: +"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." - changed
Input schema / properties / exchange / descriptionPrevious value: -"Filter by exchange name (NASDAQ, NYSE, NYSE Arca, Cboe BZX, NYSE American, OTC Link) or MIC (XNAS, XNYS, BATS)."New value: +"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." - added
Input schema / properties / exchange / enumAdded value: +[ + "NASDAQ", + "NYSE", + "NYSE Arca", + "Cboe BZX", + "NYSE American", + "OTC Link" +] - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size. Max 1000. Default 50."New value: +"Page size. Max 1000." - changed
Input schema / properties / search / descriptionPrevious value: -"Case-insensitive match on ticker or name (max 64 chars). Ranked: exact ticker, then ticker prefix, then name match, alphabetical within."New value: +"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."
- Changed
tickerbot_list_universes5 fields changed- changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor."New value: +"Opaque cursor from the previous response." - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size (applies to your own; system universes are a small fixed set returned in full on the first page)."New value: +"Page size (applies to your own). Max 100." - added
Input schema / properties / owner / defaultAdded value: +"me" - changed
Input schema / properties / owner / descriptionPrevious value: -"Which universes to list."New value: +"Which universes to list: `me` (your own), `system` (built-ins), or `all` (both)."
- Changed
tickerbot_list_webhook_deliveries4 fields changed- changed
Input schema / properties / from / descriptionPrevious value: -"Only deliveries created at/after this moment — epoch seconds, epoch milliseconds (13+ digits), or ISO datetime. (90-day retention on every plan.)"New value: +"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." - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size. Max 100. Default 50."New value: +"Page size. Max 100." - changed
Input schema / properties / to / descriptionPrevious value: -"Only deliveries created at/before this moment — same value grammar as `from`; a date-only value means through the end of that UTC day."New value: +"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."
- Changed
tickerbot_list_webhooks4 fields changed- changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor."New value: +"Opaque cursor from the previous response." - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size. Max 100. Default 50."New value: +"Page size. Max 100." - changed
Input schema / properties / status / descriptionPrevious value: -"Filter by status: `active` or `disabled`. Omit for all."New value: +"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."
- Removed
tickerbot_patch_webhook - Changed
tickerbot_scan18 fields changed- changed
Input schema / properties / asof / descriptionPrevious value: -"Optional YYYY-MM-DD or ISO timestamp for a historical scan. Unlimited depth on every plan."New value: +"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." - added
Input schema / properties / asset_classAdded value: +{ + "description": "One or more asset classes — slug or comma-separated list (`stocks`, `rates`, `crypto`, `fx`). Validated for shape, not against a fixed list, so a well-formed class we don't track simply matches nothing. Echoed in `query`.", + "type": "string" +} - added
Input schema / properties / columnsAdded value: +{ + "description": "Extra signals per row, ADDITIVE — the defaults are always present (ticker, name, asset_class, asset_type, price, change_1d_pct, gap_pct, relative_volume, market_cap). `fields` accepted as an alias.", + "type": "string" +} - changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor (row mode only)."New value: +"Opaque cursor from the previous response's `next_cursor`. Row mode only." - added
Input schema / properties / dir / defaultAdded value: +"desc" - removed
Input schema / properties / fieldsRemoved value: -{ - "description": "Comma-separated extra columns to include (row mode only).", - "type": "string" -} - added
Input schema / properties / full / defaultAdded value: +false - changed
Input schema / properties / full / descriptionPrevious value: -"Row mode: return the FULL wide row for each match (every column) instead of the slim default projection."New value: +"Return every signal instead of the default set. Mutually exclusive with `columns` — passing both is a 400." - changed
Input schema / properties / group_by / descriptionPrevious value: -"AGGREGATE MODE: 1–6 comma-separated group keys (columns or expressions, e.g. `sector`). Results become rollup rows instead of tickers. Alias a key with `AS` to name its JSON key; un-named expressions are named for you."New value: +"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`." - changed
Input schema / properties / having / descriptionPrevious value: -"Aggregate filter (requires group_by). Example: `COUNT(*) >= 10`."New value: +"Filter the aggregate rows (requires `group_by`). Custom signals are valid here too." - added
Input schema / properties / intervalAdded value: +{ + "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.", + "enum": [ + "1m", + "1h", + "1d", + "auto" + ], + "type": "string" +} - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size. Max 100. Default 50. Aggregate mode does not paginate — response sets `truncated: true` when groups were cut."New value: +"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." - added
Input schema / properties / order / defaultAdded value: +"change_1d_pct" - changed
Input schema / properties / order / descriptionPrevious value: -"Sort column. Default: the 1-day change column (rows) / tickers (aggregate)."New value: +"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." - changed
Input schema / properties / q / descriptionPrevious value: -"SQL WHERE expression. Max 4000 chars."New value: +"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." - changed
Input schema / properties / select / descriptionPrevious value: -"Aggregate output items (requires group_by). Default: group keys + COUNT(*) AS tickers. Aggregates: count/avg/sum/min/max/stddev/string_agg + FILTER (WHERE …). Alias items with AS. Example: `sector, COUNT(*) AS n, AVG(rsi_14) AS avg_rsi`."New value: +"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." - changed
Input schema / properties / universe / descriptionPrevious value: -"Optional universe slug."New value: +"Slug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,008 tracked tickers."
- Changed
tickerbot_search_news16 fields changed- changed
Input schema / properties / cursor / descriptionPrevious value: -"Opaque cursor."New value: +"Opaque pagination cursor from a prior response's `next_cursor`." - added
Input schema / properties / dir / defaultAdded value: +"desc" - added
Input schema / properties / fromAdded value: +{ + "description": "Earliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. (`since` accepted as an alias.)", + "type": "string" +} - changed
Input schema / properties / group_by / descriptionPrevious value: -"Comma-separated columns for aggregation. Alias a key with `AS` to name its JSON key; un-named expressions are named for you."New value: +"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?`." - changed
Input schema / properties / having / descriptionPrevious value: -"WHERE-style filter on aggregates. Requires group_by."New value: +"HAVING clause on the aggregate (max 1000 chars). Requires `group_by`." - added
Input schema / properties / limit / defaultAdded value: +50 - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size."New value: +"Page size. Max 1000." - changed
Input schema / properties / order / descriptionPrevious value: -"Sort column or SELECT alias. Default time_published (non-aggregate) or volume (aggregate)."New value: +"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)." - changed
Input schema / properties / q / descriptionPrevious value: -"SQL WHERE on news_article. Optional when search or a scoping param is present."New value: +"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." - changed
Input schema / properties / search / descriptionPrevious value: -"Full-text search over title+summary (websearch grammar: \"apple earnings\", quoted phrases, OR, -negation). ANDs with q and the scoping params."New value: +"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." - changed
Input schema / properties / select / descriptionPrevious value: -"Comma-separated columns to include. Defaults to a slim set."New value: +"Columns/expressions to return (max 2000 chars). Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`)." - added
Input schema / properties / tickerAdded value: +{ + "description": "Articles mentioning this symbol (ANDed with `q`).", + "type": "string" +} - added
Input schema / properties / tickersAdded value: +{ + "description": "Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.", + "type": "string" +} - added
Input schema / properties / toAdded value: +{ + "description": "Articles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.)", + "type": "string" +} - added
Input schema / properties / universeAdded value: +{ + "description": "Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "q" -]
- Changed
tickerbot_subscribe_events12 fields changed- changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel."New value: +"Delivery channel. `slack` is reserved and returns `501`." - changed
Input schema / properties / device_id / descriptionPrevious value: -"Registered device id from the mobile app (channel mobile_push)."New value: +"Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`." - changed
Input schema / properties / discord_url / descriptionPrevious value: -"Discord incoming-webhook URL (channel discord)."New value: +"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." - changed
Input schema / properties / event_q / descriptionPrevious value: -"Optional event-CONTENT filter over (ticker, ts, kind, payload jsonb) — only those four identifiers, e.g. payload->>'firm' = 'Goldman Sachs' AND payload->>'action' = 'downgrades'."New value: +"Optional event-CONTENT filter in the `/v2/events` grammar — only `ticker`, `ts`, `kind`, `payload` may appear. Composes with `q`." - changed
Input schema / properties / kinds / descriptionPrevious value: -"Comma list of kinds to fire on — any of: dividend, split, insider, analyst (e.g. \"split,analyst\"). NOTE: no enum here on purpose — a scalar enum would reject multi-kind values."New value: +"Event kinds to fire on — array or comma list." - added
Input schema / properties / kinds / enumAdded value: +[ + "earnings", + "dividend", + "split", + "insider", + "analyst" +] - changed
Input schema / properties / name / descriptionPrevious value: -"Display name."New value: +"Display name. Defaults to `events: <kinds> · <scope>`." - changed
Input schema / properties / q / descriptionPrevious value: -"Optional row-STATE filter evaluated against the event's ticker at fire time, e.g. market_cap > 1e10."New value: +"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." - changed
Input schema / properties / target_url / descriptionPrevious value: -"HTTPS delivery URL. Omit for in-app delivery."New value: +"HTTPS delivery URL; or use `channel` + `discord_url`/`device_id`. Omit for in-app." - added
Input schema / properties / tickerAdded value: +{ + "description": "Single-symbol shorthand for `tickers`.", + "type": "string" +} - changed
Input schema / properties / tickers / descriptionPrevious value: -"Scope to specific tickers (comma list, max 50). Mutually exclusive with universe; omit both for all tickers."New value: +"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." - changed
Input schema / properties / universe / descriptionPrevious value: -"Scope to a universe slug (top_10, top_100, or a saved one)."New value: +"Scope to a universe slug (`top_10`, `top_100`, or one of yours). `universe_id` accepted as an alias."
- Changed
tickerbot_subscribe_scan11 fields changed- changed
Input schema / properties / cadence / descriptionPrevious value: -"Evaluation cadence. Default realtime; hourly/nyse_open throttle. (`1m` accepted as a deprecated alias of realtime.)"New value: +"`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`." - changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel: `webhook` (POST to target_url), `discord` (embed to discord_url), `mobile_push` (to a registered device), or `in_app` (dashboard only). Inferred from the URL you pass if omitted."New value: +"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`." - added
Input schema / properties / columnsAdded value: +{ + "description": "Extra signals per fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; unknown ones are rejected at creation. `fields` accepted as an alias — and the RESPONSE reports them under `fields`, as an array.", + "type": "string" +} - changed
Input schema / properties / device_id / descriptionPrevious value: -"Registered device id from the mobile app. Required when channel is \"mobile_push\"."New value: +"Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`." - added
Input schema / properties / dirAdded value: +{ + "default": "desc", + "description": "Sort direction for `order`.", + "enum": [ + "asc", + "desc" + ], + "type": "string" +} - changed
Input schema / properties / discord_url / descriptionPrevious value: -"Discord incoming-webhook URL (https://discord.com/api/webhooks/…). Required when channel is \"discord\"."New value: +"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." - changed
Input schema / properties / name / descriptionPrevious value: -"Human-readable label. Defaults to a truncated version of the query."New value: +"Human-readable label (up to 80 chars). Defaults to `scan: <q>`." - added
Input schema / properties / orderAdded value: +{ + "default": "market_cap", + "description": "Signal the payload's match lists are sorted by before the 100-row cap applies, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real signal (validated at creation).", + "type": "string" +} - changed
Input schema / properties / q / descriptionPrevious value: -"SQL WHERE expression — same grammar as scan."New value: +"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." - changed
Input schema / properties / target_url / descriptionPrevious value: -"Optional https URL to POST matches to (the `webhook` channel). Omit for in-app delivery."New value: +"https:// URL to POST when the match set changes. Omit for in-app delivery." - changed
Input schema / properties / universe / descriptionPrevious value: -"Optional universe slug to scope the watch."New value: +"System or user-owned universe to scope the scan. `universe_id` accepted as an alias. Unknown universes are a 404 `universe_not_found`."
- Changed
tickerbot_subscribe_signal13 fields changed- changed
Input schema / properties / cadence / descriptionPrevious value: -"Evaluation cadence. Default realtime; hourly/nyse_open throttle. (`1m` accepted as a deprecated alias of realtime.)"New value: +"`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`." - changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel: `webhook` (POST to target_url), `discord` (embed to discord_url), `mobile_push` (to a registered device), or `in_app` (dashboard only). Inferred from the URL you pass if omitted."New value: +"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." - added
Input schema / properties / columnsAdded value: +{ + "description": "Comma-separated extra signals to include in each fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; an unknown signal is rejected at creation. `fields` accepted as an alias — and note the RESPONSE reports them under `fields`, as an array.", + "type": "string" +} - changed
Input schema / properties / condition / descriptionPrevious value: -"Required for numerics: single bound like \">70\" or \"<=200\". Ignored for booleans."New value: +"Required for numeric signals. Shape: `\">70\"`, `\"<30\"`, `\">=100\"`, `\"=50\"`. Sending one with a boolean or custom signal returns 400 (it does not apply)." - changed
Input schema / properties / device_id / descriptionPrevious value: -"Registered device id from the mobile app. Required when channel is \"mobile_push\"."New value: +"Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`." - added
Input schema / properties / dirAdded value: +{ + "default": "desc", + "description": "Sort direction for `order`.", + "enum": [ + "asc", + "desc" + ], + "type": "string" +} - changed
Input schema / properties / discord_url / descriptionPrevious value: -"Discord incoming-webhook URL (https://discord.com/api/webhooks/…). Required when channel is \"discord\"."New value: +"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." - changed
Input schema / properties / name / descriptionPrevious value: -"Human-readable label."New value: +"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." - added
Input schema / properties / orderAdded value: +{ + "default": "market_cap", + "description": "Signal the fired payload's match lists are sorted by before the 100-row cap is applied, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real signal (validated at creation).", + "type": "string" +} - changed
Input schema / properties / signal / descriptionPrevious value: -"Column name (e.g. golden_cross_today, rsi_14)."New value: +"Signal name from the schema (case-insensitive)." - changed
Input schema / properties / target_url / descriptionPrevious value: -"Optional https URL for the `webhook` channel; omit for in-app."New value: +"https:// URL to POST when fired. Omit for in-app delivery." - changed
Input schema / properties / ticker / descriptionPrevious value: -"Optional ticker to restrict the watch to one symbol."New value: +"Restrict to a single ticker. Default: any ticker." - changed
Input schema / properties / universe / descriptionPrevious value: -"Optional universe slug."New value: +"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`."
- Changed
tickerbot_subscribe_ticker13 fields changed- changed
Input schema / properties / cadence / descriptionPrevious value: -"Evaluation cadence. Default realtime; hourly/nyse_open throttle. (`1m` accepted as a deprecated alias of realtime.)"New value: +"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`." - changed
Input schema / properties / channel / descriptionPrevious value: -"Delivery channel: `webhook` (POST to target_url), `discord` (embed to discord_url), `mobile_push` (to a registered device), or `in_app` (dashboard only). Inferred from the URL you pass if omitted."New value: +"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." - added
Input schema / properties / columnsAdded value: +{ + "description": "Comma-separated extra signals to include in each fired payload match row, beyond the standard set (`ticker`, `name`, `asset_type`, `price`, `change_1d_pct`, `market_cap`). Each must be a real signal; an unknown signal is rejected at creation. `fields` accepted as an alias — and note the RESPONSE reports them under `fields`, as an array.", + "type": "string" +} - changed
Input schema / properties / condition / descriptionPrevious value: -"SQL WHERE fragment evaluated for this ticker."New value: +"Original name for `q` — accepted as well. The same WHERE-clause fragment; send either spelling." - changed
Input schema / properties / device_id / descriptionPrevious value: -"Registered device id from the mobile app. Required when channel is \"mobile_push\"."New value: +"Device to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`." - added
Input schema / properties / dirAdded value: +{ + "default": "desc", + "description": "Sort direction for `order`.", + "enum": [ + "asc", + "desc" + ], + "type": "string" +} - changed
Input schema / properties / discord_url / descriptionPrevious value: -"Discord incoming-webhook URL (https://discord.com/api/webhooks/…). Required when channel is \"discord\"."New value: +"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." - changed
Input schema / properties / name / descriptionPrevious value: -"Human-readable label."New value: +"Human-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`." - added
Input schema / properties / orderAdded value: +{ + "default": "market_cap", + "description": "Signal the fired payload's match lists are sorted by before the 100-row cap is applied, so a truncated list is the deterministic top 100 rather than an arbitrary sample. Must be a real signal (validated at creation).", + "type": "string" +} - added
Input schema / properties / qAdded value: +{ + "description": "WHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.)", + "type": "string" +} - changed
Input schema / properties / target_url / descriptionPrevious value: -"Optional https URL for the `webhook` channel; omit for in-app."New value: +"https:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard)." - changed
Input schema / properties / ticker / descriptionPrevious value: -"Symbol."New value: +"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." - changed
Input schema / requiredPrevious value: -[ - "ticker", - "condition" -]New value: +[ + "ticker", + "q" +]
- Changed
tickerbot_update_custom_signal6 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"New description."New value: +"New description. Not derived from `expr` — change both if the prose describes a threshold you are moving." - changed
Input schema / properties / expr / descriptionPrevious value: -"New SQL WHERE expression. Re-validated on save."New value: +"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 verbatim, not its expansion." - removed
Input schema / properties / nameRemoved value: -{ - "description": "CURRENT slug — identifies which signal to edit.", - "type": "string" -} - changed
Input schema / properties / new_name / descriptionPrevious value: -"Rename the signal to this slug (snake_case, must not collide with a built-in signal or another of your signals)."New value: +"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." - added
Input schema / properties / signalAdded value: +{ + "description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "name" -]New value: +[ + "signal" +]
- Changed
tickerbot_update_universe3 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"New notes."New value: +"New notes. Max 500 characters." - changed
Input schema / properties / name / descriptionPrevious value: -"New label."New value: +"New label. Non-empty, max 80 characters." - changed
Input schema / properties / tickers / descriptionPrevious value: -"Replace the full ticker list."New value: +"Replace the full ticker list (up to 10,000; validated against the active universe). Does not combine with `add`/`remove` (400)."
- Added
tickerbot_update_webhook
3 tool updates
- Changed
tickerbot_get_series1 field changed- changed
Input schema / properties / transitions_only / descriptionPrevious value: -"Only rows where a boolean column changed value (requires at least one boolean column). Each row carries `transition_drivers` naming the flags that flipped."New value: +"Only rows where a boolean signal changed value (requires at least one boolean signal). Each row carries `transition_drivers` naming the booleans that flipped."
- Changed
tickerbot_list_events2 fields changed- changed
Input schema / properties / kind / descriptionPrevious value: -"Comma list of kinds to include. Default is the four corporate kinds: dividend, split, insider, analyst. Two more are opt-in and join only when named: `signal` (boolean-flag firings) and `news`."New value: +"Comma list of kinds to include. Default is the four corporate kinds: dividend, split, insider, analyst. Two more are opt-in and join only when named: `signal` (boolean firings) and `news`." - changed
Input schema / properties / merge_gap_seconds / descriptionPrevious value: -"kind=signal only, with `signal` AND `tickers` set (filter mode — no q/join/group_by). Interval-union before unpivoting: runs of the flag separated by ≤ N seconds collapse into one, so a flag with thousands of per-tick fragments yields one enter and one exit per real run (3600 for hourly flags, 86400 for daily). Default 0 = raw fragments. A cursor pins this; resend it unchanged when paging."New value: +"kind=signal only, with `signal` AND `tickers` set (filter mode — no q/join/group_by). Interval-union before unpivoting: runs of the boolean separated by ≤ N seconds collapse into one, so a boolean with thousands of per-tick fragments yields one enter and one exit per real run (3600 for hourly booleans, 86400 for daily). Default 0 = raw fragments. A cursor pins this; resend it unchanged when paging."
- Changed
tickerbot_update_custom_signal1 field changed- changed
Input schema / properties / new_name / descriptionPrevious value: -"Rename the signal to this slug (snake_case, must not collide with a built-in column or another of your signals)."New value: +"Rename the signal to this slug (snake_case, must not collide with a built-in signal or another of your signals)."
3 tool updates
- Changed
tickerbot_get_ticker_bars2 fields changed- added
Input schema / properties / adjustedAdded value: +{ + "description": "Default true: prices split-adjusted (restated after each later split, as the tape is). false = the price as it printed on the tape that day — what a broker fill or a chart from that time shows. Volume scales the other way.", + "type": "boolean" +} - added
Input schema / properties / sessionAdded value: +{ + "description": "Sub-hour intervals only. `all` (default) includes pre/post-market; `regular` keeps 09:30–16:00 ET bars, which drops late-reported off-exchange (Form T) prints that land in early-morning minutes.", + "enum": [ + "all", + "regular" + ], + "type": "string" +}
- Changed
tickerbot_list_events2 fields changed- added
Input schema / properties / merge_gap_secondsAdded value: +{ + "description": "kind=signal only, with `signal` AND `tickers` set (filter mode — no q/join/group_by). Interval-union before unpivoting: runs of the flag separated by ≤ N seconds collapse into one, so a flag with thousands of per-tick fragments yields one enter and one exit per real run (3600 for hourly flags, 86400 for daily). Default 0 = raw fragments. A cursor pins this; resend it unchanged when paging.", + "type": "integer" +} - changed
Input schema / properties / signal / descriptionPrevious value: -"kind=signal only. One built-in boolean flag (e.g. golden_cross). REQUIRED to use `q` or `join` on kind=signal — naming the signal is what keeps the query on an index; optional otherwise."New value: +"kind=signal only. One built-in boolean signal (e.g. golden_cross). REQUIRED to use `q` or `join` on kind=signal — naming the signal is what keeps the query on an index; optional otherwise."
- Removed
tickerbot_list_signal_events
2 tool updates
- Changed
tickerbot_get_ticker2 fields changed- changed
Input schema / properties / asof / descriptionPrevious value: -"Optional YYYY-MM-DD or ISO timestamp. Date-only returns the row at close of that day; a timestamp returns the row as of that moment (finest tier covering each column)."New value: +"Optional YYYY-MM-DD or ISO timestamp. Date-only returns the row at close of that day; a timestamp returns the row as of that moment (finest tier covering each column). Same meaning for one symbol or a list." - changed
Input schema / properties / ticker / descriptionPrevious value: -"Symbol. Case-insensitive. Equities: bare symbol (AAPL). Crypto: X-prefixed pair (X:BTCUSD) — bare BTC/ETH are US-listed ETFs, not spot crypto."New value: +"Symbol, or a comma-separated list of up to 50 (AAPL,MSFT,NVDA — a list answers `data` keyed by symbol). Case-insensitive. Equities: bare symbol (AAPL). Crypto: X-prefixed pair (X:BTCUSD) — bare BTC/ETH are US-listed ETFs, not spot crypto."
- Changed
tickerbot_list_tickers7 fields changed- changed
Input schema / properties / asset_type / descriptionPrevious value: -"Filter by INSTRUMENT TYPE within equities: the stored value (CS, ETF, ADRC, PFD, FUND, UNIT, SP, ETS, WARRANT, RIGHT, ETN, ETV), or \"equity\" for the equity-like set. This is not an asset class — `asset_type=crypto` is rejected; crypto is in the main list under its X: symbols."New value: +"Filter by INSTRUMENT TYPE within equities: the stored value (CS, ETF, ADRC, PFD, FUND, UNIT, SP, ETS, WARRANT, RIGHT, ETN, ETV), or \"equity\" for the equity-like set. This is not an asset class — `asset_type=crypto` is rejected; use asset_class=crypto." - changed
Input schema / properties / exchange / descriptionPrevious value: -"Filter by exchange (e.g. \"XNYS\", \"XNAS\", \"BATS\")."New value: +"Filter by exchange name (NASDAQ, NYSE, NYSE Arca, Cboe BZX, NYSE American, OTC Link) or MIC (XNAS, XNYS, BATS)." - removed
Input schema / properties / min_market_capRemoved value: -{ - "description": "Minimum market cap in USD. Orders results by market_cap desc.", - "type": "number" -} - changed
Input schema / properties / search / descriptionPrevious value: -"Case-insensitive substring filter on ticker/name. Orders results by market_cap desc."New value: +"Case-insensitive match on ticker or name (max 64 chars). Ranked: exact ticker, then ticker prefix, then name match, alphabetical within." - removed
Input schema / properties / sectorRemoved value: -{ - "description": "Exact-match sector filter (e.g. \"Technology\").", - "type": "string" -} - removed
Input schema / properties / tickersRemoved value: -{ - "description": "Comma-separated symbols (max 50). When set, returns full rows for these symbols and pagination params are ignored.", - "type": "string" -} - removed
Input schema / properties / universeRemoved value: -{ - "description": "Restrict to a universe slug (top_10, top_100, or a saved one).", - "type": "string" -}
1 tool update
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / order / descriptionPrevious value: -"Sort column. Default day_change_pct (rows) / tickers (aggregate)."New value: +"Sort column. Default: the 1-day change column (rows) / tickers (aggregate)."
2 tool updates
- Changed
tickerbot_get_signals_match1 field changed- changed
Input schema / properties / include_active_since / descriptionPrevious value: -"Built-in booleans only: adds `active_since` per row — when the flag last flipped true (from the spans archive)."New value: +"Built-in booleans only: adds `active_since` + `days_live` per row — first day of the current true streak from daily state (day after the last false day; first true day if never false since it first computed; 5-year lookback, so longer streaks report the window edge)."
- Changed
tickerbot_list_signal_events1 field changed- changed
Input schema / properties / from / descriptionPrevious value: -"Window start (inclusive) on each span's started_at — YYYY-MM-DD, ISO, or epoch-ms."New value: +"Window start (inclusive) — YYYY-MM-DD, ISO, or epoch-ms. Overlap semantics: a span still open, or ended on/after from, is returned even if it started earlier."
1 tool update
- Changed
tickerbot_list_signals_catalog2 fields changed- changed
Input schema / properties / kind / descriptionPrevious value: -"Filter by kind. Omit for both. (`custom` is accepted as a legacy alias of `expression`.)"New value: +"Filter by kind. Omit for both. (`expression` is accepted as a legacy alias of `custom`.)" - changed
Input schema / properties / kind / enumPrevious value: -[ - "builtin", - "expression" -]New value: +[ + "builtin", + "custom" +]
3 tool updates
- Changed
tickerbot_list_events1 field changed- changed
Input schema / properties / group_by / descriptionPrevious value: -"Comma list of rollup keys — switches to aggregate rows, e.g. payload->>'firm' AS firm, or kind."New value: +"Comma list of rollup keys — switches to aggregate rows, e.g. payload->>'firm' AS firm, or kind. `AS` names the JSON key; an un-named payload read is keyed by its payload key (payload->>'firm' -> firm)."
- Changed
tickerbot_scan1 field changed- changed
Input schema / properties / group_by / descriptionPrevious value: -"AGGREGATE MODE: 1–6 comma-separated group keys (columns or expressions, e.g. `sector`). Results become rollup rows instead of tickers."New value: +"AGGREGATE MODE: 1–6 comma-separated group keys (columns or expressions, e.g. `sector`). Results become rollup rows instead of tickers. Alias a key with `AS` to name its JSON key; un-named expressions are named for you."
- Changed
tickerbot_search_news1 field changed- changed
Input schema / properties / group_by / descriptionPrevious value: -"Comma-separated columns for aggregation."New value: +"Comma-separated columns for aggregation. Alias a key with `AS` to name its JSON key; un-named expressions are named for you."
1 tool update
- Changed
tickerbot_list_webhooks2 fields changed- changed
Input schema / properties / status / descriptionPrevious value: -"Filter by status. Omit for all. (`pending_verification` = created but never successfully pinged.)"New value: +"Filter by status: `active` or `disabled`. Omit for all." - changed
Input schema / properties / status / enumPrevious value: -[ - "active", - "pending_verification", - "disabled" -]New value: +[ + "active", + "disabled" +]
1 tool update
- Changed
tickerbot_search_news2 fields changed- changed
Input schema / properties / q / descriptionPrevious value: -"SQL WHERE on news_article. Required."New value: +"SQL WHERE on news_article. Optional when search or a scoping param is present." - added
Input schema / properties / searchAdded value: +{ + "description": "Full-text search over title+summary (websearch grammar: \"apple earnings\", quoted phrases, OR, -negation). ANDs with q and the scoping params.", + "type": "string" +}
1 tool update
- Changed
tickerbot_subscribe_events1 field changed- removed
Input schema / properties / cadenceRemoved value: -{ - "description": "Evaluation cadence. Default realtime; hourly/nyse_open throttle. (`1m` accepted as a deprecated alias of realtime.)", - "enum": [ - "realtime", - "hourly", - "nyse_open" - ], - "type": "string" -}
Related MCP Connectors
US stocks and options data via SQL: bars, ticks, greeks, fundamentals, filings. No key.
41Live US options chains with Greeks and IV, a screener, SQL, and FMP fundamentals.
SEC dilution data, live market data, and news for U.S. equities, with point-in-time as-of queries.
Live market data, financial analysis, and portfolio research tools across 10,000+ tickers.
Related MCP Servers
- FlicenseAqualityCmaintenanceRead-only SQL MCP server for quantitative finance data (DuckDB) with stock quotes, financials, and historical K-lines, enabling cross-database JOIN queries.21-
- AlicenseNot gradedqualityBmaintenanceProvides US stock market data for AI agents, including intraday and daily bars, SEC fundamentals, filings, and insider data, with pay-per-query via USDC on Base.1MIT
- AlicenseBqualityCmaintenanceReal-time crypto, stock, and prediction-market data for agents — prices, indicators, funding rates, DeFi TVL, macro calendar, and an AI momentum score. Configure one Base wallet key and it just works. No signup, no dashboard, no subscription.223 npmMIT
- -licenseNot gradedqualityNot gradedmaintenanceReal-time financial market data MCP server. Stocks, crypto, technicals, sentiment, FDA calendar. No API keys required.-
Glama MCP Gateway
Add one secure layer between your agents and this server.