iblai-api-analytics
Read ibl.ai analytics from the platform API. One /api/analytics/ family serves
every scope; the query params decide what you get:
- Agent (chat) analytics — add
mentor_unique_id to scope any chat metric
(topics, sessions, conversations, ratings, costs, transcripts) to one agent.
- Content analytics —
/api/analytics/content/, keyed by metric=courses |
programs | pathways | skills, for catalog engagement and time spent.
- Organization-wide analytics — omit
mentor_unique_id for org totals.
- Per-user analytics — a single user's enrollments, grades, time spent,
engagement, and cross-platform snapshot.
Reads are read-only; the only writes are Data Reports and a time-spent event.
The schema is the contract
These endpoints live on the Data Manager service and its live OpenAPI schema
is the single source of truth — the URLs and params below exist for orientation
and can drift between releases. Validate against the schema before building
requests:
- Schema (raw):
https://api.iblai.app/dm/api/docs/schema/
- Swagger UI:
https://api.iblai.app/dm/api/docs/
# Confirm the analytics paths you're about to call exist verbatim:
curl -sS "https://api.iblai.app/dm/api/docs/schema/" -o /tmp/iblai_schema.yaml
grep -nE "^ /api/analytics/" /tmp/iblai_schema.yaml
Treat any mismatch between this skill and the schema as a bug in the skill — the
deployed schema wins.
Auth & conventions
dm_url = https://api.iblai.app/dm — analytics are Data Manager
endpoints reached through the gateway's /dm prefix. Every URL below is written
as {dm_url}/api/analytics/<path>; set dm_url=https://api.iblai.app/dm and use it
as the prefix.
- Header:
Authorization: Api-Token $IBLAI_API_KEY on every request.
- Placeholders:
{platform} = your workspace key = $IBLAI_ORG. On the wire it is the
platform_key query param and the orgs/{platform} / platforms/{platform}
path segment — same value everywhere.
{username} = $IBLAI_USERNAME.
{mentor} = an agent's unique id — optional: include
mentor_unique_id={mentor} for agent scope, omit for org-wide.
- Shared query params (available on most endpoints):
platform_key={platform} — the workspace key (required on most reads).
date_filter — today | 7d | 30d | 90d | all_time | custom.
custom requires start_date and end_date (both yyyy-MM-dd).
mentor_unique_id={mentor} — narrows any chat metric to one agent.
granularity — hour | day | week | month (time-series endpoints).
usergroup_ids — repeat to narrow results to specific user groups.
page / limit — pagination on list/details endpoints.
- Each endpoint below lists its own params; required ones are marked and enum
values are given inline.
[¶m=value] means optional.
- Not connected yet? Run
/iblai-api-login first.
Reads
Agent & org chat analytics
The presence of mentor_unique_id is the only difference between agent scope and
org-wide scope on every endpoint here.
- GET
{dm_url}/api/analytics/topics/?platform_key={platform}&date_filter=30d&metric=overview[&mentor_unique_id={mentor}][&granularity=day][&usergroup_ids=]
— Messages / Topics / Conversations KPIs.
metric ∈ overview (default) | sessions | ratings | highlighted.
- GET
{dm_url}/api/analytics/topics/details/?platform_key={platform}&date_filter=30d&page=1&limit=20&search=[&mentor_unique_id={mentor}]
— topics table / bar chart.
- GET
{dm_url}/api/analytics/conversations/?platform_key={platform}&date_filter=30d&metric=conversations[&mentor_unique_id={mentor}][&granularity=day]
— conversation counts. metric ∈ conversations | headline.
- GET
{dm_url}/api/analytics/sessions/?platform_key={platform}&date_filter=30d&metric=sessions[&mentor_unique_id={mentor}][&granularity=day]
— sessions line chart. metric ∈ sessions | headline.
- GET
{dm_url}/api/analytics/ratings/?platform_key={platform}&date_filter=30d&metric=ratings[&mentor_unique_id={mentor}][&granularity=day]
— thumbs / rating breakdown.
- GET
{dm_url}/api/analytics/time/?platform_key={platform}&date_filter=30d[&mentor_unique_id={mentor}][&granularity=hour]
— access-time heatmap.
- GET
{dm_url}/api/analytics/users/?platform_key={platform}&metric=active_users&date_filter=30d[&mentor_unique_id={mentor}]
— user KPIs. metric (required) ∈ currently_active | active_users |
registered_users | active_users_last_30d.
- GET
{dm_url}/api/analytics/users/details/?platform_key={platform}&date_filter=30d&page=1&limit=5&search=[&mentor_unique_id={mentor}]
— user table.
Transcripts
- GET
{dm_url}/api/analytics/messages/?platform_key={platform}&search={q}&topic={topic}&sentiment={s}&min_messages=&max_messages=&page=1&limit=20[&mentor_unique_id={mentor}][&start_date=&end_date=]
— transcript list (one row per session). search matches the user's
email or username and the session's first user message. Each row carries
the user's email.
- GET
{dm_url}/api/analytics/messages/details/?platform_key={platform}&session_id={id}[&mentor_unique_id={mentor}]
— one full transcript. session_id required. Returns summary +
messages[]. Beyond human/ai, every AI turn carries the extended
per-turn context: documents (retrieved sources, normalized, or null),
tool_calls ([{name, input, output}]; [] when none), metadata
({client, llm_model, llm_provider, llm_temperature}, or null), and
request_context (the credential-stripped request payload, or null).
Content analytics (courses / programs / pathways / skills)
Catalog engagement and time-spent, org-wide by default. Add mentor_unique_id
to scope to content consumed via one agent.
- GET
{dm_url}/api/analytics/content/?platform_key={platform}&metric=courses&date_filter=30d&include_overtime=false&page=1&limit=20[&mentor_unique_id={mentor}][&granularity=hour][&usergroup_ids=]
— aggregated content analytics + paginated item list. metric (required)
∈ course/courses | program/programs | pathway/pathways |
skill/skills. include_overtime=true adds a 7-day time-spent series
(courses only).
- GET
{dm_url}/api/analytics/content/details/{content_id}/?platform_key={platform}&metric=courses&date_filter=30d&search=&page=1&limit=20[&time_metric=][&mentor_unique_id={mentor}]
— detailed analytics for one content item (summary + per-user rows +
optional time series). metric required.
Costs
- GET
{dm_url}/api/analytics/financial/?platform_key={platform}&metric=total_costs&date_filter=30d[&show_overtime=false][&comparison_days=][&fill_method=zero][&provider=][&llm_model=][&username=][&mentor_unique_id={mentor}]
— cost KPIs / cost-per-day. metric (required) ∈ total_costs |
weekly_costs | monthly_costs. fill_method ∈ zero | previous.
- GET
{dm_url}/api/analytics/financial/details/?platform_key={platform}&group_by=provider&date_filter=30d&metrics=total_costs,sessions&page=1&limit=20&search=[&provider=][&llm_model=][&username=][&mentor_unique_id={mentor}]
— cost breakdown. group_by (required) ∈ provider | llm_model |
username | mentor | platform | action. metrics is comma-separated.
- GET
{dm_url}/api/analytics/financial/invoice/?platform_key={platform}&start_date=&end_date=&include_breakdown=true[&username={username}][&usergroup_ids=]
— invoice-style billing summary with optional per-line breakdown.
LLM usage (cost / tokens / latency)
Fine-grained LLM cost/usage/latency for the UI, proxied from the tracing
backend and tenant-scoped server-side (the platform filter is injected from
your key, never taken from input). One GET {dm_url}/api/analytics/llm-usage/
serves three resources via the resource param; every measures, aggregation,
group_by, and order_by value is whitelisted (a bad value is a 400).
- GET
{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=metrics&measures=total_cost&aggregation=sum&date_filter=30d[&group_by=model][&granularity=day][&view=observations][&order_by=total_cost][&direction=desc][&limit=50][&mentor_unique_id={mentor}][&username=][&session_id=][&trace_name=][&llm_model=]
— aggregate metrics (default resource). measures (comma-separated) ∈
total_cost | input_tokens | output_tokens | total_tokens | count |
latency. aggregation ∈ sum | avg | count | min | max | p50 |
p75 | p90 | p95 | p99. group_by ∈ type | model | tags |
environment. granularity ∈ day | week | month (omit for a flat
aggregate — there is no hour). view ∈ observations (default) | scores.
- GET
{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=observations&page=1&limit=50[&username=][&trace_name=][&llm_model=][&trace_id={id}][&observation_id={id}]
— row-level generation drill-down, page-paginated (meta carries totalItems
/ totalPages). &trace_id={id} scopes to one trace's observations;
&observation_id={id} fetches one observation. session_id is rejected
400 here (observations can't filter by session — scope via resource=traces
then drill in by trace_id); mentor_unique_id is likewise rejected on
observations.
- GET
{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=traces&order_by=timestamp&direction=desc&page=1&limit=50[&username=][&session_id=][&trace_name=][&llm_model=][&trace_id={id}][&mentor_unique_id={mentor}]
— per-user / per-session trace listing (the drill-down the Metrics API can't
group by). order_by ∈ timestamp (default) | name | username |
session_id; direction ∈ asc | desc. &trace_id={id} fetches one trace.
Unlike observations, traces are agent-scopable via mentor_unique_id.
All three return the same envelope: { "resource": "…", "data": [...], "meta": {...} }.
limit caps at 1000. A get-by-id (trace_id on traces, observation_id on
observations) whose record belongs to another organization returns 404 — existence
is never leaked. An unreachable/erroring tracing backend returns 502 (distinct
from a 500 bug). date_filter=all_time on metrics falls back to a fixed lower
bound (the backing Metrics API requires bounded timestamps).
Per-user analytics
A single user's own learning data. RBAC-gated, with a self-access bypass:
a user reading their own data (username == the caller, or no username on
/analytics/user) needs no analytics grant; reading someone else's requires a
grant.
- GET
{dm_url}/api/analytics/user?platform_key={platform}&username={username}&metrics=courses&date_filter=30d[&course_id=][&include_edx_progress=true][&overtime=false][&program_id=][&pathway_id=][&granularity=day][&mentor_unique_id={mentor}]
— the signed-in user's own holistic snapshot (self-access; no grant needed).
Same shape as learner/details. metrics is comma-separated sections:
courses (default), programs, pathways, agents, skills, credentials,
time_spent.
- GET
{dm_url}/api/analytics/learner/details?platform_key={platform}&username={username}&metrics=courses&date_filter=30d[&course_id=][&include_edx_progress=true][&overtime=false][&program_id=][&pathway_id=][&granularity=day][&mentor_unique_id={mentor}]
— holistic snapshot of one user across catalog enrollments, agent engagement,
skills, credentials, and time spent. Same metrics sections as above. With
course_id, include_edx_progress=true folds in live edX progress
(completion, grade).
- GET
{dm_url}/api/analytics/learners/?platform_key={platform}&username={username}&date_filter=30d&page=1&limit=20[&overtime=false][&granularity=day][&mentor_unique_id={mentor}]
— unified user analytics: cross-platform summary (username only) or
platform-specific detail (username + platform_key).
- GET
{dm_url}/api/analytics/learners/list/?platform_key={platform}&search=&sort_by=&sort_order=&page=1&limit=20&date_filter=30d[&mentor_unique_id={mentor}]
— paginated user roster with per-user metrics (platform admins only;
platform_key required).
- GET
{dm_url}/api/analytics/time-spent/user/?platform_key={platform}&start_date=&end_date=[&course_id=][&mentor_uuid=][&session_uuid=][&url=][&username=][&include_main_platform=]
— total time spent (seconds) for the current authenticated user.
Audit (agent configuration changes)
- GET
{dm_url}/api/ai-mentor/orgs/{platform}/users/{username}/mentors/audit-logs/?limit=20&offset=0&mentor={mentor}[&action=0|1|2][&actor_email=][&actor_username=][&from_date=][&to_date=]
— audit trail of agent config changes. The .../users/{username}/agents/audit-logs/
path is the newer alias for the same log; {username} occupies the user_id
path segment.
Data Reports
Data Reports are async: POST to kick one off (see Writes), then poll and
download. Agent/course scoping is optional — it only applies to agent reports
(mentor) or course reports (course_id); org-wide reports need neither.
- GET
{dm_url}/api/reports/platforms/{platform}/[?mentor_id={mentorDbId}]
— list available reports + latest status. mentor_id is optional; when given,
access is scoped to that agent.
- GET
{dm_url}/api/reports/platforms/{platform}/{report_name}[?mentor_unique_id={mentor}]
— status/details of one report type; poll until complete. mentor_unique_id
optional (agent reports only).
- GET
{dm_url}/api/reports/platforms/{platform}/{task_id}/download?format=csv[&columns=][&bom=][&charset=]
— download a completed report as CSV or JSON.
Writes
Data Reports — Confirm with the user first
- POST
{dm_url}/api/reports/platforms/{platform}/new — kick off (or re-fetch
the status of) a report. Only report_name is essential; everything else is
optional and depends on the report type. Then poll and download via the Reads
above.{
"report_name": "string (report slug, e.g. ai-mentor-chat-history)",
"start_date": "yyyy-MM-dd",
"end_date": "yyyy-MM-dd",
"mentor": "uuid — agent (mentor) reports only",
"course_id": "string — course reports only",
"usergroup_ids": [1, 2],
"source": "string — host the report is requested from",
"query": "string — advanced SQL-like query, report permitting"
}
Record time spent
- POST
{dm_url}/api/analytics/orgs/{platform}/time/update/ — record a
time-spent event for the current user (required: count, timestamp, url):{
"timestamp": "ISO-8601",
"count": 30,
"url": "string",
"course_id": "string",
"mentor_uuid": "uuid",
"block_id": "string",
"session_uuid": "uuid",
"metadata": {}
}
Example
Org-wide Messages/Topics/Conversations KPIs (omit mentor_unique_id), then the
same scoped to one agent, then course content analytics:
dm_url="https://api.iblai.app/dm"
curl -s "$dm_url/api/analytics/topics/?platform_key=$IBLAI_ORG&date_filter=30d&metric=overview" \
-H "Authorization: Api-Token $IBLAI_API_KEY"
curl -s "$dm_url/api/analytics/topics/?platform_key=$IBLAI_ORG&date_filter=30d&metric=overview&mentor_unique_id=$MENTOR" \
-H "Authorization: Api-Token $IBLAI_API_KEY"
curl -s "$dm_url/api/analytics/content/?platform_key=$IBLAI_ORG&metric=courses&date_filter=30d" \
-H "Authorization: Api-Token $IBLAI_API_KEY"
Notes
- Schema first. Re-fetch
{dm_url}/api/docs/schema/ and confirm paths/params
before shipping requests; this skill can lag the deployment.
- Same endpoints, two scopes.
mentor_unique_id present ⇒ agent scope;
absent ⇒ org-wide. platform_key={platform} is your workspace key throughout.
date_filter=custom requires both start_date and end_date (yyyy-MM-dd).
- Cost values are already user-facing — the financial endpoints return
platform-marked-up USD. Consume as-is; do not re-apply any markup.
- Costs are best-effort (a separate datastore) — a null/empty cost block
means the cost store was unreachable, not zero spend.
- Time is in seconds on
time-spent/user/ and content time-spent fields.
- Per-user RBAC: self-access (own username, or no username on
/analytics/user)
needs no grant; reading another user requires an analytics grant, and
learners/list/ requires platform admin. In prose say "user", not
"learner" — learner survives only in wire names like learners/list/.
- External content: content used by your users but owned elsewhere is marked
external with limited metadata.
- For finding agents/content and recommendations, use
/iblai-api-search.
1---2name: iblai-api-analytics3description: Read ibl.ai analytics via the platform API — agent (chat) analytics, content (courses/programs/pathways/skills) analytics, org-wide KPIs, per-user learning data, costs, and fine-grained LLM usage (per-model/-agent/-user cost, tokens, latency) — plus generate and download Data Reports. Scope per-agent or organization-wide. Use to pull usage, engagement, cost, catalog, or per-user analytics.4---56# iblai-api-analytics78Read ibl.ai analytics from the platform API. One `/api/analytics/` family serves9every scope; the query params decide what you get:1011- **Agent (chat) analytics** — add `mentor_unique_id` to scope any chat metric12 (topics, sessions, conversations, ratings, costs, transcripts) to one agent.13- **Content analytics** — `/api/analytics/content/`, keyed by `metric=courses` |14 `programs` | `pathways` | `skills`, for catalog engagement and time spent.15- **Organization-wide analytics** — omit `mentor_unique_id` for org totals.16- **Per-user analytics** — a single user's enrollments, grades, time spent,17 engagement, and cross-platform snapshot.1819Reads are read-only; the only writes are Data Reports and a time-spent event.2021## The schema is the contract2223These endpoints live on the **Data Manager** service and its live OpenAPI schema24is the single source of truth — the URLs and params below exist for orientation25and **can drift between releases**. Validate against the schema before building26requests:2728- **Schema (raw):** `https://api.iblai.app/dm/api/docs/schema/`29- **Swagger UI:** `https://api.iblai.app/dm/api/docs/`3031```bash32# Confirm the analytics paths you're about to call exist verbatim:33curl -sS "https://api.iblai.app/dm/api/docs/schema/" -o /tmp/iblai_schema.yaml34grep -nE "^ /api/analytics/" /tmp/iblai_schema.yaml35```3637Treat any mismatch between this skill and the schema as a bug in the skill — the38deployed schema wins.3940## Auth & conventions4142- **`dm_url`** = `https://api.iblai.app/dm` — analytics are **Data Manager**43 endpoints reached through the gateway's `/dm` prefix. Every URL below is written44 as `{dm_url}/api/analytics/<path>`; set `dm_url=https://api.iblai.app/dm` and use it45 as the prefix.46- **Header:** `Authorization: Api-Token $IBLAI_API_KEY` on every request.47- **Placeholders:**48 - `{platform}` = your workspace key = `$IBLAI_ORG`. On the wire it is the49 `platform_key` query param and the `orgs/{platform}` / `platforms/{platform}`50 path segment — same value everywhere.51 - `{username}` = `$IBLAI_USERNAME`.52 - `{mentor}` = an agent's unique id — **optional**: include53 `mentor_unique_id={mentor}` for agent scope, omit for org-wide.54- **Shared query params (available on most endpoints):**55 - `platform_key={platform}` — the workspace key (required on most reads).56 - `date_filter` — `today` | `7d` | `30d` | `90d` | `all_time` | `custom`.57 `custom` requires `start_date` **and** `end_date` (both `yyyy-MM-dd`).58 - `mentor_unique_id={mentor}` — narrows any chat metric to one agent.59 - `granularity` — `hour` | `day` | `week` | `month` (time-series endpoints).60 - `usergroup_ids` — repeat to narrow results to specific user groups.61 - `page` / `limit` — pagination on list/`details` endpoints.62- Each endpoint below lists its own params; **required** ones are marked and enum63 values are given inline. `[¶m=value]` means optional.64- Not connected yet? Run **`/iblai-api-login`** first.6566## Reads6768### Agent & org chat analytics6970The presence of `mentor_unique_id` is the only difference between agent scope and71org-wide scope on every endpoint here.7273- **GET** `{dm_url}/api/analytics/topics/?platform_key={platform}&date_filter=30d&metric=overview[&mentor_unique_id={mentor}][&granularity=day][&usergroup_ids=]`74 — Messages / Topics / Conversations KPIs.75 `metric` ∈ `overview` (default) | `sessions` | `ratings` | `highlighted`.76- **GET** `{dm_url}/api/analytics/topics/details/?platform_key={platform}&date_filter=30d&page=1&limit=20&search=[&mentor_unique_id={mentor}]`77 — topics table / bar chart.78- **GET** `{dm_url}/api/analytics/conversations/?platform_key={platform}&date_filter=30d&metric=conversations[&mentor_unique_id={mentor}][&granularity=day]`79 — conversation counts. `metric` ∈ `conversations` | `headline`.80- **GET** `{dm_url}/api/analytics/sessions/?platform_key={platform}&date_filter=30d&metric=sessions[&mentor_unique_id={mentor}][&granularity=day]`81 — sessions line chart. `metric` ∈ `sessions` | `headline`.82- **GET** `{dm_url}/api/analytics/ratings/?platform_key={platform}&date_filter=30d&metric=ratings[&mentor_unique_id={mentor}][&granularity=day]`83 — thumbs / rating breakdown.84- **GET** `{dm_url}/api/analytics/time/?platform_key={platform}&date_filter=30d[&mentor_unique_id={mentor}][&granularity=hour]`85 — access-time heatmap.86- **GET** `{dm_url}/api/analytics/users/?platform_key={platform}&metric=active_users&date_filter=30d[&mentor_unique_id={mentor}]`87 — user KPIs. `metric` (**required**) ∈ `currently_active` | `active_users` |88 `registered_users` | `active_users_last_30d`.89- **GET** `{dm_url}/api/analytics/users/details/?platform_key={platform}&date_filter=30d&page=1&limit=5&search=[&mentor_unique_id={mentor}]`90 — user table.9192### Transcripts9394- **GET** `{dm_url}/api/analytics/messages/?platform_key={platform}&search={q}&topic={topic}&sentiment={s}&min_messages=&max_messages=&page=1&limit=20[&mentor_unique_id={mentor}][&start_date=&end_date=]`95 — transcript list (one row per session). `search` matches the user's96 **email** or username and the session's first user message. Each row carries97 the user's `email`.98- **GET** `{dm_url}/api/analytics/messages/details/?platform_key={platform}&session_id={id}[&mentor_unique_id={mentor}]`99 — one full transcript. `session_id` **required**. Returns `summary` +100 `messages[]`. Beyond `human`/`ai`, every AI turn carries the extended101 per-turn context: `documents` (retrieved sources, normalized, or `null`),102 `tool_calls` (`[{name, input, output}]`; `[]` when none), `metadata`103 (`{client, llm_model, llm_provider, llm_temperature}`, or `null`), and104 `request_context` (the credential-stripped request payload, or `null`).105106### Content analytics (courses / programs / pathways / skills)107108Catalog engagement and time-spent, org-wide by default. Add `mentor_unique_id`109to scope to content consumed via one agent.110111- **GET** `{dm_url}/api/analytics/content/?platform_key={platform}&metric=courses&date_filter=30d&include_overtime=false&page=1&limit=20[&mentor_unique_id={mentor}][&granularity=hour][&usergroup_ids=]`112 — aggregated content analytics + paginated item list. `metric` (**required**)113 ∈ `course`/`courses` | `program`/`programs` | `pathway`/`pathways` |114 `skill`/`skills`. `include_overtime=true` adds a 7-day time-spent series115 (courses only).116- **GET** `{dm_url}/api/analytics/content/details/{content_id}/?platform_key={platform}&metric=courses&date_filter=30d&search=&page=1&limit=20[&time_metric=][&mentor_unique_id={mentor}]`117 — detailed analytics for one content item (summary + per-user rows +118 optional time series). `metric` **required**.119120### Costs121122- **GET** `{dm_url}/api/analytics/financial/?platform_key={platform}&metric=total_costs&date_filter=30d[&show_overtime=false][&comparison_days=][&fill_method=zero][&provider=][&llm_model=][&username=][&mentor_unique_id={mentor}]`123 — cost KPIs / cost-per-day. `metric` (**required**) ∈ `total_costs` |124 `weekly_costs` | `monthly_costs`. `fill_method` ∈ `zero` | `previous`.125- **GET** `{dm_url}/api/analytics/financial/details/?platform_key={platform}&group_by=provider&date_filter=30d&metrics=total_costs,sessions&page=1&limit=20&search=[&provider=][&llm_model=][&username=][&mentor_unique_id={mentor}]`126 — cost breakdown. `group_by` (**required**) ∈ `provider` | `llm_model` |127 `username` | `mentor` | `platform` | `action`. `metrics` is comma-separated.128- **GET** `{dm_url}/api/analytics/financial/invoice/?platform_key={platform}&start_date=&end_date=&include_breakdown=true[&username={username}][&usergroup_ids=]`129 — invoice-style billing summary with optional per-line breakdown.130131### LLM usage (cost / tokens / latency)132133Fine-grained LLM cost/usage/latency for the UI, proxied from the tracing134backend and **tenant-scoped server-side** (the platform filter is injected from135your key, never taken from input). One `GET {dm_url}/api/analytics/llm-usage/`136serves three resources via the `resource` param; every `measures`, `aggregation`,137`group_by`, and `order_by` value is whitelisted (a bad value is a `400`).138139- **GET** `{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=metrics&measures=total_cost&aggregation=sum&date_filter=30d[&group_by=model][&granularity=day][&view=observations][&order_by=total_cost][&direction=desc][&limit=50][&mentor_unique_id={mentor}][&username=][&session_id=][&trace_name=][&llm_model=]`140 — aggregate metrics (default resource). `measures` (comma-separated) ∈141 `total_cost` | `input_tokens` | `output_tokens` | `total_tokens` | `count` |142 `latency`. `aggregation` ∈ `sum` | `avg` | `count` | `min` | `max` | `p50` |143 `p75` | `p90` | `p95` | `p99`. `group_by` ∈ `type` | `model` | `tags` |144 `environment`. `granularity` ∈ `day` | `week` | `month` (omit for a flat145 aggregate — there is no `hour`). `view` ∈ `observations` (default) | `scores`.146- **GET** `{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=observations&page=1&limit=50[&username=][&trace_name=][&llm_model=][&trace_id={id}][&observation_id={id}]`147 — row-level generation drill-down, page-paginated (`meta` carries `totalItems`148 / `totalPages`). `&trace_id={id}` scopes to one trace's observations;149 `&observation_id={id}` fetches one observation. `session_id` is **rejected150 `400`** here (observations can't filter by session — scope via `resource=traces`151 then drill in by `trace_id`); `mentor_unique_id` is likewise **rejected** on152 observations.153- **GET** `{dm_url}/api/analytics/llm-usage/?platform_key={platform}&resource=traces&order_by=timestamp&direction=desc&page=1&limit=50[&username=][&session_id=][&trace_name=][&llm_model=][&trace_id={id}][&mentor_unique_id={mentor}]`154 — per-user / per-session trace listing (the drill-down the Metrics API can't155 group by). `order_by` ∈ `timestamp` (default) | `name` | `username` |156 `session_id`; `direction` ∈ `asc` | `desc`. `&trace_id={id}` fetches one trace.157 Unlike observations, traces **are** agent-scopable via `mentor_unique_id`.158159All three return the same envelope: `{ "resource": "…", "data": [...], "meta": {...} }`.160`limit` caps at `1000`. A get-by-id (`trace_id` on traces, `observation_id` on161observations) whose record belongs to another organization returns **`404`** — existence162is never leaked. An unreachable/erroring tracing backend returns **`502`** (distinct163from a `500` bug). `date_filter=all_time` on `metrics` falls back to a fixed lower164bound (the backing Metrics API requires bounded timestamps).165166### Per-user analytics167168A single user's own learning data. **RBAC-gated**, with a self-access bypass:169a user reading **their own** data (username == the caller, or no username on170`/analytics/user`) needs no analytics grant; reading someone else's requires a171grant.172173- **GET** `{dm_url}/api/analytics/user?platform_key={platform}&username={username}&metrics=courses&date_filter=30d[&course_id=][&include_edx_progress=true][&overtime=false][&program_id=][&pathway_id=][&granularity=day][&mentor_unique_id={mentor}]`174 — the signed-in user's own holistic snapshot (self-access; no grant needed).175 Same shape as `learner/details`. `metrics` is comma-separated sections:176 `courses` (default), `programs`, `pathways`, `agents`, `skills`, `credentials`,177 `time_spent`.178- **GET** `{dm_url}/api/analytics/learner/details?platform_key={platform}&username={username}&metrics=courses&date_filter=30d[&course_id=][&include_edx_progress=true][&overtime=false][&program_id=][&pathway_id=][&granularity=day][&mentor_unique_id={mentor}]`179 — holistic snapshot of one user across catalog enrollments, agent engagement,180 skills, credentials, and time spent. Same `metrics` sections as above. With181 `course_id`, `include_edx_progress=true` folds in live edX progress182 (completion, grade).183- **GET** `{dm_url}/api/analytics/learners/?platform_key={platform}&username={username}&date_filter=30d&page=1&limit=20[&overtime=false][&granularity=day][&mentor_unique_id={mentor}]`184 — unified user analytics: cross-platform summary (username only) or185 platform-specific detail (username + `platform_key`).186- **GET** `{dm_url}/api/analytics/learners/list/?platform_key={platform}&search=&sort_by=&sort_order=&page=1&limit=20&date_filter=30d[&mentor_unique_id={mentor}]`187 — paginated user roster with per-user metrics (**platform admins only**;188 `platform_key` **required**).189- **GET** `{dm_url}/api/analytics/time-spent/user/?platform_key={platform}&start_date=&end_date=[&course_id=][&mentor_uuid=][&session_uuid=][&url=][&username=][&include_main_platform=]`190 — total time spent (seconds) for the current authenticated user.191192### Audit (agent configuration changes)193194- **GET** `{dm_url}/api/ai-mentor/orgs/{platform}/users/{username}/mentors/audit-logs/?limit=20&offset=0&mentor={mentor}[&action=0|1|2][&actor_email=][&actor_username=][&from_date=][&to_date=]`195 — audit trail of agent config changes. The `.../users/{username}/agents/audit-logs/`196 path is the newer alias for the same log; `{username}` occupies the `user_id`197 path segment.198199### Data Reports200201Data Reports are async: **POST** to kick one off (see Writes), then poll and202download. Agent/course scoping is optional — it only applies to agent reports203(`mentor`) or course reports (`course_id`); org-wide reports need neither.204205- **GET** `{dm_url}/api/reports/platforms/{platform}/[?mentor_id={mentorDbId}]`206 — list available reports + latest status. `mentor_id` is optional; when given,207 access is scoped to that agent.208- **GET** `{dm_url}/api/reports/platforms/{platform}/{report_name}[?mentor_unique_id={mentor}]`209 — status/details of one report type; poll until complete. `mentor_unique_id`210 optional (agent reports only).211- **GET** `{dm_url}/api/reports/platforms/{platform}/{task_id}/download?format=csv[&columns=][&bom=][&charset=]`212 — download a completed report as CSV or JSON.213214## Writes215216### Data Reports — Confirm with the user first217218- **POST** `{dm_url}/api/reports/platforms/{platform}/new` — kick off (or re-fetch219 the status of) a report. Only `report_name` is essential; everything else is220 optional and depends on the report type. Then poll and download via the Reads221 above.222 ```json223 {224 "report_name": "string (report slug, e.g. ai-mentor-chat-history)",225 "start_date": "yyyy-MM-dd",226 "end_date": "yyyy-MM-dd",227 "mentor": "uuid — agent (mentor) reports only",228 "course_id": "string — course reports only",229 "usergroup_ids": [1, 2],230 "source": "string — host the report is requested from",231 "query": "string — advanced SQL-like query, report permitting"232 }233 ```234235### Record time spent236237- **POST** `{dm_url}/api/analytics/orgs/{platform}/time/update/` — record a238 time-spent event for the current user (required: `count`, `timestamp`, `url`):239 ```json240 {241 "timestamp": "ISO-8601",242 "count": 30,243 "url": "string",244 "course_id": "string",245 "mentor_uuid": "uuid",246 "block_id": "string",247 "session_uuid": "uuid",248 "metadata": {}249 }250 ```251252## Example253254Org-wide Messages/Topics/Conversations KPIs (omit `mentor_unique_id`), then the255same scoped to one agent, then course content analytics:256257```bash258dm_url="https://api.iblai.app/dm"259260curl -s "$dm_url/api/analytics/topics/?platform_key=$IBLAI_ORG&date_filter=30d&metric=overview" \261 -H "Authorization: Api-Token $IBLAI_API_KEY"262263curl -s "$dm_url/api/analytics/topics/?platform_key=$IBLAI_ORG&date_filter=30d&metric=overview&mentor_unique_id=$MENTOR" \264 -H "Authorization: Api-Token $IBLAI_API_KEY"265266curl -s "$dm_url/api/analytics/content/?platform_key=$IBLAI_ORG&metric=courses&date_filter=30d" \267 -H "Authorization: Api-Token $IBLAI_API_KEY"268```269270## Notes271272- **Schema first.** Re-fetch `{dm_url}/api/docs/schema/` and confirm paths/params273 before shipping requests; this skill can lag the deployment.274- **Same endpoints, two scopes.** `mentor_unique_id` present ⇒ agent scope;275 absent ⇒ org-wide. `platform_key={platform}` is your workspace key throughout.276- **`date_filter=custom`** requires both `start_date` and `end_date` (`yyyy-MM-dd`).277- **Cost values are already user-facing** — the financial endpoints return278 platform-marked-up USD. Consume as-is; do not re-apply any markup.279- **Costs are best-effort** (a separate datastore) — a null/empty cost block280 means the cost store was unreachable, not zero spend.281- **Time is in seconds** on `time-spent/user/` and content time-spent fields.282- **Per-user RBAC:** self-access (own username, or no username on `/analytics/user`)283 needs no grant; reading another user requires an analytics grant, and284 `learners/list/` requires platform admin. In prose say **"user"**, not285 "learner" — `learner` survives only in wire names like `learners/list/`.286- **External content:** content used by your users but owned elsewhere is marked287 `external` with limited metadata.288- For *finding* agents/content and recommendations, use `/iblai-api-search`.