> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Read the Topic Intelligence dashboard

> Operator guide to Insights → Topic Intelligence — the cross-channel 'what customers contact us about' surface that ranks every contact reason with its per-channel split, trend direction, and CSAT, deflection, and handle-time outcomes.

# Topic Intelligence

Topic Intelligence answers one question: **what are customers contacting you about, and is it rising or falling?** Open **Insights → Topic Intelligence** in the Orbit console to see every conversation across voice, SMS, WhatsApp, web chat, and email bucketed into its contact reason, ranked by volume, with each reason's trend direction, per-channel split, and outcome signals — CSAT, deflection, and average handle time. It sits one level up from [Contact reasons](/guides/contact-reasons): Contact reasons classifies text-inbox threads only; Topic Intelligence is the unified cross-channel read of the same question.

## What a "topic" is

A topic is the contact reason assigned to a conversation — the canonical cross-channel taxonomy for "what this person contacted us about." Two sources produce the label:

* **Upstream intent classification.** When the conversation-intelligence pipeline has already classified the thread (the `intent` label), that label wins.
* **Lexicon fallback.** When no upstream label exists, the service falls back to a built-in contact-reason lexicon over the conversation's summary or last message, so the surface works from day one — billing, refund, cancellation, technical issue, account access, delivery, sales inquiry, complaint, appointment, product support — with `other` as the catch-all bucket.

Every conversation lands in exactly one topic. The topic list is ranked by current-window volume, so the page reads "what drives our inbound load" rather than a flat glossary of categories.

## How topics are computed

The endpoint loads the most recent conversations (voice threads bridged through the call log, text threads through the conversation record) across the five supported channels — voice, SMS, WhatsApp, email, web chat — and aggregates them in one pass:

* **Current vs prior window.** The comparison window is split in half: the current span and an equal-length prior span immediately before it (a 30d read compares the last 30 days against the 30 days before that). Trend is always relative to the prior span.
* **Per-channel split.** Each topic carries its volume broken down by channel, so you can see that "delivery" is voice-heavy while "account access" is web-chat-heavy.
* **Trend direction.** A topic absent from the prior window is marked **new**; otherwise, a swing of at least 15% up or down marks it **rising** or **falling**, and anything inside that band stays **flat**.

Only conversations on the supported inbound channels count — this is an inbound contact-reason surface, not an outbound one.

## Metrics the surface reports

Each topic row rolls up four numbers:

* **Volume and trend.** Current-window conversation count, the prior-window count, the percentage change (or `new` when the prior window was empty), and the trend label — `rising`, `falling`, `flat`, or `new`.
* **CSAT.** Mean CSAT over current-window conversations that carried a score (1–5). `null` when none did, so an unrated topic reads as blank rather than zero.
* **Deflection rate.** The fraction of resolved conversations that closed with no human assignee — self-service resolution. A conversation still open counts toward volume but not toward deflection.
* **Average handle time (AHT).** Mean resolution time in seconds across current-window conversations with a recorded value. Conversations the idle-conversation sweep force-closed without being worked are excluded rather than counted, so a conversation that sat untouched for a week does not report that week as handle time.

Missing values stay `null` — the surface distinguishes "no signal" from "zero."

## Reading flow

1. **Pick the window.** The selector offers 24h, 7d, 30d (default), and 90d. Use 24h for incident triage and 30d or 90d for staffing and routing decisions where one bad afternoon should not move the number.
2. **Optionally filter one channel.** The channel filter narrows the corpus to a single surface — voice, SMS, WhatsApp, email, or web chat — when a portfolio-wide spike needs localizing.
3. **Read the ranked list.** Topics appear highest-volume first, so the top rows answer "what is consuming our inbound load." Scan the trend column, not just the volume: a small topic marked **rising** is the early-warning signal a large **flat** topic hides.
4. **Drill into a topic.** Each topic links up to five recent example conversations (id, channel, timestamp) so you can jump from a spiking reason to the raw threads behind it. Use the examples to check tag quality before acting on the trend.

## Decisions it feeds

* **Route the escalation team.** A topic marked **rising** with high volume and low deflection is the candidate for a staffing adjustment or a dedicated queue before it saturates.
* **Fix AI containment.** High volume + low deflection means your flow or AI agent is escalating what it should have resolved — that is the containment fix, not a routing fix.
* **Rebalance human handle time.** High handle time with high deflection is expensive: the flow resolved it without a human, but slowly — a cheaper automation target.
* **Audit the tagging.** If the top topic is `other` or a topic you do not recognize, drill into the linked examples. Mis-tagged or catch-all-dominated buckets mean the contact-reason taxonomy needs attention, not that customers are confused.

## API contract

The dashboard reads `GET /api/v1/analytics/topic-intelligence`. Use it directly when you want to export the list or feed a warehouse:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/analytics/topic-intelligence?window=30d" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

Query parameters:

| Parameter | Values                                          | Default      |
| --------- | ----------------------------------------------- | ------------ |
| `window`  | `24h`, `7d`, `30d`, `90d`                       | `30d`        |
| `channel` | `voice`, `sms`, `whatsapp`, `email`, `web_chat` | all channels |

Response `200 OK` (trimmed to one topic):

```json theme={null}
{
  "window": "30d",
  "channel": null,
  "current_since": "2026-08-01T00:00:00.000Z",
  "previous_since": "2026-07-02T00:00:00.000Z",
  "total_conversations": 4120,
  "topics": [
    {
      "topic": "delivery",
      "volume": 842,
      "previous_volume": 610,
      "change_pct": 38.0,
      "trend": "rising",
      "channels": { "voice": 270, "sms": 180, "whatsapp": 240, "email": 120, "web_chat": 32 },
      "avg_csat": 2.8,
      "deflection_rate": 0.62,
      "avg_aht_seconds": 214.5,
      "examples": [
        { "id": "conv_8f04c2", "channel": "whatsapp", "occurred_at": "2026-08-30T14:05:00.000Z" }
      ]
    }
  ]
}
```

| Field                                 | What it is                                                                    |
| ------------------------------------- | ----------------------------------------------------------------------------- |
| `window` / `channel`                  | echoed request context — `channel` is `null` when unfiltered                  |
| `current_since` / `previous_since`    | ISO-8601 bounds of the current and prior spans                                |
| `total_conversations`                 | conversations counted in the current window                                   |
| `topics[].volume` / `previous_volume` | current and prior span counts for the topic                                   |
| `topics[].change_pct`                 | relative change vs the prior window, percent; `null` when the prior was empty |
| `topics[].trend`                      | `rising`, `falling`, `flat`, or `new` (15% swing threshold)                   |
| `topics[].channels`                   | per-channel volume split within the current window                            |
| `topics[].avg_csat`                   | mean CSAT, or `null` when no scored conversation                              |
| `topics[].deflection_rate`            | self-service resolution fraction in `[0,1]`, or `null`                        |
| `topics[].avg_aht_seconds`            | mean handle time in seconds, or `null`                                        |
| `topics[].examples`                   | up to five most-recent threads for drill-in                                   |

Results are cached for one minute per (window, channel) pair; the console refreshes in the same cadence.

## See also

* [Contact reasons](/guides/contact-reasons) — the text-inbox reason classifier that Topic Intelligence generalizes cross-channel.
* [Sentiment portfolio dashboard](/guides/sentiment-portfolio-reading) — the *how customers feel* counterpart to *what they contact you about*.
* [Insights dashboards](/guides/insights-dashboards) — the catalog of every other Insights surface.
