Insights API
Retrieve the conversation insights that power your dashboard’s Insights page: sentiment, resolution and escalation rates, top topics, tone, per-day trends, and the findings Ragtime raises for your team. These endpoints are available:
GET /insights: deterministic metrics and trends for a date range. No AI cost and fast, which suits dashboards and scheduled reports.POST /insights/query: ask a natural-language question and get a written answer grounded in those same metrics.GET /insights/highlights: the cards worth knowing about without asking, such as sentiment shifts, volume anomalies and emerging topics. Poll this to raise your own alerts.POST /insights/export: render a report as PDF, CSV or JSON for archival and compliance./insights/saved-queries: store a named question and re-run it later./insights/findings: the findings from the daily check, what is going well as well as what needs attention, and the calls to resolve, defer, decline, dismiss or reopen them.
Not everything the dashboard shows has an endpoint yet. See What the API does not cover.
Reading requires an API key with the insights:read scope; creating, updating and deleting saved queries requires insights:write; acting on findings and running the check requires findings:write. See API Keys to create one.
Insights in the dashboard
Section titled “Insights in the dashboard”Dashboard → Insights has four tabs: Analytics, Insights, Findings and Transcripts. They share one filter bar, so a period, channel or language you pick on one tab carries over to the others.
The filter bar holds:
- Period. Presets (today, 7, 30 or 90 days, all time) and a custom range. On a narrow screen the bar shows only the short ranges.
- What the numbers show. Totals, or change from the previous period (as long as the one you picked, just before it), or change from dates you choose. Change is off for all time and for a custom range without a start date. Analytics cards then say how each figure moved, and the usage chart draws the compared period as a dashed line. On the Findings tab each finding says how its conversations moved.
- Scope. One menu for channel, comparison and language. Once you pick a channel, the menu offers a second channel (or all the others) to compare it with. Findings can compare two channels too: see Findings in the dashboard.
The choices live in the page URL (range, from, to, device, compare, lang, change, cfrom, cto), so a link shows the same view. The dashboard also remembers them for the session, per project.
Every tab ends its filter row with Export and Settings. Export gives the current tab’s data: Insights results as PDF, CSV or JSON, Transcripts as a ZIP, and Findings as CSV or JSON of the filtered list. PDFs print the dashboard as it looks, charts and map included. Settings holds data collection, the date format used in exports, and the tools to purge conversations or generate and remove test data. A purge follows the filter bar’s period and channel. Only a purge of all time on all channels also removes findings and their history; anything narrower leaves findings alone.
The Analytics tab includes a Top countries chart, taken from each visitor’s IP address.
Metrics endpoint
Section titled “Metrics endpoint”curl "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights?from=2026-03-01&to=2026-03-25" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"Metrics query parameters
Section titled “Metrics query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
from |
string | 30 days ago | Start date (YYYY-MM-DD), inclusive. |
to |
string | Today | End date (YYYY-MM-DD), inclusive. |
deviceId |
string | — | Limit metrics to a single device/kiosk by its slug (e.g. lobby-kiosk). |
compare |
string | — | Compare against another device slug, or __rest__ for every other device. Requires deviceId. Returns a comparison block. |
compareToPrevious |
boolean | false |
When true, also returns metrics for the immediately-preceding period as previousMetrics. Ignored when compare is set. |
Metrics response
Section titled “Metrics response”{ "projectId": "proj-uuid-...", "range": { "from": "2026-03-01", "to": "2026-03-25" }, "compareToPrevious": false, "metrics": { "conversationCount": 184, "avgSentiment": 0.42, "sentimentBreakdown": { "positive": 96, "neutral": 61, "negative": 27 }, "resolutionRate": 0.78, "escalationRate": 0.09, "unansweredRate": 0.05, "topTopics": [ { "topic": "billing", "count": 45, "share": 0.245 }, { "topic": "opening hours", "count": 31, "share": 0.168 } ], "toneDistribution": { "polite": 0.71, "frustrated": 0.18, "formal": 0.4 } }, "previousMetrics": null, "trend": [ { "date": "2026-03-24", "conversationCount": 12, "avgSentiment": 0.38, "resolved": 9, "escalated": 1, "unanswered": 1 } ], "comparison": null, "compatibility": { "currentProvider": "openai", "currentModel": "text-embedding-3-small", "compatibleCount": 184, "incompatibleCount": 0, "missingCount": 0, "staleModels": [] }, "notices": []}Metric fields
Section titled “Metric fields”| Field | Description |
|---|---|
conversationCount |
Number of analyzed conversations in the period. |
avgSentiment |
Average sentiment score from -1 (negative) to 1 (positive), or null when no data. |
sentimentBreakdown |
Conversation counts grouped by sentiment label. |
resolutionRate |
Share of conversations where the user’s need was met (0–1), or null when unknown. |
escalationRate |
Share of conversations handed off to a human (0–1), or null when unknown. |
unansweredRate |
Share of conversations the assistant could not answer (0–1), or null when unknown. |
topTopics |
Up to 10 topics with their count and share (fraction of conversations). |
toneDistribution |
Average value (0–1) for each detected tone dimension. |
trend |
Per-day series (gap-filled) of counts, average sentiment, and outcome tallies. |
comparison |
When compare is set: the same metrics and trend for the comparison scope. |
compatibility |
Embedding-model coverage for analyzed conversations. See note below. |
notices |
Human-readable warnings, e.g. when some conversations were analyzed with an older embedding model. |
Question endpoint
Section titled “Question endpoint”Ask a natural-language question. The answer is generated from the same deterministic metrics above — figures are never invented.
curl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/query" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "question": "How did customer sentiment change this month?", "from": "2026-03-01", "to": "2026-03-25", "compareToPrevious": true }'Question request body
Section titled “Question request body”| Field | Type | Default | Description |
|---|---|---|---|
question |
string | — | Required. The question to answer (max 500 characters). |
from |
string | 30 days ago | Start date (YYYY-MM-DD), inclusive. |
to |
string | Today | End date (YYYY-MM-DD), inclusive. |
deviceId |
string | — | Limit analysis to a single device/kiosk by its slug. |
compare |
string | — | Compare against another device slug, or __rest__ for every other device. Requires deviceId. |
compareToPrevious |
boolean | false |
Include the immediately-preceding period for comparison. Ignored when compare is set. |
Question response
Section titled “Question response”{ "projectId": "proj-uuid-...", "question": "How did customer sentiment change this month?", "range": { "from": "2026-03-01", "to": "2026-03-25" }, "compareToPrevious": true, "relevantConversationCount": 42, "answer": "Sentiment improved over the period, rising from an average of 0.31 to 0.42. Resolution stayed strong at 78%, while escalations held steady around 9%.", "relevantMetrics": [ { "key": "sentiment", "phrase": "Average sentiment rose from 0.31 to 0.42." }, { "key": "outcomes", "phrase": "Resolution held at 78% with low escalation." } ], "metrics": { "...": "same shape as the metrics endpoint" }, "previousMetrics": { "...": "metrics for the preceding period, or null" }, "trend": [{ "...": "per-day series" }], "comparison": null, "compatibility": { "...": "embedding coverage" }, "notices": []}The answer is a written summary; relevantMetrics highlights the metrics most relevant to the question, and relevantConversationCount is how many analyzed conversations semantically matched it. The metrics, previousMetrics, trend, comparison, compatibility, and notices fields match the metrics endpoint.
Highlights endpoint
Section titled “Highlights endpoint”The proactive cards the dashboard shows before you ask anything. Every finding is derived from SQL; the AI is used only to phrase the headline, never to produce a number.
curl "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/highlights?from=2026-03-01" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"Highlights query parameters
Section titled “Highlights query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
from |
string | 30 days ago | Start date (YYYY-MM-DD), inclusive. |
to |
string | Today | End date (YYYY-MM-DD), inclusive. |
deviceId |
string | — | Limit to a single device/kiosk by its slug. |
refresh |
boolean | false |
Bypass the cache and recompute. |
Highlights response
Section titled “Highlights response”{ "projectId": "proj-uuid-...", "range": { "from": "2026-03-01", "to": null }, "hasData": true, "conversationCount": 184, "cached": true, "cards": [ { "id": "sentiment_shift", "kind": "sentiment_shift", "severity": "positive", "title": "Sentiment climbing", "detail": "Average sentiment rose by 0.11 versus the preceding period.", "metricLabel": "Avg sentiment", "metricValue": "0.42", "sparkline": [{ "date": "2026-03-24", "value": 0.38 }], "baseline": 0.31, "topicBars": null, "suggestedQuestion": "What drove the improvement in sentiment?" } ]}kind is one of sentiment_shift, volume_change, volume_anomaly, emerging_topic, escalation_change or sentiment_driver. Up to six cards are returned, ranked by significance; cards is empty when the period holds no analyzed conversations (hasData: false).
Export endpoint
Section titled “Export endpoint”Render a report for archival or compliance. The question is answered server-side, so the figures in the document are the ones this API computed.
curl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/export" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "question": "How did sentiment develop this month?", "format": "pdf-a4", "from": "2026-03-01" }' \ --output insights.pdfExport request body
Section titled “Export request body”| Field | Type | Default | Description |
|---|---|---|---|
question |
string | — | Required. The question the report answers (max 500 characters). |
format |
string | — | Required. One of pdf-a4, pdf-letter, csv, json. |
dateFormat |
string | iso |
How dates are rendered inside the document: iso, eu, us, long, relative. |
from |
string | 30 days ago | Start date (YYYY-MM-DD), inclusive. |
to |
string | Today | End date (YYYY-MM-DD), inclusive. |
deviceId |
string | — | Limit to a single device/kiosk by its slug. |
compare |
string | — | Compare against another device slug, or __rest__. Requires deviceId. |
compareToPrevious |
boolean | false |
Include the immediately-preceding period. Ignored when compare is set. |
The response is a file attachment: application/pdf, text/csv or application/json depending on format. PDF exports include the same charts the dashboard renders.
Saved queries
Section titled “Saved queries”Store a named question with its date range and device filters, then replay it. Saved queries created through the API belong to the project, so every key bound to that project sees the same list — they are separate from the per-user queries saved in the dashboard.
# Createcurl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/saved-queries" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "name": "Weekly sentiment", "question": "How did customer sentiment develop?", "defaults": { "compareToPrevious": true, "deviceId": "lobby-kiosk" } }'
# Re-run, overriding the windowcurl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/saved-queries/{queryId}/run?from=2026-03-01&to=2026-03-25" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"| Method | Path | Scope | Description |
|---|---|---|---|
GET |
/insights/saved-queries |
insights:read |
List the project’s saved queries. |
POST |
/insights/saved-queries |
insights:write |
Create one. Names are unique per project. |
GET |
/insights/saved-queries/{id} |
insights:read |
Read one. |
PATCH |
/insights/saved-queries/{id} |
insights:write |
Update name, question or defaults. |
DELETE |
/insights/saved-queries/{id} |
insights:write |
Delete one. |
POST |
/insights/saved-queries/{id}/run |
insights:read |
Replay it. Returns the same shape as /insights/query. |
defaults holds the same parameters /insights/query accepts — from, to, compareToPrevious, deviceId, compare — and is replaced wholesale on update: a field you omit is cleared, not kept. The run endpoint accepts those same names as query parameters, each overriding the saved value.
{ "savedQuery": { "id": "query-uuid-...", "name": "Weekly sentiment", "question": "How did customer sentiment develop?", "defaults": { "from": null, "to": null, "compareToPrevious": true, "deviceId": "lobby-kiosk", "compare": null }, "createdAt": "2026-03-25T09:12:44.000Z", "updatedAt": "2026-03-25T09:12:44.000Z" }}Findings
Section titled “Findings”Findings are what the daily check raises for your team, as the dashboard’s Findings view shows them. Each comes with its figures, a recommendation, the source it concerns and the anonymized visitor questions behind it, never conversation IDs.
There are seven kinds, in three groups. The group is the finding’s polarity.
| Polarity | Dashboard label | Kinds |
|---|---|---|
attention |
Needs attention | missing_knowledge (questions the knowledge base can’t answer), confusing_source (a source whose conversations often end badly), unused_source (a source nobody uses) |
well |
Going well | strong_source (a source whose conversations end well), improving_outcomes (conversations end well more often than before), improving_topic (a topic whose conversations now end well more often) |
know |
Worth knowing | emerging_topic (a topic that is growing) |
A fixed problem counts as going well: once Ragtime has watched an attention finding stay fixed (confirmed), its polarity is well.
| Method | Path | Scope | Description |
|---|---|---|---|
GET |
/insights/findings |
insights:read |
List findings, open first and ranked by impact. |
POST |
/insights/findings/{findingId} |
findings:write |
Resolve, defer, decline, dismiss or reopen one finding. |
POST |
/insights/findings/check |
findings:write |
Run the daily check for this project now. |
findings:write is separate from insights:write on purpose: a key that manages saved questions can’t decline findings.
Findings in the dashboard
Section titled “Findings in the dashboard”The Findings tab opens on everything that is open, whatever its kind. Above the list, an overview says in plain figures what changed. Every number in it comes from Ragtime’s own counts, and the AI only phrases the sentences.
- Ranking. Findings are ranked by impact. A badge shows the trend (New, Rising, Stable or Fading), and open findings that came back after being resolved carry a Regressed badge.
- Filters. The Status menu (Open, Regressed, Deferred, Won’t address, Resolved, Confirmed, All) shows counts. A second control narrows by Needs attention, Going well or Worth knowing. The shared filter bar narrows by period, channel and language. A channel or language counts only that segment’s conversations. An unused source has no conversations to narrow, so it leaves a narrowed view.
- Evidence. A card shows example visitor questions, each linked to its conversation for team members, and next-step links such as adding a source to the right library. A finding about a confusing source points at the source and doesn’t offer to edit the assistant’s instructions.
- Check now runs today’s check instead of waiting for the daily one.
- Change. With the filter bar set to show change, findings group by what happened: came back, new, resolved, grew, faded, or about the same.
- Comparing channels. With a channel and a second channel picked, findings group by where they happen: mostly at one channel, mostly at the other, no clear difference, or not tied to a channel. Rates are per hundred conversations so a busy channel doesn’t win on size. A finding counts as “mostly” at a channel only when its rate there is at least three quarters of the two channels’ combined rates and a statistical test says the gap is more than chance. A line above the list compares how the two channels’ conversations end.
The List/Map toggle shows where findings happened, on OpenStreetMap. A circle is a channel and a square is a source. Each is ringed by its findings’ polarities and counts them, and places too close together merge until you zoom in. A finding sits at every channel its conversations came through that has a location, or at its source when the source has a location. Picking a place lists its findings. Picking a finding shows its places, how many of its conversations were at each, and the source its answers leaned on. Findings with no located place, and the project’s overall outcomes, are listed apart from the map. The Map button is off while nothing can be placed. A PDF made from the map view prints a still map above the list.
For the map to show anything, give your channels a location. Every channel type has a Location field (where a kiosk stands, the school a learning channel serves, the area a campaign targets), with place search. See Channels.
The daily check sends one Slack message per project to the targets you set up, listing what is new, back, regressed or rising. It carries headlines, figures and recommendations, never the visitors’ questions.
List findings
Section titled “List findings”curl "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/findings?status=open" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"| Parameter | Type | Description |
|---|---|---|
status |
string | Only findings with this status: open, deferred, declined, resolved or confirmed. |
kind |
string | Only findings of this kind, one of the seven above. |
{ "projectId": "proj-uuid-...", "findings": [ { "id": "finding-uuid-...", "kind": "missing_knowledge", "status": "open", "title": "Visitors ask: \"Can I bring my dog?\"", "volume": 14, "metrics": { "...": "figures specific to the kind" }, "questions": ["Can I bring my dog?", "Are pets allowed inside?"], "source": null, "recommendation": { "lead": "Add an answer about pets to the knowledge base.", "points": ["14 conversations asked about this and got no answer."], "next": "Once it is done, mark this resolved. Ragtime watches these questions for 14 days to confirm it." }, "deferredUntil": null, "decisionNote": null, "decidedAt": null, "resolvedAt": null, "checkUntil": null, "firstSeenAt": "2026-09-21T03:00:12.000Z", "lastSeenAt": "2026-09-30T03:00:09.000Z", "testData": false, "impact": 24.5, "trend": "rising", "delta": 6, "regressed": false, "polarity": "attention" } ]}| Field | Description |
|---|---|
volume |
How many conversations the finding concerns. |
metrics |
The figures behind the finding; the keys depend on kind. |
questions |
The anonymized visitor questions behind it. |
source |
{ id, name } of the Knowledge Base source a source finding is about, or null. |
recommendation |
What to do: lead (one sentence), points (why, and where to start) and optionally next. |
checkUntil |
For a resolved finding: until when Ragtime watches for it coming back. After that it becomes confirmed. |
testData |
true when raised from generated test data rather than real conversations. |
impact |
Conversations affected, weighted by how badly. The list is ranked by it. |
trend |
Against the 14 days before: new, rising, stable or fading. null until the daily check has measured it. |
delta |
Conversations in the current window minus those in the window before. null when trend is null. |
regressed |
true for an open finding that came back after it was resolved. |
polarity |
attention, well or know. See the table above. |
Act on a finding
Section titled “Act on a finding”curl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/findings/{findingId}" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "action": "defer", "deferUntil": "2026-11-01" }'| Field | Type | Description |
|---|---|---|
action |
string | Required. resolve, defer, decline, dismiss or reopen. |
deferUntil |
string | Required to defer. A future date (YYYY-MM-DD), taken as the start of that day in UTC; at most 365 days ahead. |
note |
string | Required to decline: why it won’t be addressed, so nobody raises it again (max 2000 characters). |
The rules match the dashboard. A finding that needs attention is resolved, deferred or declined, and anything else is only dismissed or reopened; using the wrong action returns 400. Resolving an already-resolved finding, dismissing a dismissed one or reopening an open one returns 409. Resolving a source finding also resolves the same finding in every other project that shares the source’s library. The finding’s history records the API key that acted.
{ "status": "deferred", "siblingsResolved": 0 }Run the check now
Section titled “Run the check now”curl -X POST "https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights/findings/check" \ -H "Authorization: Bearer rt_live_YOUR_KEY_HERE"Runs the same pass the daily job runs, so repeating it is safe. Returns how many findings it opened, brought back, reopened, confirmed fixed and flagged as rising:
{ "opened": 2, "returned": 0, "reopened": 1, "confirmed": 3, "trending": 1 }What the API does not cover
Section titled “What the API does not cover”These parts of the Insights page exist in the dashboard only:
- Narrowing findings.
GET /insights/findingstakesstatusandkind. It has no period, channel or language filter, and no change or channel comparison. The rawmetricsobject includes the per-channel counts, but the grouping the dashboard does with them is not exposed. - The map and the overview. Where findings happened, and the overview above the list, have no endpoint.
- Language. The metrics, question, highlights and export endpoints filter by
deviceIdbut not by language. - Change against other dates.
compareToPreviouscompares with the period just before. Comparing with dates you choose is dashboard only. - Findings export. The CSV and JSON export of the findings list is a dashboard action. The list endpoint returns the same data.
- Settings. Data collection, the date format, purging conversations and generating or removing test data have no endpoints.
Rate Limiting
Section titled “Rate Limiting”The metrics, question, highlights, export, run and findings endpoints are rate-limited per API key. Default: 30 requests per minute. Rate limit headers are included in every response:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per window. |
X-RateLimit-Remaining |
Remaining requests in current window. |
X-RateLimit-Reset |
Unix timestamp when the window resets. |
Retry-After |
Seconds to wait (only on 429). |
Error Responses
Section titled “Error Responses”| Status | Meaning |
|---|---|
400 |
Invalid date format, missing question, question too long, or a finding action missing its deferUntil or note. |
401 |
Missing or invalid API key. |
403 |
Key doesn’t match the project or lacks the required scope. |
404 |
A referenced device (deviceId or compare), saved query or finding was not found. |
409 |
A saved query with that name already exists, or the finding is already in the requested state. |
429 |
Rate limit exceeded. Check Retry-After header. |
Using with N8N
Section titled “Using with N8N”Use an HTTP Request node to pull weekly insights into Slack digests, spreadsheets, or BI tools:
// N8N HTTP Request node configuration:// Method: GET// URL: https://your-ragtime-instance.com/api/v1/projects/{projectId}/insights// Authentication: Header Auth// Header Name: Authorization// Header Value: Bearer rt_live_YOUR_KEY_HERE// Query Params:// from: {{ $now.minus(7, 'days').format('yyyy-MM-dd') }}// to: {{ $now.format('yyyy-MM-dd') }}// compareToPrevious: true