Skip to main content

Reporting API

The reporting API lets your own systems read the same numbers as the reports, and lets Studio flows and bots add milestones to IVR journeys:

EndpointPurposeScope
GET /analytics/catalogMetrics, dimensions, range presets and time bucketsreports:read
GET /analytics/dimension-valuesThe values a dimension takes, for filtersreports:read
POST /analytics/queryRun a query and get the result as JSONreports:read
POST /analytics/exportThe same query, or a saved report, as a CSV or Excel filereports:read
GET /analytics/reportsSaved reports you can see, to export by IDreports:read
GET /analytics/exports/:dayThe nightly open data files for a day, with download linksreports:read
POST /analytics/journey-events/:tokenRecord an IVR milestone from a Studio flowtoken in the URL
POST /analytics/journey-eventsRecord a milestone from a bot or servicejourneys:write
POST /analytics/mcpThe MCP server for AI assistants, see Ask AI and MCPmcp

Everything else (report settings, saved report editing, email schedules, the backfill) is done in the Reports view.

Basics​

  • Base URL: https://<your-floor-walker-host>
  • Authentication: an API key in the X-Api-Key header, with the scope for what it does: reports:read for the catalog, dimension values, queries, exports, saved reports and the open data files; journeys:write for POST /analytics/journey-events; mcp for the MCP server, plus contacts:read for its contact-level tools. Ask us for one key per integration, with only the scopes it needs. API keys are issued by us and see every row; keep them on the server side. A signed-in Flex user's token (Authorization: Bearer <Flex token>) works too, with that user's permissions: agents get their own rows only, and the open data files need a supervisor.
  • Format: JSON. Times are ISO 8601 with an offset. Durations are in seconds, percentages 0–100.
  • Errors return an HTTP status and { "error": "<code>", "details": … }, for example 400 validation_error (with the problems in details), 400 unknown_metric, 400 unknown_dimension, 400 dimension_not_supported (a dimension that does not apply to a metric's source), 400 invalid_range, 400 too_many_buckets, 401 unauthorized, 403 forbidden (the key's scopes do not cover this endpoint; details.requiredScopes lists the scopes that would) and 404 export_not_found.

Catalog​

GET /analytics/catalog lists everything a query can use:

  • metrics: id, source, English and Hebrew label, description, format and higherIsBetter. The metrics are described in the Metrics catalog.
  • dimensions: id, label and the sources they apply to, including your custom report fields as custom.<key>.
  • sources: the data sets metrics come from (for example segments for queue and agent legs, contacts, queue_load, agent_time, journeys).
  • rangePresets, buckets and the default timezone.

GET /analytics/dimension-values?dimension=queue&source=segments&from=<ISO>&to=<ISO> returns { "values": [...] }: the values a dimension takes in that period, for building filters.

Query​

POST /analytics/query

{
"metrics": ["offered", "service_level", "asa"],
"dimensions": ["queue"],
"preset": "last_week",
"timezone": "Europe/London"
}
FieldMeaning
metrics1 to 20 metric IDs. Metrics from different sources may be combined; each is computed on its own data set and joined on the dimensions
dimensionsUp to 4 dimension IDs to group by
filtersDimension ID to a list of allowed values, for example { "queue": ["WQ…"], "channel": ["voice"] }
preset, or from + toA relative period (today, yesterday, last_7_days, last_30_days, last_90_days, this_week, last_week, this_month, last_month) or ISO times
bucketTime breakdown: none (default), 15m, 30m, 1h, 1d, 1w, 1mo
timezoneIANA time zone for days and buckets; defaults to your environment's
sort{ "by": "<column id>", "dir": "asc" | "desc" }
limitRows, up to 10,000 (default 1,000)
totalsTotals over the whole period (default true)

Response:

{
"columns": [
{ "id": "queue", "kind": "dimension", "label": { "en": "Queue", "he": "תור" } },
{ "id": "offered", "kind": "metric", "label": { "en": "Offered", "he": "…" }, "format": "count" },
{ "id": "service_level", "kind": "metric", "label": { "en": "Service level", "he": "…" }, "format": "percent", "higherIsBetter": true }
],
"rows": [{ "queue": "WQ0123…", "offered": 1840, "service_level": 82.4, "asa": 14.2 }],
"labels": { "queue": { "WQ0123…": "Billing" } },
"totals": { "offered": 5210, "service_level": 79.8, "asa": 17.9 },
"meta": { "from": "…", "to": "…", "bucket": "none", "timezone": "Europe/London", "truncated": false }
}

Queues and agents appear in rows by their Twilio SID; labels gives their names. With a bucket, each row also has a t column with the bucket's start time. meta.truncated is true when the limit cut the result.

Counting follows the reports exactly: contacts count in the period in which they started, in the query's time zone. See How contacts are counted.

Export​

POST /analytics/export returns a file instead of JSON:

{ "query": { "metrics": ["handled", "aht"], "dimensions": ["agent"], "preset": "yesterday" }, "format": "xlsx", "language": "en", "name": "Agents yesterday" }
FieldMeaning
queryA query as above, or
reportIdA saved report, optionally with from and to to run it over other dates
formatcsv (default) or xlsx
languageen (default) or he, for column names and the sheet direction
nameThe file name

The file is described in Exports. GET /analytics/reports returns { "reports": [...] } with the id, name and definition of every saved report you can see (your own and shared ones).

Open data files​

GET /analytics/exports/2026-11-01 returns the manifest of that day's open data files and a download link for each file:

{
"manifest": { "day": "2026-11-01", "timezone": "Asia/Jerusalem", "tables": [{ "table": "…", "rows": 5210, "files": ["…/date=2026-11-01/….parquet", "…/date=2026-11-01/….csv.gz"], "columns": [...] }] },
"links": { "…/date=2026-11-01/….parquet": "https://…" }
}

Links are valid for one hour. A day that has not been written returns 404 export_not_found.

IVR milestones​

Milestones attach business events to a contact's IVR or bot journey. See Milestones for what they are used for.

From a Studio flow​

POST /analytics/journey-events/<token>, form-encoded or JSON, with no other authentication. The token is a secret we give you for your Studio flows: either the installation's journey token or an API key with only the journeys:write scope (one per flow if you like, so it can be revoked on its own). A wrong or revoked token returns 404.

POST https://<your-floor-walker-host>/analytics/journey-events/<token>
Content-Type: application/x-www-form-urlencoded

event=self_service_resolved&CallSid={{trigger.call.CallSid}}&topic=balance

From a bot or service​

POST /analytics/journey-events with an API key that has the journeys:write scope:

curl -X POST https://<your-floor-walker-host>/analytics/journey-events \
-H "X-Api-Key: <your-api-key>" -H "Content-Type: application/json" \
-d '{"event":"intent_detected","conversation_sid":"CHxxxxxxxx","attributes":{"intent":"billing"}}'
FieldMeaning
event1 to 64 letters, digits, _, . or -. self_service_resolved marks the journey as contained
CallSid / call_sid, ConversationSid / conversation_sid, or ExecutionSid / execution_sidThe contact the milestone belongs to; one is required
atOptional time of the milestone
attributes, or any other fieldStored with the milestone, up to 8,000 characters as JSON

Both return 201 with { "ok": true, "id": "…" } and the journey or contact the milestone was attached to. Errors: 400 invalid_event, 400 missing_contact, 413 attributes_too_large.