Skip to main content
Glama

Server Details

The stock market, in SQL — scan, replay, or subscribe across ~12k US tickers and top 100 cryptos.

If you are the author of this connector, you can claim ownership with GitHub, an HTTP challenge, or a DNS record. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
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

B3.4/5.0

Scored across 32 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count2/5

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.

Completeness5/5

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 tools
tickerbot_create_custom_signalBInspect

A named boolean predicate you can reference anywhere a built-in signal goes.

ParametersJSON Schema
NameRequiredDescriptionDefault
exprYesBoolean 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.
nameYesSlug — `^[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.
descriptionNoFree-form notes. Max 500 chars. Absent or empty comes back as `""` rather than null.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
signalYesThe stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoOptional 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.
nameYesHuman-readable label, up to 80 characters. Display-only — never used to reference the universe.
tickersYesTicker symbols, up to 10,000. Validated against the active universe. `[]` is accepted — a shell universe you can fill later via PATCH.
descriptionNoFree-form notes, up to 500 characters. Stored as `""` when omitted.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesThe slug — the universe's handle in `?universe=`.
nameYesDisplay label.
sizeYesMember count.
as_ofYesServer time this response was assembled (ISO 8601).
systemYes`false` — this is your universe.
tickersYesMembers, after this call.
created_atYesCreation timestamp.
updated_atYesLast modification timestamp.
descriptionYesFree-form notes; `""` when unset.
effective_atNoSystem universes only; absent on yours.
rebalance_methodNoSystem universes only; absent on yours.
next_rebalance_atNoSystem universes only; absent on yours.

TDQS

B3.3/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
dirNoSort direction for `order`. Not accepted on `event` triggers (400).desc
nameNoDisplay name, max 80 characters. Defaults to an auto-generated one from the trigger.
orderNoSignal 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
cadenceNoEvaluation cadence — a user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).realtime
channelNoDelivery channel. See Delivery channels.
columnsNoExtra 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.
triggerYesWhat 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_idNoRegistered device id (channel `mobile_push`, see /v2/devices).
target_urlNoHTTPS delivery URL (the `webhook` channel), max 1024 characters. Omit for in-app delivery, or use `channel` + `discord_url`/`device_id` for other channels.
discord_urlNoDiscord webhook URL (channel `discord`).

Output Schema

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
test_urlYesReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
channel_configYesReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
signing_secretYesReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
forceNoWhen `true`, skip the reference check and delete. References will break on next recompile.
signalYesCustom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesUniverse slug.
forceNoA 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

A4.4/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id.

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id.

Output Schema

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
toNoWindow end (inclusive): a bare `YYYY-MM-DD` means through the end of that day, same as series. Mutually exclusive with `asof` and `before` (400).
asofNoPoint-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.
fromNoWindow 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).
limitNoMost-recent N bars. Max 1000 — an over-cap value is clamped, not an error.
beforeNoReturn 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).
cursorNoContinuation 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.
tickerYesTicker symbol, or a comma-separated list (up to 50) for a bulk response keyed by symbol.
sessionNoSub-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
adjustedNoDefault `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.
intervalYesBar 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

ParametersJSON Schema
NameRequiredDescription
barsYesOHLCV bars, chronological, in the compact array shape. Bulk requests key this by symbol instead.
noteNoPresent 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_ofYesServer time this response was assembled (ISO 8601).
countYesBars returned in single-symbol mode; the number of SYMBOLS in bulk mode.
notesNoBulk (comma-list) requests only: the per-symbol disclosures, keyed by symbol, in place of `note`.
tickerYesThe symbol you asked for.
sessionYesThe session filter applied: `all` (default) or `regular` (09:30–16:00 ET, sub-hour intervals only).
adjustedYesWhether the bars are split-adjusted — `true` unless you passed `adjusted=false`.
coverageYesWhy 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.
intervalYesThe bar size served.
next_cursorNoOpaque 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

D1.9/5.0
Behavior1/5

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.

Conciseness2/5

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.

Completeness1/5

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.

Parameters3/5

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.

Purpose3/5

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.

Usage Guidelines1/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax 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.
tickerYesETF symbol. Case-insensitive.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesHoldings in this page.
totalYesTotal constituents held, before `limit`.
is_etfYesWhether the symbol is an ETF, from the instrument type on its ticker record.
tickerYesThe ETF you asked for.
holdingsYesConstituents, heaviest first, each with its weight.
truncatedYes`true` when `limit` cut the list short.

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
tickerYesETF symbol. Case-insensitive.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesSectors returned.
is_etfYesWhether the symbol is an ETF, from the instrument type on its ticker record.
tickerYesThe ETF you asked for.
sectorsYesSector weights, heaviest first, in the vendor's ETF-profile vocabulary.

TDQS

A4.3/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
toNoLatest timestamp (inclusive), `YYYY-MM-DD` or ISO.
asofNoPoint-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.
fromNoEarliest 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.
limitNoGrid 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.
cursorNoOpaque cursor from the previous response — every ticker pages backward in lockstep on the shared grid, no per-ticker gaps or duplicates.
tickerNoSingle-symbol form — `/v2/series?ticker=AAPL` is ticker history in its canonical spelling. Exactly one of `ticker` or `tickers` is required.
columnsNoUp 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.
tickersNoComma-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.
intervalNoGrid granularity. `1w` resamples the daily tier weekly (Monday-keyed); `1q` is the fiscal-quarter grid.1d
transitions_onlyNoOnly 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

ParametersJSON Schema
NameRequiredDescription
_metaYesPer-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_ofYesServer time this response was assembled (ISO 8601).
countYesRows per ticker in this page.
seriesYesKeyed by ticker: an array of flat rows, chronological, each keyed `t` plus the columns you asked for.
columnsYesColumns in the response, echoed.
tickersYesSymbols in the response, echoed.
intervalYesThe grid granularity served.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
asofNoOptional. 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.
limitNoPage size. Max 200.
cursorNoOpaque cursor from the previous response.
signalYesA 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_byNoRow 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
intervalNoGrain 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
universeNoOptional. Scope to a system or caller-owned universe slug.
conditionNoRequired 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_sinceNoBuilt-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

ParametersJSON Schema
NameRequiredDescription
_metaNoWith `asof` only: how the read was resolved — interval served and requested, blending, sources, frozen fields. See the as-of read below.
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
signalYesThe signal you asked for.
resultsYesMatching tickers with the signal value.
universeYesThe universe you scoped to, echoed; `null` when unscoped.
conditionYesThe bound you passed, echoed; `null` for boolean and custom signals.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
asofNoOptional. 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.
tickerYesOne 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.
intervalNoGrain 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

ParametersJSON Schema
NameRequiredDescription
dataYesThe full ticker row — every signal on the schema page. On the list form, an object keyed by symbol, one full row each.
_metaNoWith `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_ofYesServer time this response was assembled (ISO 8601).
countNoList form only — how many of `requested` were found.
tickerNoThe symbol you asked for, normalised. Single form only.
not_foundNoList form only — the requested symbols we do not track, in request order. An empty array when every symbol was found.
requestedNoList form only — the canonical symbols asked for, de-duplicated, in request order.

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
tickerYesCase-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

ParametersJSON Schema
NameRequiredDescription
nameYesCompany or instrument name.
as_ofYesServer time this response was assembled (ISO 8601).
spansYesPer resolution — `oldest`, `newest`, `rows`.
tickerYesThe symbol you asked for.
minute_tierYesWhether 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_fieldsYesPer-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

A4.5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesUniverse slug.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesThe slug — the universe's handle in `?universe=`.
nameYesDisplay label.
sizeYesMember count.
as_ofYesServer time this response was assembled (ISO 8601).
systemYes`true` for a built-in universe, `false` for one you created.
tickersYesMembers.
created_atYesCreation timestamp.
updated_atYesLast modification timestamp.
descriptionYesFree-form notes; `""` when unset.
effective_atNoSystem universes only: when this membership took effect.
rebalance_methodNoSystem universes only: how membership is chosen.
next_rebalance_atNoSystem universes only: when membership is next recomputed.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.).

Output Schema

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

C2.6/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNoSQL 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.
toNoWindow 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.)
dirNoAggregate-mode sort direction.desc
firmNoAnalyst-only structured filter — requires `kind=analyst` alone (`400` otherwise). Exact firm-name match on the ratings feed.
fromNoEvents 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.)
joinNoSet to `state` to allow ticker-state signals in `q`/`select`/`group_by`/`having`, evaluated as of each event's timestamp (daily resolution).
kindNoComma list of kinds. Omitted → the five corporate kinds; `signal` and `news` join only when named here.
limitNoPage size (row modes) / max rollup rows (aggregate mode). Max 1000.
orderNoAggregate-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.)
actionNoAnalyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions.
cursorNoOpaque cursor from the previous response — carries the original filters (and `q` when short), so pass it alone. Not valid with `group_by`.
havingNoPost-aggregation filter. Requires `group_by`.
selectNoAggregate-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.
signalNoSignal-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.
tickerNoSingle-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins.
tickersNoComma list of tickers (max 50). Mutually exclusive with `universe`.
group_byNoComma 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`.
intervalNoGrain 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
universeNoUniverse slug (`top_10`, `top_100`, or one of yours) to scope the stream. Mutually exclusive with `tickers`.
transitionNoSignal-only filter — requires `kind=signal` alone. `enter` (false→true) or `exit` (true→false).

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
queryYesYour 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`.
resultsYesOne row per event (`ticker`, `ts`, `kind`, `payload`), or rollup rows plus `truncated: true` when an aggregate exceeds `limit`.
truncatedNoAggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.
next_cursorNoOpaque 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

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNoFilter by kind (`expression` accepted as a legacy alias for `custom`). Omit to return both.
limitNoPage size for the custom-signal slice. Max 200.
cursorNoOpaque cursor from a prior response.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page — the canonical count, equal to `count_builtin` + `count_custom`.
signalsYesThe 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`.
taxonomyNoAbsent 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_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.
count_customYesYour custom signals.
count_builtinYesBuilt-in signals in the catalog.

TDQS

A3.9/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoPage size. Max 1000.
cursorNoOpaque 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.
searchNoCase-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.
exchangeNoFilter 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_typeNoFilter 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_classNoFilter 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

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
resultsYesOne 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_cursorYesOpaque token for the next page; `null` on the last page.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoPage size (applies to your own). Max 100.
ownerNoWhich universes to list: `me` (your own), `system` (built-ins), or `all` (both).me
cursorNoOpaque cursor from the previous response.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
universesYesThe universes in scope. Every row carries `system: true|false`.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id.
toNoOnly 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.
fromNoOnly 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.
limitNoPage size. Max 100.
cursorNoOpaque cursor.
statusNoFilter by delivery status.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
deliveriesYesAttempts, newest first: status, attempt, response code, error, and `body_string` — the exact JSON POSTed.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A3.6/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoPage size. Max 100.
cursorNoOpaque cursor from the previous response.
statusNoFilter 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

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
webhooksYesYour subscriptions, newest first, each with its `subscription_origin` and health fields. `signing_secret` is stripped — it is shown only on create.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesSQL 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.
dirNoSort direction.desc
asofNoOptional. 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.
fullNoReturn every signal instead of the default set. Mutually exclusive with `columns` — passing both is a 400.
limitNoPage 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.
orderNoSignal 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
cursorNoOpaque cursor from the previous response's `next_cursor`. Row mode only.
havingNoFilter the aggregate rows (requires `group_by`). Custom signals are valid here too.
selectNoAggregate 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.
columnsNoExtra 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_byNoAGGREGATE 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`.
intervalNoGrain 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
universeNoSlug of a system universe (`top_10`, `top_100`) or one of your own. Omitted, the scan runs across all ~21,072 tracked tickers.
asset_classNoOne 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

ParametersJSON Schema
NameRequiredDescription
_metaNo`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_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
queryYesYour query, echoed — `q`, `order`, `dir`, `limit`, and any scope.
resultsYesOne row per match — every signal on the schema page, plus any you named.
truncatedNoAggregate mode only (`group_by`): `true` when the rollup stopped at its row cap. Aggregate responses are unpaged, so `next_cursor` is absent there.
next_cursorNoOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNoWHERE 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.
toNoArticles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.)
dirNoSort direction.desc
fromNoEarliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`. (`since` accepted as an alias.)
limitNoPage size. Max 1000.
orderNoSort — 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).
cursorNoOpaque pagination cursor from a prior response's `next_cursor`.
havingNoHAVING clause on the aggregate (max 1000 chars). Requires `group_by`.
searchNoFull-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.
selectNoColumns/expressions to return (max 2000 chars). Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`).
tickerNoArticles mentioning this symbol (ANDed with `q`).
tickersNoComma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.
group_byNoAGGREGATE 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?`.
universeNoUniverse slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
countYesRows in this page.
queryYesYour filters, echoed.
resultsYesArticle rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list.
truncatedNoAggregate mode only (`group_by`): `true` when the rollup stopped at its row cap.
next_cursorYesOpaque token for the next page; `null` on the last page. Pass it back as `cursor`.

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNoOptional 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.
nameNoDisplay name. Defaults to `events: <kinds> · <scope>`.
kindsYesEvent kinds to fire on — array or comma list.
tickerNoSingle-symbol shorthand for `tickers`.
channelNoDelivery channel. `slack` is reserved and returns `501`.
event_qNoOptional event-CONTENT filter in the `/v2/events` grammar — only `ticker`, `ts`, `kind`, `payload` may appear. Composes with `q`.
tickersNoScope 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.
universeNoScope to a universe slug (`top_10`, `top_100`, or one of yours). `universe_id` accepted as an alias.
device_idNoDevice to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.
target_urlNoHTTPS delivery URL; or use `channel` + `discord_url`/`device_id`. Omit for in-app.
discord_urlNoDiscord 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

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
test_urlYesReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
channel_configYesReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
signing_secretYesReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesWHERE-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.
dirNoSort direction for `order`.desc
nameNoHuman-readable label (up to 80 chars). Defaults to `scan: <q>`.
orderNoSignal 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
cadenceNo`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`.
channelNoDelivery 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`.
columnsNoExtra 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.
universeNoSystem or user-owned universe to scope the scan. `universe_id` accepted as an alias. Unknown universes are a 404 `universe_not_found`.
device_idNoDevice to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.
target_urlNohttps:// URL to POST when the match set changes. Omit for in-app delivery.
discord_urlNoDiscord 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

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
test_urlYesReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
channel_configYesReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
signing_secretYesReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A3.8/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
dirNoSort direction for `order`.desc
nameNoHuman-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.
orderNoSignal 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
signalYesSignal name from the schema (case-insensitive).
tickerNoRestrict to a single ticker. Default: any ticker.
cadenceNo`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`.
channelNoDelivery 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.
columnsNoComma-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.
universeNoRestrict 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`.
conditionNoRequired 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_idNoDevice to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.
target_urlNohttps:// URL to POST when fired. Omit for in-app delivery.
discord_urlNoDiscord 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

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
test_urlYesReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
channel_configYesReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
signing_secretYesReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
qYesWHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.)
dirNoSort direction for `order`.desc
nameNoHuman-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`.
orderNoSignal 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
tickerYesCase-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.
cadenceNoHow 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`.
channelNoDelivery 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.
columnsNoComma-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.
conditionNoOriginal name for `q` — accepted as well. The same WHERE-clause fragment; send either spelling.
device_idNoDevice to notify, from `POST /v2/devices/register`. Required when `channel` is `mobile_push`; unknown ids are a 404 `device_not_found`.
target_urlNohttps:// URL to POST when the condition fires. Omit for in-app delivery (visible in the dashboard).
discord_urlNoDiscord 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

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
test_urlYesReturned on create only: the `POST /v2/webhooks/{id}/test` URL for this record.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
channel_configYesReturned on create only: the channel-specific delivery settings as stored (e.g. the Discord URL, the device id).
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
signing_secretYesReturned on create only — shown once, never again. HMAC key for verifying the `X-Tickerbot-Signature` header on deliveries.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesDelivery id of the test — `dl_…`, the same shape as a real delivery on the deliveries route.
testYes`true` — this delivery was a synthetic ping, not a trigger firing.
as_ofYesServer time this response was assembled (ISO 8601).
errorYesWhy delivery failed, in words; `null` on success.
statusYesThe delivery record's status: `delivered` or `permanent_failure` (a test is never retried).
deliveredYesWhether your endpoint accepted the ping (2xx within the timeout).
created_atYesWhen the ping was sent (ISO 8601).
elapsed_msYesRound-trip time of the ping.
webhook_idYesThe webhook that was tested.
http_statusYesThe status your endpoint returned; `null` when it could not be reached.

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
exprNoNew 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.
signalYesCustom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.
new_nameNoNew 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.
descriptionNoNew description. Not derived from `expr` — change both if the prose describes a threshold you are moving.

Output Schema

ParametersJSON Schema
NameRequiredDescription
as_ofYesServer time this response was assembled (ISO 8601).
signalYesThe stored signal: `name`, `kind` (`custom`), `description`, `expr` (your predicate as stored), `created_at`, `updated_at`.

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesUniverse slug.
addNoAdd these tickers (deduplicated).
nameNoNew label. Non-empty, max 80 characters.
removeNoRemove these tickers.
tickersNoReplace the full ticker list (up to 10,000; validated against the active universe). Does not combine with `add`/`remove` (400).
descriptionNoNew notes. Max 500 characters.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYesThe slug — the universe's handle in `?universe=`.
nameYesDisplay label.
sizeYesMember count.
as_ofYesServer time this response was assembled (ISO 8601).
systemYes`false` — this is your universe.
tickersYesMembers, after this call.
created_atYesCreation timestamp.
updated_atYesLast modification timestamp.
descriptionYesFree-form notes; `""` when unset.
effective_atNoSystem universes only; absent on yours.
rebalance_methodNoSystem universes only; absent on yours.
next_rebalance_atNoSystem universes only; absent on yours.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesWebhook id.
nameNoNew display name. Non-empty, max 80 characters.
cadenceNoEvaluation cadence. A user preference — never gated. Event triggers deliver on ingest — only `realtime` is accepted on them (400 otherwise).
enabledNo`false` disables the webhook (status → `disabled`). `true` is a no-op unless disabled, in which case use `POST /v2/webhooks/{id}/enable` instead.
target_urlNoNew 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

ParametersJSON Schema
NameRequiredDescription
qYesThe stored predicate. Custom signals appear expanded: the SQL is frozen at creation.
idYesThe webhook id — `wh_…`, the handle for every other call on this record.
dirYesSort direction for that list; `null` means the default (`desc`).
nameYesYour label for the subscription.
as_ofYesServer time this response was assembled (ISO 8601).
orderYesSort signal for the payload row list; `null` means the evaluator default (`market_cap`).
fieldsYesExtra signals carried on each fired match row; `null` means the standard set.
sourceYesWhich API version created the record; `v2` for anything you create today.
statusYes`active` or `disabled`. Auto-disable follows repeated delivery failure.
cadenceYesHow often the trigger is evaluated — `realtime`, `hourly`, or `nyse_open`.
channelYesWhere deliveries go: `webhook`, `discord`, `in_app`, or `mobile_push`.
event_qNoEvent-trigger webhooks only: the payload filter, or `null`.
rule_idYesLegacy link to a v1 alert rule; `null` on everything created through v2.
deliveryYesLegacy alias of `channel`, kept aligned for older readers.
created_atYesCreation timestamp.
last_errorNoThe last evaluation error; `null` on a healthy hook. The answer to "why is my webhook not firing?".
last_firedYesWhen a delivery last went out; `null` if it never has.
target_urlYesYour HTTPS endpoint; `null` on every channel except `webhook`.
updated_atYesLast modification timestamp.
event_kindsNoEvent-trigger webhooks only: the kinds subscribed (`split`, `dividend`, `insider`, `analyst`, `earnings`).
universe_idYesUniverse the trigger is scoped to, or `null` for the whole market.
next_eval_atYesWhen the evaluator will next consider this subscription.
trigger_kindNoEvent-trigger webhooks only: `event`.
event_tickersNoEvent-trigger webhooks only: the symbols the trigger is scoped to, or `null` for the universe / whole market.
last_match_setYesTickers matching at the last evaluation — the set the next run is diffed against, which is what makes firing edge-triggered.
last_evaluated_atYesWhen it was last evaluated; `null` until the first run.
last_eval_error_atNoWhen the last evaluation error happened; `null` on a healthy hook.
subscription_originYesWhich door created it — `type` (`ticker`/`signal`/`scan`/`event`), its `ref`, and the `condition` in display form.
last_predicate_valueYesThe trigger's value at the last evaluation; `null` until it has run.

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 3 tool updates
    • Changedtickerbot_create_webhook1 field changed
      • changedInput schema / properties / trigger / properties / condition / description
        Previous 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`."
    • Changedtickerbot_get_signal2 fields changed
      • changedInput schema / properties / condition / description
        Previous 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)."
      • changedInput schema / properties / signal / description
        Previous 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`."
    • Changedtickerbot_subscribe_signal1 field changed
      • changedInput schema / properties / condition / description
        Previous 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)."
  2. 1 tool update
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / universe / description
        Previous 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."
  3. 1 tool update
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / universe / description
        Previous 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."
  4. 1 tool update
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / universe / description
        Previous 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."
  5. 29 tool updates
    • Changedtickerbot_create_custom_signal1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_create_universe1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_create_webhook1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_enable_webhook1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_bars1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_etf_holdings1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_etf_sectors1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_series1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_signal1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_ticker1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_ticker_coverage1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_universe1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_get_webhook1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_events1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_signals1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_tickers1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_universes1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_webhook_deliveries1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_list_webhooks1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_scan1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_search_news1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_subscribe_events1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_subscribe_scan1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_subscribe_signal1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_subscribe_ticker1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_test_webhook1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_update_custom_signal1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_update_universe1 field changed
      • changedOutput 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"
        +}
    • Changedtickerbot_update_webhook1 field changed
      • changedOutput 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"
        +}
  6. 4 tool updates
    • Changedtickerbot_list_events1 field changed
      • changedInput schema / properties / kind / enum
        Previous value: -[
        -  "earnings",
        -  "dividend",
        -  "split",
        -  "insider",
        -  "analyst",
        -  "news",
        -  "signal"
        -]New value: +[
        +  "dividend",
        +  "split",
        +  "insider",
        +  "analyst",
        +  "earnings",
        +  "signal",
        +  "news"
        +]
    • Changedtickerbot_list_tickers1 field changed
      • removedInput schema / properties / exchange / enum
        Removed value: -[
        -  "NASDAQ",
        -  "NYSE",
        -  "NYSE Arca",
        -  "Cboe BZX",
        -  "NYSE American",
        -  "OTC Link"
        -]
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / universe / description
        Previous 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."
    • Changedtickerbot_subscribe_events1 field changed
      • changedInput schema / properties / kinds / enum
        Previous value: -[
        -  "earnings",
        -  "dividend",
        -  "split",
        -  "insider",
        -  "analyst"
        -]New value: +[
        +  "dividend",
        +  "split",
        +  "insider",
        +  "analyst",
        +  "earnings"
        +]
  7. 1 tool update
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / universe / description
        Previous 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."
  8. 37 tool updates
    • Changedtickerbot_create_custom_signal3 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"Optional human description."New value: +"Free-form notes. Max 500 chars. Absent or empty comes back as `\"\"` rather than null."
      • changedInput schema / properties / expr / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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."
    • Changedtickerbot_create_universe4 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"Optional free-form notes."New value: +"Free-form notes, up to 500 characters. Stored as `\"\"` when omitted."
      • changedInput schema / properties / id / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous value: -"Human-readable name."New value: +"Human-readable label, up to 80 characters. Display-only — never used to reference the universe."
      • changedInput schema / properties / tickers / description
        Previous 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."
    • Changedtickerbot_create_webhook13 fields changed
      • addedInput schema / properties / cadence / default
        Added value: +"realtime"
      • changedInput schema / properties / cadence / description
        Previous 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)."
      • changedInput schema / properties / channel / description
        Previous value: -"Delivery channel. Inferred from the URL you pass if omitted."New value: +"Delivery channel. See Delivery channels."
      • changedInput schema / properties / columns / description
        Previous 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."
      • changedInput schema / properties / device_id / description
        Previous value: -"Registered device id from the mobile app (channel mobile_push)."New value: +"Registered device id (channel `mobile_push`, see /v2/devices)."
      • addedInput schema / properties / dir
        Added value: +{
        +  "default": "desc",
        +  "description": "Sort direction for `order`. Not accepted on `event` triggers (400).",
        +  "enum": [
        +    "asc",
        +    "desc"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / discord_url / description
        Previous value: -"Discord incoming-webhook URL (channel discord)."New value: +"Discord webhook URL (channel `discord`)."
      • changedInput schema / properties / name / description
        Previous value: -"Display name. Defaults from the trigger."New value: +"Display name, max 80 characters. Defaults to an auto-generated one from the trigger."
      • addedInput schema / properties / order
        Added 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"
        +}
      • changedInput schema / properties / target_url / description
        Previous 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."
      • changedInput schema / properties / trigger / description
        Previous 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`."
      • addedInput schema / properties / trigger / properties
        Added 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"
        +  }
        +}
      • addedInput schema / properties / trigger / required
        Added value: +[
        +  "type"
        +]
    • Changedtickerbot_delete_custom_signal5 fields changed
      • addedInput schema / properties / force / default
        Added value: +false
      • changedInput schema / properties / force / description
        Previous 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."
      • removedInput schema / properties / name
        Removed value: -{
        -  "description": "Custom signal slug.",
        -  "type": "string"
        -}
      • addedInput schema / properties / signal
        Added value: +{
        +  "description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "name"
        -]New value: +[
        +  "signal"
        +]
    • Changedtickerbot_delete_universe1 field changed
      • addedInput schema / properties / force
        Added 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"
        +}
    • Changedtickerbot_delete_webhook1 field changed
      • changedInput schema / properties / id / description
        Previous value: -"Webhook id (looks like `wh_…`)."New value: +"Webhook id."
    • Addedtickerbot_get_bars
    • Addedtickerbot_get_etf_holdings
    • Addedtickerbot_get_etf_sectors
    • Changedtickerbot_get_series13 fields changed
      • addedInput schema / properties / asof
        Added 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"
        +}
      • changedInput schema / properties / columns / description
        Previous 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."
      • changedInput schema / properties / cursor / description
        Previous 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."
      • changedInput schema / properties / from / description
        Previous 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."
      • addedInput schema / properties / interval / default
        Added value: +"1d"
      • changedInput schema / properties / interval / description
        Previous 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."
      • addedInput schema / properties / limit / default
        Added value: +252
      • changedInput schema / properties / limit / description
        Previous 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."
      • changedInput schema / properties / ticker / description
        Previous 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."
      • changedInput schema / properties / tickers / description
        Previous 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."
      • changedInput schema / properties / to / description
        Previous value: -"Latest timestamp (inclusive; a bare date means through that day)."New value: +"Latest timestamp (inclusive), `YYYY-MM-DD` or ISO."
      • changedInput schema / properties / transitions_only / description
        Previous 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)."
      • removedInput schema / required
        Removed value: -[]
    • Addedtickerbot_get_signal
    • Removedtickerbot_get_signals_match
    • Changedtickerbot_get_ticker3 fields changed
      • changedInput schema / properties / asof / description
        Previous 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."
      • addedInput schema / properties / interval
        Added 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"
        +}
      • changedInput schema / properties / ticker / description
        Previous 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."
    • Removedtickerbot_get_ticker_bars
    • Changedtickerbot_get_ticker_coverage1 field changed
      • changedInput schema / properties / ticker / description
        Previous 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."
    • Removedtickerbot_get_ticker_history
    • Removedtickerbot_get_ticker_holdings
    • Removedtickerbot_get_ticker_sectors
    • Changedtickerbot_get_webhook1 field changed
      • changedInput schema / properties / id / description
        Previous value: -"Webhook id (looks like `wh_…`)."New value: +"Webhook id returned by a subscribe endpoint (`POST /v2/tickers/{T}/subscribe`, etc.)."
    • Changedtickerbot_list_events25 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"kind=analyst only. Exact rating-action filter."New value: +"Analyst-only structured filter — requires `kind=analyst` alone. Same `action` vocabulary as Analyst actions."
      • changedInput schema / properties / cursor / description
        Previous 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`."
      • addedInput schema / properties / dir / default
        Added value: +"desc"
      • changedInput schema / properties / firm / description
        Previous 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."
      • changedInput schema / properties / from / description
        Previous 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.)"
      • changedInput schema / properties / group_by / description
        Previous 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`."
      • changedInput schema / properties / having / description
        Previous value: -"Post-aggregation filter (requires group_by), e.g. COUNT(*) > 5."New value: +"Post-aggregation filter. Requires `group_by`."
      • addedInput schema / properties / interval / default
        Added value: +"auto"
      • changedInput schema / properties / interval / description
        Previous 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`."
      • changedInput schema / properties / interval / enum
        Previous value: -[
        -  "1m",
        -  "1h",
        -  "1d"
        -]New value: +[
        +  "1m",
        +  "1h",
        +  "1d",
        +  "auto"
        +]
      • changedInput schema / properties / join / description
        Previous 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)."
      • changedInput schema / properties / kind / description
        Previous 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."
      • addedInput schema / properties / kind / enum
        Added value: +[
        +  "earnings",
        +  "dividend",
        +  "split",
        +  "insider",
        +  "analyst",
        +  "news",
        +  "signal"
        +]
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Max 1000."New value: +"Page size (row modes) / max rollup rows (aggregate mode). Max 1000."
      • removedInput schema / properties / merge_gap_seconds
        Removed 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"
        -}
      • changedInput schema / properties / order / description
        Previous 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.)"
      • changedInput schema / properties / q / description
        Previous 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."
      • changedInput schema / properties / select / description
        Previous 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."
      • changedInput schema / properties / signal / description
        Previous 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."
      • changedInput schema / properties / ticker / description
        Previous value: -"Single-ticker filter, e.g. AAPL."New value: +"Single-ticker filter. When both `ticker` and `tickers` are passed, `ticker` wins."
      • changedInput schema / properties / tickers / description
        Previous value: -"Comma-separated tickers, max 50. Mutually exclusive with `universe`."New value: +"Comma list of tickers (max 50). Mutually exclusive with `universe`."
      • changedInput schema / properties / to / description
        Previous 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.)"
      • changedInput schema / properties / transition / description
        Previous 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)."
      • changedInput schema / properties / universe / description
        Previous 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`."
    • Addedtickerbot_list_signals
    • Removedtickerbot_list_signals_catalog
    • Removedtickerbot_list_system_universes
    • Changedtickerbot_list_tickers9 fields changed
      • changedInput schema / properties / asset_class / description
        Previous 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."
      • changedInput schema / properties / asset_type / description
        Previous 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`."
      • addedInput schema / properties / asset_type / enum
        Added value: +[
        +  "CS",
        +  "ETF",
        +  "ADRC",
        +  "PFD",
        +  "FUND",
        +  "UNIT",
        +  "SP",
        +  "ETS",
        +  "WARRANT",
        +  "RIGHT",
        +  "ETN",
        +  "ETV",
        +  "equity"
        +]
      • changedInput schema / properties / cursor / description
        Previous 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."
      • changedInput schema / properties / exchange / description
        Previous 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."
      • addedInput schema / properties / exchange / enum
        Added value: +[
        +  "NASDAQ",
        +  "NYSE",
        +  "NYSE Arca",
        +  "Cboe BZX",
        +  "NYSE American",
        +  "OTC Link"
        +]
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Max 1000. Default 50."New value: +"Page size. Max 1000."
      • changedInput schema / properties / search / description
        Previous 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."
    • Changedtickerbot_list_universes5 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque cursor."New value: +"Opaque cursor from the previous response."
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous 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."
      • addedInput schema / properties / owner / default
        Added value: +"me"
      • changedInput schema / properties / owner / description
        Previous value: -"Which universes to list."New value: +"Which universes to list: `me` (your own), `system` (built-ins), or `all` (both)."
    • Changedtickerbot_list_webhook_deliveries4 fields changed
      • changedInput schema / properties / from / description
        Previous 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."
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Max 100. Default 50."New value: +"Page size. Max 100."
      • changedInput schema / properties / to / description
        Previous 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."
    • Changedtickerbot_list_webhooks4 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque cursor."New value: +"Opaque cursor from the previous response."
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Max 100. Default 50."New value: +"Page size. Max 100."
      • changedInput schema / properties / status / description
        Previous 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."
    • Removedtickerbot_patch_webhook
    • Changedtickerbot_scan18 fields changed
      • changedInput schema / properties / asof / description
        Previous 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."
      • addedInput schema / properties / asset_class
        Added 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"
        +}
      • addedInput schema / properties / columns
        Added 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"
        +}
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque cursor (row mode only)."New value: +"Opaque cursor from the previous response's `next_cursor`. Row mode only."
      • addedInput schema / properties / dir / default
        Added value: +"desc"
      • removedInput schema / properties / fields
        Removed value: -{
        -  "description": "Comma-separated extra columns to include (row mode only).",
        -  "type": "string"
        -}
      • addedInput schema / properties / full / default
        Added value: +false
      • changedInput schema / properties / full / description
        Previous 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."
      • changedInput schema / properties / group_by / description
        Previous 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`."
      • changedInput schema / properties / having / description
        Previous value: -"Aggregate filter (requires group_by). Example: `COUNT(*) >= 10`."New value: +"Filter the aggregate rows (requires `group_by`). Custom signals are valid here too."
      • addedInput schema / properties / interval
        Added 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"
        +}
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous 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."
      • addedInput schema / properties / order / default
        Added value: +"change_1d_pct"
      • changedInput schema / properties / order / description
        Previous 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."
      • changedInput schema / properties / q / description
        Previous 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."
      • changedInput schema / properties / select / description
        Previous 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."
      • changedInput schema / properties / universe / description
        Previous 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."
    • Changedtickerbot_search_news16 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Opaque cursor."New value: +"Opaque pagination cursor from a prior response's `next_cursor`."
      • addedInput schema / properties / dir / default
        Added value: +"desc"
      • addedInput schema / properties / from
        Added 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"
        +}
      • changedInput schema / properties / group_by / description
        Previous 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?`."
      • changedInput schema / properties / having / description
        Previous value: -"WHERE-style filter on aggregates. Requires group_by."New value: +"HAVING clause on the aggregate (max 1000 chars). Requires `group_by`."
      • addedInput schema / properties / limit / default
        Added value: +50
      • changedInput schema / properties / limit / description
        Previous value: -"Page size."New value: +"Page size. Max 1000."
      • changedInput schema / properties / order / description
        Previous 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)."
      • changedInput schema / properties / q / description
        Previous 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."
      • changedInput schema / properties / search / description
        Previous 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."
      • changedInput schema / properties / select / description
        Previous 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`)."
      • addedInput schema / properties / ticker
        Added value: +{
        +  "description": "Articles mentioning this symbol (ANDed with `q`).",
        +  "type": "string"
        +}
      • addedInput schema / properties / tickers
        Added value: +{
        +  "description": "Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.",
        +  "type": "string"
        +}
      • addedInput schema / properties / to
        Added value: +{
        +  "description": "Articles strictly before this instant — same strict ISO subset, matching `/v2/events`. (`until` accepted as an alias.)",
        +  "type": "string"
        +}
      • addedInput schema / properties / universe
        Added value: +{
        +  "description": "Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.",
        +  "type": "string"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "q"
        -]
    • Changedtickerbot_subscribe_events12 fields changed
      • changedInput schema / properties / channel / description
        Previous value: -"Delivery channel."New value: +"Delivery channel. `slack` is reserved and returns `501`."
      • changedInput schema / properties / device_id / description
        Previous 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`."
      • changedInput schema / properties / discord_url / description
        Previous 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."
      • changedInput schema / properties / event_q / description
        Previous 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`."
      • changedInput schema / properties / kinds / description
        Previous 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."
      • addedInput schema / properties / kinds / enum
        Added value: +[
        +  "earnings",
        +  "dividend",
        +  "split",
        +  "insider",
        +  "analyst"
        +]
      • changedInput schema / properties / name / description
        Previous value: -"Display name."New value: +"Display name. Defaults to `events: <kinds> · <scope>`."
      • changedInput schema / properties / q / description
        Previous 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."
      • changedInput schema / properties / target_url / description
        Previous 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."
      • addedInput schema / properties / ticker
        Added value: +{
        +  "description": "Single-symbol shorthand for `tickers`.",
        +  "type": "string"
        +}
      • changedInput schema / properties / tickers / description
        Previous 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."
      • changedInput schema / properties / universe / description
        Previous 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."
    • Changedtickerbot_subscribe_scan11 fields changed
      • changedInput schema / properties / cadence / description
        Previous 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`."
      • changedInput schema / properties / channel / description
        Previous 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`."
      • addedInput schema / properties / columns
        Added 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"
        +}
      • changedInput schema / properties / device_id / description
        Previous 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`."
      • addedInput schema / properties / dir
        Added value: +{
        +  "default": "desc",
        +  "description": "Sort direction for `order`.",
        +  "enum": [
        +    "asc",
        +    "desc"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / discord_url / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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>`."
      • addedInput schema / properties / order
        Added 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"
        +}
      • changedInput schema / properties / q / description
        Previous 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."
      • changedInput schema / properties / target_url / description
        Previous 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."
      • changedInput schema / properties / universe / description
        Previous 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`."
    • Changedtickerbot_subscribe_signal13 fields changed
      • changedInput schema / properties / cadence / description
        Previous 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`."
      • changedInput schema / properties / channel / description
        Previous 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."
      • addedInput schema / properties / columns
        Added 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"
        +}
      • changedInput schema / properties / condition / description
        Previous 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)."
      • changedInput schema / properties / device_id / description
        Previous 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`."
      • addedInput schema / properties / dir
        Added value: +{
        +  "default": "desc",
        +  "description": "Sort direction for `order`.",
        +  "enum": [
        +    "asc",
        +    "desc"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / discord_url / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous 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."
      • addedInput schema / properties / order
        Added 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"
        +}
      • changedInput schema / properties / signal / description
        Previous value: -"Column name (e.g. golden_cross_today, rsi_14)."New value: +"Signal name from the schema (case-insensitive)."
      • changedInput schema / properties / target_url / description
        Previous 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."
      • changedInput schema / properties / ticker / description
        Previous value: -"Optional ticker to restrict the watch to one symbol."New value: +"Restrict to a single ticker. Default: any ticker."
      • changedInput schema / properties / universe / description
        Previous 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`."
    • Changedtickerbot_subscribe_ticker13 fields changed
      • changedInput schema / properties / cadence / description
        Previous 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`."
      • changedInput schema / properties / channel / description
        Previous 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."
      • addedInput schema / properties / columns
        Added 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"
        +}
      • changedInput schema / properties / condition / description
        Previous 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."
      • changedInput schema / properties / device_id / description
        Previous 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`."
      • addedInput schema / properties / dir
        Added value: +{
        +  "default": "desc",
        +  "description": "Sort direction for `order`.",
        +  "enum": [
        +    "asc",
        +    "desc"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / discord_url / description
        Previous 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."
      • changedInput schema / properties / name / description
        Previous value: -"Human-readable label."New value: +"Human-readable label (up to 80 chars). Defaults to `<TICKER>: <query>`."
      • addedInput schema / properties / order
        Added 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"
        +}
      • addedInput schema / properties / q
        Added value: +{
        +  "description": "WHERE-clause fragment using signal names from the schema — the same grammar as /v2/scan. (`condition` accepted as an alias.)",
        +  "type": "string"
        +}
      • changedInput schema / properties / target_url / description
        Previous 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)."
      • changedInput schema / properties / ticker / description
        Previous 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."
      • changedInput schema / required
        Previous value: -[
        -  "ticker",
        -  "condition"
        -]New value: +[
        +  "ticker",
        +  "q"
        +]
    • Changedtickerbot_update_custom_signal6 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"New description."New value: +"New description. Not derived from `expr` — change both if the prose describes a threshold you are moving."
      • changedInput schema / properties / expr / description
        Previous 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."
      • removedInput schema / properties / name
        Removed value: -{
        -  "description": "CURRENT slug — identifies which signal to edit.",
        -  "type": "string"
        -}
      • changedInput schema / properties / new_name / description
        Previous 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."
      • addedInput schema / properties / signal
        Added value: +{
        +  "description": "Custom signal slug (the signal name). A built-in name answers 404 — built-ins are read-only.",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "name"
        -]New value: +[
        +  "signal"
        +]
    • Changedtickerbot_update_universe3 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"New notes."New value: +"New notes. Max 500 characters."
      • changedInput schema / properties / name / description
        Previous value: -"New label."New value: +"New label. Non-empty, max 80 characters."
      • changedInput schema / properties / tickers / description
        Previous 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)."
    • Addedtickerbot_update_webhook
  9. 3 tool updates
    • Changedtickerbot_get_series1 field changed
      • changedInput schema / properties / transitions_only / description
        Previous 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."
    • Changedtickerbot_list_events2 fields changed
      • changedInput schema / properties / kind / description
        Previous 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`."
      • changedInput schema / properties / merge_gap_seconds / description
        Previous 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."
    • Changedtickerbot_update_custom_signal1 field changed
      • changedInput schema / properties / new_name / description
        Previous 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)."
  10. 3 tool updates
    • Changedtickerbot_get_ticker_bars2 fields changed
      • addedInput schema / properties / adjusted
        Added 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"
        +}
      • addedInput schema / properties / session
        Added 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"
        +}
    • Changedtickerbot_list_events2 fields changed
      • addedInput schema / properties / merge_gap_seconds
        Added 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"
        +}
      • changedInput schema / properties / signal / description
        Previous 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."
    • Removedtickerbot_list_signal_events
  11. 2 tool updates
    • Changedtickerbot_get_ticker2 fields changed
      • changedInput schema / properties / asof / description
        Previous 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."
      • changedInput schema / properties / ticker / description
        Previous 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."
    • Changedtickerbot_list_tickers7 fields changed
      • changedInput schema / properties / asset_type / description
        Previous 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."
      • changedInput schema / properties / exchange / description
        Previous 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)."
      • removedInput schema / properties / min_market_cap
        Removed value: -{
        -  "description": "Minimum market cap in USD. Orders results by market_cap desc.",
        -  "type": "number"
        -}
      • changedInput schema / properties / search / description
        Previous 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."
      • removedInput schema / properties / sector
        Removed value: -{
        -  "description": "Exact-match sector filter (e.g. \"Technology\").",
        -  "type": "string"
        -}
      • removedInput schema / properties / tickers
        Removed value: -{
        -  "description": "Comma-separated symbols (max 50). When set, returns full rows for these symbols and pagination params are ignored.",
        -  "type": "string"
        -}
      • removedInput schema / properties / universe
        Removed value: -{
        -  "description": "Restrict to a universe slug (top_10, top_100, or a saved one).",
        -  "type": "string"
        -}
  12. 1 tool update
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / order / description
        Previous value: -"Sort column. Default day_change_pct (rows) / tickers (aggregate)."New value: +"Sort column. Default: the 1-day change column (rows) / tickers (aggregate)."
  13. 2 tool updates
    • Changedtickerbot_get_signals_match1 field changed
      • changedInput schema / properties / include_active_since / description
        Previous 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)."
    • Changedtickerbot_list_signal_events1 field changed
      • changedInput schema / properties / from / description
        Previous 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."
  14. 1 tool update
    • Changedtickerbot_list_signals_catalog2 fields changed
      • changedInput schema / properties / kind / description
        Previous 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`.)"
      • changedInput schema / properties / kind / enum
        Previous value: -[
        -  "builtin",
        -  "expression"
        -]New value: +[
        +  "builtin",
        +  "custom"
        +]
  15. 3 tool updates
    • Changedtickerbot_list_events1 field changed
      • changedInput schema / properties / group_by / description
        Previous 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)."
    • Changedtickerbot_scan1 field changed
      • changedInput schema / properties / group_by / description
        Previous 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."
    • Changedtickerbot_search_news1 field changed
      • changedInput schema / properties / group_by / description
        Previous 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."
  16. 1 tool update
    • Changedtickerbot_list_webhooks2 fields changed
      • changedInput schema / properties / status / description
        Previous 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."
      • changedInput schema / properties / status / enum
        Previous value: -[
        -  "active",
        -  "pending_verification",
        -  "disabled"
        -]New value: +[
        +  "active",
        +  "disabled"
        +]
  17. 1 tool update
    • Changedtickerbot_search_news2 fields changed
      • changedInput schema / properties / q / description
        Previous value: -"SQL WHERE on news_article. Required."New value: +"SQL WHERE on news_article. Optional when search or a scoping param is present."
      • addedInput schema / properties / search
        Added 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"
        +}
  18. 1 tool update
    • Changedtickerbot_subscribe_events1 field changed
      • removedInput schema / properties / cadence
        Removed 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

Related MCP Servers

  • F
    license
    A
    quality
    C
    maintenance
    Read-only SQL MCP server for quantitative finance data (DuckDB) with stock quotes, financials, and historical K-lines, enabling cross-database JOIN queries.
    2
    1
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides 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.
    1
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Real-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.
    22
    3 npm
    MIT
  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Real-time financial market data MCP server. Stocks, crypto, technicals, sentiment, FDA calendar. No API keys required.
    -
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.