# How an investigation works

> The steps between a ticket arriving and an answer reaching the customer, and which configuration governs each one.

Source: https://triagic.com/docs/concepts/how-triagic-works

Understanding this sequence is what makes the configuration pages make sense. Each
step is governed by something an admin sets.

## The pipeline [#the-pipeline]

### The ticket arrives [#the-ticket-arrives]

Via a HubSpot webhook or the two-minute poller. Contact and company associations are
fetched, then an LLM extracts the identity fields Triagic needs:
`context`, `userId`, `email`.

*Governed by:* your HubSpot connection, and [PII redaction](/docs/admin/security#pii-redaction)
which scrubs the subject, body and metadata before any of it reaches a model.

### The user's funnel drop-off is computed [#the-users-funnel-drop-off-is-computed]

The last 72 hours of that user's events are compared against your configured product
funnel, producing the step where they fell out.

*Governed by:* the PostHog funnel lookup, which means `POSTHOG_HOST`, `POSTHOG_API_KEY` and
`POSTHOG_PROJECT_ID` on the machine running Triagic, plus a funnel definition at
`config/funnel.json` under that install's root. With none of that set the step is simply unavailable and the
rest of the pipeline continues. This is separate from PostHog as a *data source* for
the agent, which is a [catalog integration](/docs/integrations) like any other.

### Similar past tickets are retrieved [#similar-past-tickets-are-retrieved]

Vector search over previously triaged tickets pulls the top five most similar ones
and injects them, with their confirmed root causes, into the prompt. This is why
Triagic gets better at your product over time without anyone training anything.

*Governed by:* whether your AI provider supports embeddings. Without embeddings,
vector search degrades gracefully rather than failing.

### A playbook is chosen [#a-playbook-is-chosen]

An LLM classifier matches the ticket against every enabled playbook's **description**
and **routing hints** and assigns one. An engineer can override the assignment from
the ticket page, which pins it so the classifier will not touch it again.

*Governed by:* your [playbooks](/docs/playbooks).

### The agent investigates [#the-agent-investigates]

The agent loops, up to 25 tool iterations, calling read-only tools across the MCP
servers the playbook allows, citing evidence per system, and finally writing a root
cause report. The ticket moves to `triaged` and the report appears in the inbox, with
follow-up chat available on the ticket.

*Governed by:* the playbook's **triage instructions** and **data sources**, and your
organization's [spend cap](/docs/admin/spending).

### A person answers the customer [#a-person-answers-the-customer]

The report includes a suggested reply. Someone reads it, edits it in a draft dialog
and presses **Send**. The agent has no email tool, by construction. The send is
recorded on the ticket's Activity timeline and in the audit log. See
[Emailing the customer](/docs/desktop/tickets#emailing-the-customer).

*Governed by:* the [Resend connection](/docs/desktop/ticket-sources#email-resend) on
the desktop Integrations page.
Without it the pipeline still runs; there is just nothing to send with.

## Where classification actually happens [#where-classification-actually-happens]

Only at ingest, or on an explicit **Re-investigate**. A ticket already sitting in the
inbox won't retroactively pick up a playbook that was created afterwards. You have
to re-investigate it. This trips people up right after they write their first
playbook.

A ticket that matches no playbook is triaged generically: the standard prompt, all
tools available. That is a working default, not a failure.

## Why scoping data sources is not just a permissions knob [#why-scoping-data-sources-is-not-just-a-permissions-knob]

A playbook's data-source allowlist is what the agent can *see*. Narrowing it makes
investigations faster and cheaper and stops the agent wandering into irrelevant
systems. But it also means a root cause living outside the allowlist is
unreachable, and the agent will correctly report that it could not determine one.

The honest test for a new playbook is to run the same ticket twice, once scoped and
once unscoped, and see whether the scoped run still finds the answer.

## The other direction: checkups [#the-other-direction-checkups]

This pipeline starts with a ticket, so it only ever answers "why did this break for
this customer?". [Checkups](/docs/checkups) are the same agent pointed the other way:
a standing procedure you run against your whole environment or your support
operation, on demand or on a schedule, asking what is wrong across all of it. They
are read-only under the same tool gate, and a finding can be promoted to a ticket,
which then enters the pipeline above like any other.

## Cost [#cost]

Every LLM call (agent iterations, the classifier, metadata extraction, embeddings)
is logged individually with its model, token counts, latency and USD cost. A single
investigation makes several calls and is presented as one folded total in the UI.

Cost is snapshotted at write time, so correcting a price later never rewrites
history. Totals, per-model latency percentiles, a per-task split and a daily cost
chart live on the desktop's **Usage** page; the organization-wide cap that stops runs
lives in the portal.
