message.comDevelopers

Reports Live

Read-only analytics endpoints that power the dashboard's Reports tab and your custom integrations. All filterable by date range, department, and (for agent reports) agent ID. Output is time-series plus a total.

Reports are eventually-consistent. Numbers settle within a few seconds of the source events. Do not expect counter-perfect parity with the live inbox.

Common parameters

FieldTypeDescription
rangeoptionalstringPredefined range: 1d, 7d, 30d, 90d, custom.
fromoptionaldatetimeRequired if range=custom. ISO 8601 UTC.
tooptionaldatetimeRequired if range=custom. ISO 8601 UTC.
departmentIdoptionaluuidFilter to one department.
agentIdoptionaluuidFilter to one agent. Agents see only their own reports by default.
channeloptionalenumSubset of chat, ticket, call.

Volume

GET/api/v1/reports/conversations/totalAuth: Bearer
GET/api/v1/reports/conversations/by-channelAuth: Bearer

Total conversations in the range, plus a daily series. by-channel returns the same numbers split by chat / ticket / call.

Code samples

cURL
curl 'https://app.message.com/api/v1/reports/conversations/total?range=7d' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/reports/conversations/total?range=7d', {
  headers: { Authorization: 'Bearer ' + token }
});
const report = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/reports/conversations/total",
    headers={"Authorization": f"Bearer {token}"},
    params={"range": "7d"},
)
report = r.json()
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/reports/conversations/total?range=7d")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{token}"
  http.request(req)
end
report = JSON.parse(res.body)
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$report = json_decode(file_get_contents("https://app.message.com/api/v1/reports/conversations/total?range=7d", false, $ctx), true);
200 OK
{
  "range": { "start": "2026-05-06T00:00:00Z", "end": "2026-05-13T23:59:59Z" },
  "filters": { "departmentId": null, "agentId": null },
  "series": [
    { "bucket": "2026-05-06", "value": 124 },
    { "bucket": "2026-05-07", "value": 138 },
    { "bucket": "2026-05-08", "value": 119 }
  ],
  "total": 381
}

Ticket-specific reports

GET/api/v1/reports/tickets/by-statusAuth: Bearer
GET/api/v1/reports/tickets/response-timesAuth: Bearer
GET/api/v1/reports/tickets/satisfactionAuth: Bearer
  • by-status: counts of open / pending / solved / closed.
  • response-times: median and 90th-percentile first response and full resolution.
  • satisfaction: CSAT score over time and response volume.

Agent performance

GET/api/v1/reports/agents/performanceAuth: Bearer

Per-agent volume, response-time medians, and CSAT. Admins see all agents; supervisors see only their departments; agents see only themselves.

200 OK · agents/performance
{
  "agent": { "id": "uuid", "name": "Alex Lee" },
  "conversations": 84,
  "firstResponseMedianSeconds": 41,
  "fullResolutionMedianSeconds": 1820,
  "csatScore": 4.6,
  "csatResponses": 39
}

Customer-side metrics

GET/api/v1/reports/customers/queuedAuth: Bearer
GET/api/v1/reports/customers/queue-abandonmentAuth: Bearer

Currently-queued count (live), and historical abandonment rate per channel.

AI-surfaced insights

GET/api/v1/reports/insights/top-questionsAuth: Bearer
GET/api/v1/reports/insights/alertsAuth: Bearer

Planned AI surfaces the most-asked questions across recent conversations, and flags anomalies (sudden volume spikes, unusual sentiment shifts). Ships with the AI substrate.

Export

GET/api/v1/reports/exportAuth: Bearer

Returns a signed URL for a CSV or JSON snapshot of any report. Pass type=<report> and format=csv|json. URL is valid for 15 minutes.

Errors

CodeWhen
400 invalid_queryRange invalid or custom range missing from/to.
403 forbiddenAgent role not permitted for requested scope.
422 range_too_largeCustom range exceeds 365 days.

Common pitfalls

  • Comparing tiny ranges. Daily granularity over a 1d range gives you 24 buckets of one hour each. Use granularity=hour explicitly for diagnostic dashboards.
  • Bucket gaps. Quiet days return value: 0, not missing buckets. Render them as zero, not "no data".
  • Forgetting the role scope. An agent calling /agents/performance sees only their own row, not a 403. The list is shorter than admins expect.