Tools
An agent has thirty-six tools. Twenty-three only read. Thirteen change data: they create, edit and delete dashboards and broadcasts, and prepare, schedule, start and recall broadcasts.
metriox_filter_catalog, metriox_filter_values, metriox_get_bot_activity,
metriox_get_bot_profile, metriox_list_segments and metriox_query_sessions exist and are available to the agent, but have
no entries yet. All six only read.
Normally the agent picks the tool itself and you do not need to know these names. The list is useful for understanding the limits and for phrasing requests more precisely.
The project comes from the token, not from a parameter. You cannot pass someone else's project into a call: there is no project id among the parameters at all.
Filter
Seven of the tools below take an optional filter — one line holding a condition. Without it they count
every event the bot recorded in the range.
event.name = "purchase" and user.tg.is_premium = "true"
What stands to the left of the operator says what the condition is about:
| Starts with | What it asks about |
|---|---|
event.name, event.type, event.origin, event.text, event.user_id, event.source.channel | Fields of the event itself |
event.tg.<key>, event.source.<key>, event.lookup.<key>, event.web.<key>, event.sender.<key> | Built-in event properties |
event.props.<key> | The properties your bot sends |
user.tg.<key>, user.meta.<key>, user.props.<key> | The person's profile as it stands now, not as it stood at the time of the event |
user.events(<event conditions>, last: 30d).count | What the person did: count, active_days, unique_events, sum(event.props.<key>, channel: "telegram_bot_api"), busiest_day.count |
Operators: =, !=, <, <=, >, >=, contains, startswith, in ["a", "b"], not in, plus
and, or, not and parentheses. Text is quoted; numbers, true/false and dates (2026-09-01) are
not. The quotes decide how the value is compared: event.tg.chat_id = 100 compares numbers,
event.tg.chat_id = "100" compares text.
Property names differ from bot to bot. metriox_filter_catalog lists them, already spelled the way a
filter writes them (filterName). That matters more than it sounds: a mistaken prefix —
event.props.country where event.lookup.country was meant — is not an error but an empty answer,
because a property by that name was simply never sent.
The answer repeats the filter. When filter is given, the response carries filterApplied — the same
condition in canonical form — and filterWarnings. It is the guard against a filter that parsed correctly
but asked the wrong thing: filterApplied shows which question the numbers actually answer.
A mistake in the line is a refusal, not an empty answer. The agent gets a code and the character offsets, and no query runs:
The filter was not accepted:
- value_type_mismatch (characters 13-14): event.name takes a quoted string.
Nothing was queried. Fix the expression and call again.
user.events()with nothing inside counts every event, including the messages the bot itself sent — that one raises a warning.event.user_idin a group is the chat's id, not the person's. Count people with theUsersmetric instead.user.events(...)with nolast:,before:orfrom:counts over the same range as the query itself.
metriox_list_bots
The project's bots.
No parameters. Returns each bot's id and name.
Almost all work starts here: the other tools need a botId, and only bots from this list are reachable
by the token.
metriox_get_project_overview
Event quota and usage for the current billing period: the limit, how many events and bytes have been used, what remains, and the period bounds.
No parameters.
Answers questions like "are we within the limit?" and "how many events are left before the period ends?".
metriox_search_events
Pages through one bot's raw events over a time range, newest first.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC, for example 2026-09-01T00:00:00Z |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
limit | no | Events per page, 1 to 100. Defaults to 50 |
cursor | no | Cursor from a previous call, for the next page |
filter | no | A condition over events and people — see Filter above |
At most 100 events come back per call. If there are more, the response carries a cursor and the agent requests the next page itself.
This is the tool for investigating specific cases, such as "show payment error events from yesterday".
For counting, use metriox_query_kpi: it aggregates server-side and does not spend the agent's context
walking events.
metriox_query_kpi
A single number for one bot over a time range.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
metric | no | What to count, see below |
filter | no | A condition over events and people — see Filter above |
Values for metric:
Events: every event in the rangeUsers: distinct users active in the rangeEventsPerUser: the ratio of the twoNewUsers: users whose first ever event falls in the range
metriox_query_series
A metric over time for one bot, bucketed by hour, day, week or month.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
metric | no | Events, Users, EventsPerUser or NewUsers. Defaults to Events |
granularity | no | Auto, Hour, Day, Week or Month. Defaults to Auto |
splitBy | no | Split into one line per dimension value: event_name, source, event_origin or event_type |
seriesLimit | no | How many lines to keep when splitBy is set, by size. Defaults to 10 |
filter | no | A condition over events and people — see Filter above |
This is the tool for "how did it change" questions and for comparing periods. Auto almost always
picks a sensible bucket on its own.
metriox_query_top
The largest values of a dimension over a range: which events fire most, which sources bring the most traffic, or the most common value of a custom property.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
metric | no | Events, Users, EventsPerUser or NewUsers. Defaults to Events |
groupBy | no | event_name, source, event_origin or event_type. Defaults to event_name |
detailKey | no | Group by a custom event property instead of a built-in dimension, for example $tg.callback_data. Takes precedence over groupBy |
limit | no | How many rows to return, 1 to 100. Defaults to 20 |
includeTotal | no | Also return the total across every value, not just the returned rows. Costs a second scan, so it is off by default |
filter | no | A condition over events and people — see Filter above |
metriox_query_funnel
Conversion through an ordered sequence of events: how many people reached each step and where they drop off.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
steps | yes | Event names in order, between 2 and 10. Discover the names with metriox_query_top grouped by event_name |
mode | no | UniqueUsers counts anyone who did each step at any point in the range; Ordered requires the steps to have happened in sequence. Defaults to UniqueUsers |
filter | no | A condition over events and people — see Filter above. It applies to every event the funnel reads and narrows the population; it does not define a step |
A step is an event name. More complex step conditions are not available over MCP yet.
metriox_query_retention
Retention: of the people who first appeared in each period, how many came back later.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range in which cohorts are formed, inclusive, ISO-8601 UTC |
toUtc | yes | End of the cohort range, exclusive, ISO-8601 UTC |
granularity | no | Cohort and bucket size: Day, Week or Month. Defaults to Day |
windowUnits | no | How many buckets to follow each cohort for, 1 to 90, in units of the granularity. Defaults to 30 |
filter | no | A condition over events and people — see Filter above. It applies to every event the cohorts are built from, both joining a cohort and coming back |
Each cohort comes back with two series, and taking the wrong one produces a wrong curve with no sign anything went wrong:
returnedRateByBucketis per period: the share who came back in exactly that bucket.cumulativeReturnedRateByBucketis within N: the share who came back at least once by that bucket.
You cannot sum the first to get the second: each figure is its own distinct count, so anyone active in more than one period would be counted twice. Bucket 0 is the cohort's own period and is always the whole cohort.
Filters on the starting and returning events are not available over MCP yet: the tool measures retention across any event.
metriox_list_users
Pages through the people who used the bot in a range: how many events each has, and when they were first and last seen.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
search | no | Case-insensitive substring over the user id and their Telegram username, first name and last name. This is how you find one specific person |
sortBy | no | UserId, EventsCount, FirstSeen or LastSeen. Without it the default order applies, which is the cheapest for deep paging |
sortDirection | no | Asc or Desc when sortBy is set. Defaults to Desc |
limit | no | Users per page, 1 to 100. Defaults to 50 |
cursor | no | Cursor from a previous call |
filter | no | A condition over events and people — see Filter above |
metriox_get_conversation
The message history between the bot and one user, newest first: what the person sent, what the bot replied, which buttons were offered and which were pressed.
| Parameter | Required | Description |
|---|---|---|
botId | yes | A bot from metriox_list_bots |
platformUserId | yes | The person's numeric Telegram user id, not an @username. Take it from metriox_list_users or metriox_search_events |
fromUtc | yes | Start of the range, inclusive, ISO-8601 UTC |
toUtc | yes | End of the range, exclusive, ISO-8601 UTC |
limit | no | Items per page, 1 to 100. Defaults to 50 |
cursor | no | Cursor from a previous call, for older history |
This is the main tool for investigating a complaint: it shows what actually happened to one person.
metriox_get_dashboard
One dashboard's contents: its saved widget layout.
| Parameter | Required | Description |
|---|---|---|
dashboardId | yes | A dashboard from metriox_list_dashboards |
The shape of data is owned by the frontend rather than the server, and is not yet checked against a
schema server-side, so do not rely on it being stable.
metriox_list_campaigns
The project's broadcast campaigns, newest first, with their status and counts.
| Parameter | Required | Description |
|---|---|---|
botId | no | Only campaigns for this bot. Omit for every bot in the project |
status | no | Draft, Scheduled, Preparing, Prepared, Sending, Completed, Cancelling, Cancelled or Skipped |
limit | no | Campaigns per page, 1 to 50. Defaults to 25 |
afterCreatedAtUtc | no | Cursor from a previous response, paired with afterId |
afterId | no | Cursor from a previous response, paired with afterCreatedAtUtc |
seriesId | no | A repeating broadcast: list the runs it has made instead of the project's campaigns |
A repeating broadcast's runs stay out of the ordinary listing, as they do in the dashboard: each run is a
campaign of its own with its own counts, and seriesId is how to find them. A run in Skipped did not go
out; scheduleFailureCode says why.
metriox_get_campaign
One campaign in full: status, bot, audience definition, message payload and counts.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
metriox_get_campaign_progress
How far a broadcast has got: how many recipients are pending, in flight, sent, failed or skipped.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
Returns the current numbers immediately. To follow a send, the agent calls it again rather than expecting one call to block until something changes.
metriox_get_campaign_results
What a broadcast achieved: how many people received it, replied, blocked the bot and which buttons they pressed. Each recipient is measured over their own window after delivery. Conversion comes back only when the campaign has a goal saved.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | Campaign from metriox_list_campaigns |
windowDays | no | Days after delivery to measure each recipient for, 1-30. Without it: the window saved with the campaign, or 3 |
The goal is not a parameter: it is saved with the campaign so the report shows the same number to everyone. A goal passed in a call would produce a number nobody else could reproduce.
Read unavailable first. For a broadcast that has not been sent (NotStarted), and for one whose
recipient list retention has deleted (RecipientsPurged), every number is zero and means nothing.
Two more fields change what the figures mean: blockTracking of NotCaptured means the bot records no
membership events, so a zero block count is missing data rather than a fact, and accruing means some
recipients' windows are still open and the numbers will rise.
metriox_list_campaign_recipients
The individual recipients of a campaign and what happened to each. This is the tool for "who did not get it, and why".
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
status | no | Only recipients in this status. Omit for all |
limit | no | Recipients per page, 1 to 200. Defaults to 50 |
afterChatId | no | Cursor: the chatId of the last row on the previous page. Only valid paired with afterIsTest |
afterIsTest | no | Cursor: the isTest flag of the last row. Defaults to false. Needed because a chat id is not unique within a campaign |
metriox_create_campaign
Create a broadcast as a draft. Nothing is sent: a draft has to be prepared and then started.
| Parameter | Required | Description |
|---|---|---|
botId | yes | Bot that will send the broadcast |
name | yes | Internal name, not shown to recipients |
text | yes | Message text |
lastDays | no | Only people active in the last N days. Mutually exclusive with allTime and with fromUtc/toUtc |
allTime | no | Everyone the bot has ever seen |
fromUtc / toUtc | no | Absolute audience bounds, only together |
parseMode | no | HTML or MarkdownV2. Omit for plain text |
buttons | no | Buttons, one per row: "label|url" for a link or "label|cb:value" for a callback |
disableWebPagePreview | no | Suppress the link preview |
disableNotification | no | Deliver silently |
excludeRecentlyMessagedDays | no | Frequency cap, 1 to 90: skip anyone this bot already messaged in another broadcast within the last N days. Checked at send time, so the prepared recipient count still includes them; skipped people get the status Skipped |
The audience must be given in exactly one of the three ways. Media, text entities and multi-column keyboards are not available over MCP yet.
metriox_update_campaign
Edit a broadcast while it is still Draft. Once prepared, the payload is frozen and edits are refused.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
name | no | New internal name |
text | no | New text. Replaces the message whole, so pass buttons again to keep them |
parseMode, buttons, disableWebPagePreview, disableNotification | no | As on create |
This tool keeps the campaign's audience exactly as it is, filter, hand-written list and frequency cap included. Who receives a broadcast is decided when it is created: recreate the campaign to change it.
metriox_delete_campaign
Delete a campaign that is still Draft. A campaign that has been sent is a record and is never deleted.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
metriox_prepare_campaign
Freeze the draft's message and build its recipient list. Still sends nothing. Calling it again resumes rather than restarting.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
Refused if the filter matched nobody: a broadcast with zero recipients would complete instantly and look, on the progress panel, exactly like a perfect delivery.
metriox_unprepare_campaign
Withdraw a prepared campaign back to draft and discard its recipient list. Only from Prepared.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
Preparing it again rebuilds the audience, so the recipient set can differ.
metriox_test_send_campaign
Send the campaign's message to one chat, through the identical path a real broadcast uses.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
chatId | yes | Numeric chat id, not an @username |
Use a chat you control. The bot must already have a conversation with it, because Telegram does not let a bot initiate one. The campaign's status does not change.
metriox_start_campaign
Start the broadcast. The message goes to every prepared recipient. Requires admin rights.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign in Prepared |
etag | yes | The etag field from metriox_get_campaign, read immediately before starting |
The etag guards against sending on figures that have moved: if the campaign changed after the agent read
it, the start is refused. Ask a person to confirm before calling this.
metriox_schedule_campaign
Set a draft to send itself later: once, or on a repeating rule. Requires admin rights. The audience is rebuilt and every quota re-checked at the moment it sends, not now, so today's recipient count is only an estimate.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign in Draft |
etag | yes | The etag field from metriox_get_campaign, read immediately before calling |
kind | yes | Once, Daily, Weekly or Monthly |
runAtUtc | for Once | When to send, ISO 8601. At least a minute ahead and at most a year |
localTime | for repeating rules | Time of day as HH:mm in timeZoneId |
timeZoneId | for repeating rules | An IANA time zone, e.g. Europe/Moscow |
weekdays | for Weekly | Day names: ["Monday", "Thursday"]. The same names metriox_get_campaign returns |
monthDay | for Monthly | Day of the month, 1 to 31. A shorter month sends on its last day |
endsOnLocalDate | no | The last day the series may run, yyyy-MM-dd in timeZoneId |
Confirm the time, the rule and the message with a person before calling this. While a broadcast is scheduled it cannot be edited: unschedule it first.
Weekday numbers are refused in place of names: systems number the week differently, and a mix-up would
move every send by a day. Why a send did not happen is in the campaign's scheduleFailureCode; see
Scheduled and repeating sends.
metriox_unschedule_campaign
Cancel a schedule, leaving an editable draft. Nothing is sent and nothing is recalled. Write access is enough.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A scheduled campaign from metriox_list_campaigns |
For a repeating broadcast, the runs it has already made stay; only the rule is removed.
metriox_cancel_campaign
Recall a broadcast: stop sending, and delete what has already gone out. Requires admin rights. Works on a completed campaign too, not only one still sending. Telegram lets a bot delete its own message for 48 hours only, counted per message — see Broadcasts.
| Parameter | Required | Description |
|---|---|---|
campaignId | yes | A campaign from metriox_list_campaigns |
metriox_list_dashboards
The project's dashboards visible to the token's user: names, ids and who created each one.
No parameters.
Returns the list only, not dashboard contents or widget layouts.
metriox_create_dashboard
Creates a dashboard from a list of cards.
| Parameter | Required | Description |
|---|---|---|
name | yes | Name, up to 120 characters |
cards | yes | Cards in order, 1 to 200. Each has a type, and optionally w, h, id |
availability | no | Everyone or DashboardCreator. Defaults to Everyone |
Card fields: type is the widget type (for example kpi, series); w is width in 12-column units,
1 to 12; h is height in row units, 1 to 60; id is your own identifier, generated when omitted.
Omitting w or h gives the card the maximum on that axis. Oversized values are clamped to the
maximum.
The set of widget types is defined by the web app rather than the server, so MCP cannot list them: call
metriox_get_dashboard on an existing dashboard to see which types are in use.
metriox_update_dashboard
Changes a name, visibility or cards. Only what you pass is changed.
| Parameter | Required | Description |
|---|---|---|
dashboardId | yes | A dashboard from metriox_list_dashboards |
name | no | New name |
cards | no | The complete new card list |
availability | no | New visibility |
cards replaces the whole list rather than adding to it. Read the dashboard with
metriox_get_dashboard first and send back the full set you want.
The server does not compare versions or return a conflict: if someone edits the same dashboard in the browser at the same time, whoever writes last wins and the other change is lost.
metriox_delete_dashboard
Deletes a dashboard. This cannot be undone.
| Parameter | Required | Description |
|---|---|---|
dashboardId | yes | A dashboard from metriox_list_dashboards |
What is not in the list
Over MCP these are not available yet:
- cohorts, histograms and heatmaps;
- user profiles and distributions over their properties;
- managing bots, project members or the plan.
Some of these will arrive in later versions. Managing bots, members and billing is unavailable by design: see the rights boundary.