Conversation insights

Developer·7 minutes to read

What it does

Reads your organisation's conversation insights as counts: how many bot conversations it had in a date range, by type of work and by level of automation. You can also ask for the same counts for some members or groups, to compare them. The numbers are the ones Organisation → Usage → Insights shows for the same range. The answer holds ids and counts only, never a conversation, a title, a message, an attachment or anyone's name.

Authentication

You need a key of scope admin, made by a person who is still an owner or admin of a team organisation. These all get 401 unauthenticated:

  • a read or write key, or a key whose person is no longer an admin;
  • any key while an admin has turned insights off in Organisation → Usage → Insights;
  • a key of a personal organisation, which has no insights.

See Authentication and keys.

Request

GET /usage/insights

<!-- generated:begin GET /usage/insights --> <!-- generated:end GET /usage/insights -->

from and to are UTC days (YYYY-MM-DD), both included. Leave them out for the month to date. A range may be at most 366 days long.

members is a comma-separated list of up to 50 members, each given by id or by email address in any letter case; you can mix the two. Each must be a member of your organisation. A member named by email address is answered under their id, exactly as if you had named the id, and a member you name twice is answered once. groups is a comma-separated list of up to 50 group ids, each a live group of your organisation. A group is counted by who is in it now.

To read usage itself — per day, or per metered call filtered by member, bot, routine or way of paying — see Usage by day and by call below.

curl -sS "$BOTSEON_API_URL/api/v1/usage/insights?from=2026-10-01&to=2026-10-17" \
  -H "authorization: Bearer $BOTSEON_API_KEY" \
  -H "accept: application/json"

Response

The first entry is the whole organisation. Then comes one entry per member you named, then one per group, in the order you named them.

{
  "from": "2026-10-01",
  "to": "2026-10-17",
  "data": [
    {
      "subject": { "type": "organisation", "id": "6f1e2d3c-4b5a-4968-8776-655443322110" },
      "total": 42,
      "byWorkType": {
        "inbox": 12, "calendar": 6, "sales": 4, "support": 5, "marketing": 1, "finance": 3,
        "people": 0, "engineering": 2, "research": 4, "projects": 1, "general": 4
      },
      "byAutomation": { "answered": 20, "drafted": 9, "completed": 13 }
    }
  ]
}
KeyLevel of automation
answeredNot automated: the bot answered, and a person did the work.
draftedSemi-automated: the bot prepared the work, and a person reviewed it.
completedFully automated: the bot did the work.

Errors

StatusCodeWhenWhat to do
400invalid_requestA day that is not YYYY-MM-DD, a start after its end, a range over 366 days, or a member (by id or email address) or group that is not your organisation's. details names the field. An address nobody holds and one that belongs to another organisation get the same answer.Fix the field details names.
401unauthenticatedThe key is not an admin's admin key, insights are off, or the organisation is personal.Use an admin key from a current admin, or turn insights on in the app.
429rate_limitedMore than 20 requests in a minute for your organisation.Wait for Retry-After.

Rate limits

This route allows 20 requests a minute per organisation. The 21st in the same minute gets 429 with Retry-After: 60. This limit is the route's own, so your other API calls carry on. The shared limits still apply too: 300 requests a minute per key and 1 000 per organisation. See Envelopes, errors, idempotency and paging.

Usage by day and by call

GET /usage/daily and GET /usage/events read your organisation's usage for a reporting or cost tool. They take the same admin key as this page, answer ids and numbers only, and are listed with the other administrator routes in Audit log, usage, spend limits and groups. The shared rate limits apply.

GET /usage/daily

One row per member per UTC day with runs, calls, credits, spendMicroCents and costMicroCents. from and to are required UTC days (YYYY-MM-DD), both included; the range may be at most 366 days long. limit is 1 to 200 rows a page (50 when you leave it out), and cursor is the nextCursor of the page before.

GET /usage/events

One row per metered model call, oldest first. It takes these query parameters:

ParameterWhat it means
from, toA day (YYYY-MM-DD) or an instant (ISO 8601 with an offset). to is now when you leave it out, and from is 366 days before to. from must be before to: for one day, give to as an instant, such as from=2026-10-01&to=2026-10-01T23:59:59Z.
membersMembers, by id or by email address in any letter case; you can mix the two. A member's calls are the calls of the runs they started.
botsBot ids. A deleted bot's calls can still be read.
routinesRoutine ids: the calls of the runs those routines started.
keysAPI key ids, as GET /keys lists them: the calls of the runs those keys started with POST /runs, POST /conversations/{id}/messages or a run.create in POST /batch. A revoked key's calls can still be read. Runs started before keys were recorded (October 2026) match no key.
serviceAccountsService-account ids, as GET /service-accounts lists them: the calls of the runs any of those accounts' keys started with POST /runs, POST /conversations/{id}/messages or a run.create in POST /batch. An archived account's calls can still be read. A person's own key is not a service account: narrow to it with members or keys.
credentialKindHow the call was paid for: platform (your plan and credit), byok (your own provider key) or local (a model on your own machine).
limitRows per page, 1 to 1 000. 50 when you leave it out.
cursorThe nextCursor of the page before; send it with the same filters.

Each filter is a comma-separated list of at most 100 values. A call matches a filter when it matches any of its values, and it is on the page only when it matches every filter you give.

curl -sS "$BOTSEON_API_URL/api/v1/usage/events?from=2026-10-01&credentialKind=platform&limit=1000" \
  -H "authorization: Bearer $BOTSEON_API_KEY" \
  -H "accept: application/json"

The answer is a page: data and nextCursor, null on the last page. Each row has id, occurredAt, userId, botId, runId, model, credentialKind, inputTokens, outputTokens, credits, costMicroCents (the list price), spendMicroCents (what paid for the call; one credit is 1 000 000 micro-cents) and serviceAccountId (the service account whose key started the run, or null when a person, the screen or a routine started it). Add up spendMicroCents over a month filtered to one member or one bot and you get that member's or bot's figure on Organisation → Usage. Add up costMicroCents over a calendar month (UTC) filtered to one service account and you get the spend that account's monthly spend limit is checked against, the monthSpendMicroCents of GET /service-accounts/{id}.

A value the route cannot read is 400 invalid_request naming the parameter: a value that is not an id, an email address or one of the listed kinds; a member, bot, routine, key or service account that is not your organisation's (the path names the position too: members.1 is the second value of members); a limit over 1 000; a parameter the route does not take; a start not before its end or a window over 366 days; or a cursor this list did not issue. You never get an empty page for a value that names nothing. An address nobody holds and one that belongs to another organisation get the same answer.

Last verified against build b2955f0.