RoChartdocs
Reference

Query language

The JSON behind every chart — trends, funnels, retention, raw events, SQL and game analytics — with filters, breakdowns and date ranges.

Every chart in RoChart is a JSON object with a kind. The insight editor builds it, saved insights store it, and agents send it to the MCP query tool or POST /api/projects/:projectId/query. That means an agent can rebuild any chart you see, and you can open any insight an agent saved. You only send what differs from the defaults.

kindAnswersCan be saved as an insight
trendsCounts or totals over timeYes
funnelConversion through ordered stepsYes
retentionDo people come back?Yes
pathsCommon sequences of pages or eventsYes
lifecycleNew, returning, resurrecting and dormant peopleYes
stickinessHow many days people did itYes
eventsRaw events, newest firstNo
sqlAnything else, in read-only SQLNo
game_engagement, game_acquisition, game_retention, game_monetization, game_liveThe Game analytics pageNo

Every result includes a summary: a few lines of plain text with the numbers that matter.

Building blocks

Date ranges

{ "dateRange": { "from": "-30d" } }

from takes -30min, -1h, -3h, -24h, -7d, -12w, -6m, -1y, today, yesterday, mtd, ytd, all, or an ISO date or date and time such as 2026-10-01 or 2026-10-01T09:30. min is minutes and m is months. to is optional and defaults to now. A date in to includes that whole day; a date and time ends exactly then. Ranges and times without an offset follow the project timezone. The default is the last 30 days (7 days for events and game analytics).

{ "dateRange": { "from": "2026-11-02T09:00", "to": "2026-11-02T17:30" } }

Intervals

minute, 5min, 10min, hour, day (default), week or month. Weeks start on Monday. Minute intervals work for up to about 1 day (minute), 1 week (5min) and 2 weeks (10min); hourly intervals for up to 31 days. With a minute interval, -1h starts on a minute boundary and -3h on a 5-minute one, so every bucket is whole except the current one.

Filters

{ "type": "event", "key": "$robux", "operator": "gte", "value": 100 }
typeFilters on
event (default)A property of the event, such as mode or $device_type
personThe player's current property, such as premium or level
groupA property of the event's group. Add groupTypeIndex.
cohortMembership: operator in or not_in, value the cohort id
OperatorMeaning
eq / neqEquals / doesn't equal. An array works like in.
in / not_inIs (not) one of the values in an array
contains / not_containsContains the text (not case-sensitive)
regexMatches a regular expression
gt / gte / lt / lteNumber comparisons
is_set / is_not_setHas a value / doesn't. Leave out value.

All filters in a list must match. For "either/or", use in with several values, or a cohort that matches any rule.

Breakdowns

{ "breakdown": { "type": "person", "key": "platform", "limit": 5 } }

type is event, person, group (add groupTypeIndex) or cohort (with cohortIds, up to 10). limit keeps the most common values (10 by default, up to 50). Empty values show as (none).

Shared fields

trends, funnel, retention and events also take filters (applied everywhere) and cohortId (only people in that cohort).

{
  "kind": "trends",
  "series": [{ "event": "$purchase", "math": "sum", "mathProperty": "$robux" }],
  "interval": "week",
  "dateRange": { "from": "-12w" },
  "compare": true
}
Field
series1–10 series: { event, math?, mathProperty?, groupTypeIndex?, filters?, name? }. event: null means all events.
mathtotal (default), unique_persons, unique_sessions, unique_groups (with groupTypeIndex), or sum, avg, min, max, median, p90, p99 (with mathProperty)
intervalSee above
breakdownOptional
comparetrue adds the previous period
displayline, area, bar, number or table. Only changes how the app draws it.

The result has labels (bucket starts) and, per series, data (one value per label) and total (over the whole range). incompleteFrom is the index of a bucket that's still in progress.

summary
$purchase · sum($robux): total 1,811,500; last complete week 427,391 (+0.6% vs previous week); peak 427,391 on 2026-10-26.
The last week is still in progress.

funnel

{
  "kind": "funnel",
  "steps": [{ "event": "$session_start" }, { "event": "tutorial_completed" }, { "event": "$purchase" }],
  "windowDays": 7,
  "dateRange": { "from": "-30d" }
}
Field
steps2–12 steps: { event, name?, filters? }
windowDaysDays to convert from the first step, 1–365 (default 14)
orderordered (default) or strict
breakdownOptional. The value comes from the first step.
aggregationGroupTypeIndexCount groups (such as clans) instead of people

Each step in the result has count, conversionFromStart, conversionFromPrevious and dropoff.

summary
$session_start 1,759 → tutorial_completed 625 (35.5%) → $purchase 194 (31.0%)
Overall conversion 11.0%. Biggest drop-off: $session_start → tutorial_completed (64.5% lost).

retention

{
  "kind": "retention",
  "startEvent": { "event": "tutorial_completed" },
  "returnEvent": { "event": "$session_start" },
  "period": "day",
  "periods": 8
}

period is day, week (default) or month. periods is the number of columns, 2–30 (default 8). Each cohort in the result has a start, a size and values, the number of people who returned in each period. average is the weighted retention per period.

paths

{ "kind": "paths", "include": "events", "startPoint": "$session_start", "steps": 5, "per": "session", "dateRange": { "from": "-7d" } }

include is events, pageviews (for website data) or both. startPoint and endPoint take an event name or a page path, and endPoint returns the steps leading up to it. steps is 2–8, and per is session or person. The result has nodes (id is "<step>:<name>"), links between them, and total.

lifecycle

{ "kind": "lifecycle", "event": { "event": "$session_start" }, "interval": "week", "dateRange": { "from": "-8w" } }

Returns labels and, per period, new, returning, resurrecting and dormant counts. The current period's dormant is 0 until it ends.

stickiness

{ "kind": "stickiness", "series": [{ "event": "match_ended" }], "interval": "day", "dateRange": { "from": "-30d" } }

For each event, counts[i] is how many people were active on exactly i + 1 days (or weeks, with "interval": "week"). periods is the number of days or weeks in the range.

events

{ "kind": "events", "event": "$purchase", "limit": 50 }

Optional fields are event, personId, distinctId, groupTypeIndex + groupKey, limit (up to 1,000, default 100) and before. When truncated is true, pass the last row's timestamp as before to get the next page.

sql

{ "kind": "sql", "sql": "SELECT properties.$device_type AS platform, uniq(person_id) AS players FROM events WHERE event = '$session_start' GROUP BY platform" }

One read-only SELECT (or WITH … SELECT) over your project's data, in ClickHouse SQL. Times are in your project's timezone, and person_id already accounts for merged identities, so uniq(person_id) counts real people. The SQL Editor lists every table, column and property.

TableOne row per
eventsEvent. event, distinct_id, person_id, session_id, timestamp, properties, device_type, country, group_0…group_4, plus web columns (pathname, referrer_domain, utm_*, browser, os) for website events
personsPerson. id, properties, created_at
sessionsSession, by $session_id: started_at, duration_seconds and more
groupsClan or other group. group_type_index, group_key, properties
Any saved queryWhatever it returns: SELECT * FROM paying_players
ShorthandMeans
properties.mode, properties['my key']A property as text
toFloat64OrNull(properties.$robux)A property as a number
person.properties.name (on events)The player's property
inCohort(person_id, 3)Whether the person is in cohort 3

A query runs for at most 20 seconds and returns at most 10,000 rows. Add "display": { "type": "line", "x": "day", "y": ["users"] } to say how the result should be charted (table, line, bar or number).

Game analytics

The Game analytics page runs on five kinds, one per tab plus the live strip, computed from the Roblox events with the definitions on that page.

{ "kind": "game_engagement", "dateRange": { "from": "-28d" }, "breakdown": "platform", "filters": [{ "dimension": "join_source", "values": ["friend", "referral"] }] }
KindFieldsReturns
game_engagementdateRange, interval (hour or day), breakdown, filters, compareDAU, new vs returning, playtime, session time, sessions, MAU and peak concurrent players
game_acquisitiondateRange, by, filtersNew players by first join source, campaign, country, platform or locale, with each source's D1/D7 retention, playtime and revenue per new player
game_retentiondateRange (cohort days), breakdown, filters, daysD1, D7 and D30 per cohort day, stickiness and the cohort table
game_monetizationdateRange, interval, filters, compareRobux revenue, purchases, payers, payer conversion, ARPDAU, ARPPU and top products
game_livefiltersPlayers in game now, servers, concurrent players over the last hour, and today so far vs yesterday at the same time

Filters and breakdowns here use player dimensions instead of event properties: platform, country, locale, join_source, campaign, new_vs_returning, premium or server_type, taken from the session's $session_start. These kinds are new and their fields may still change; the MCP query tool always describes the current ones.

Cohort definitions

Cohorts aren't a query kind, but their rules use the same building blocks:

{
  "match": "all",
  "criteria": [
    { "type": "behavior", "event": "$purchase", "withinDays": 90, "minCount": 2 },
    { "type": "person_property", "key": "platform", "operator": "in", "value": ["Phone", "Tablet"] },
    { "type": "cohort", "cohortId": 3, "negate": true }
  ]
}
RuleFields
person_propertykey, operator, value
behaviorevent, withinDays (default 30), minCount (default 1), performed (false = did not do it), sumProperty + minSum (a total, such as $robux ≥ 500), filters
cohortcohortId, negate

match is all (default) or any, with 1–20 rules.

Errors

An invalid query returns 400 invalid_query with a message that names the field, and usually a hint:

{ "error": "invalid_query", "message": "Invalid date \"last week\"", "hint": "Use relative values like \"-7d\", \"-24h\", \"-12w\", \"-6m\", \"mtd\", \"ytd\", \"today\", or an ISO date \"2026-01-31\"" }

On this page