# Working tickets

> Reading an investigation, re-running it, pinning a playbook, and following up.

Source: https://triagic.com/docs/desktop/tickets

**Tickets** is the inbox. Tickets arrive from whichever
[ticket source](/docs/desktop/ticket-sources) you connected, by webhook or by a
poller that runs every two minutes.

Whether they are then investigated depends on that source. Auto-triage is **off by
default** on a connected source: tickets ingest, get embedded and classified against
your playbooks, and wait for someone to hit **Investigate now** on the ticket. Turn
triage on for the source and each new ticket is investigated in the background. By
the time you look at one, it usually already has an answer. Tickets that have no
source config (Slack, manual ones, and checkup findings promoted to tickets) always
triage on arrival, because there is no toggle for them.

**Native form submissions are the exception that runs the other way.** Their triage
setting is per form rather than per source, it lives in the cloud portal, and it starts
**on**: a submission is investigated as it arrives unless an admin turns that form's
switch off. The desktop's Forms card has no triage toggle for the same reason. See
[Forms](/docs/admin/forms).

## Reading an investigation [#reading-an-investigation]

Open a ticket and the **AI Investigation** section holds the result:

* **Root cause.** What the agent concluded, in a sentence or two.
* **Evidence.** Cited per system. This is the part to actually read. A root cause
  with three corroborating sources is worth more than a confident one with none.
* **Tool chips.** Every tool the agent called, in order. These tell you where it
  looked, which is as informative as what it found.
* **Funnel drop-off.** The step this user fell out of, if the PostHog funnel lookup
  is configured on the machine running Triagic.
* **Cost chip.** What the run cost.

> **Note:** The agent is read-only, by construction
>
> Write protection is enforced at the MCP server level (read-only flags,
> non-destructive tool sets), not by asking the model nicely. It cannot change your
> data even if it decides it should.

## When the answer is wrong or thin [#when-the-answer-is-wrong-or-thin]

Three levers, in increasing order of effort:

**Ask a follow-up.** The ticket has a chat below the investigation. It runs under the
same playbook and the same tool allowlist, and it remembers the conversation: the
last 20 turns are replayed on every call, so you can build on previous answers
instead of restating context. Ticket chats are shared per ticket, not private to you,
so a colleague can pick up where you left off.

**Pin a different playbook and re-investigate.** If the agent looked in the wrong
systems, the playbook is usually why. Assign a better-fitting one from the ticket
page and hit **Re-investigate**.

**Fix the playbook.** If the same class of ticket keeps going wrong, that is a
playbook problem, not a per-ticket one. See
[Anatomy of a playbook](/docs/playbooks).

## Playbook assignment, and what pinning does [#playbook-assignment-and-what-pinning-does]

Every ticket is classified against your playbooks at ingest. That assignment is
`auto`.

Assigning a playbook by hand **pins** it (`manual`): auto-classification will not
touch that ticket again. Clearing the assignment removes the pin, so the next
re-investigation may classify it afresh.

> **Warning:** New playbooks do not apply retroactively
>
> Classification only runs at ingest or on an explicit **Re-investigate**. A ticket
> already in the inbox will not pick up a playbook created afterwards until you
> re-investigate it.

If a playbook is disabled while tickets still reference it, those tickets keep the
label for history but fall back to generic triage: all tools, no playbook
instructions.

## Marking a ticket resolved [#marking-a-ticket-resolved]

**Resolve**, on the ticket header, sets the status to `resolved` and writes a
`ticket.resolve` audit row. It is refused while an investigation is running, and
resolving an already-resolved ticket does nothing, so a double click is safe. On a form
ticket it does one thing more, covered under [Form tickets](#form-tickets).

## Emailing the customer [#emailing-the-customer]

When an investigation produces a **Suggested reply to customer**, that card has a
**Send email** button next to the copy button. It opens a draft dialog: **To**
prefilled from the ticket's extracted email, **Subject** as `Re: <ticket subject>`,
and the body prefilled with the suggested reply, and nothing else. If the run wrote
no customer reply the body starts empty rather than pasting internal analysis at a
customer.

The same dialog is on the Console's answer bar, from the agent's last answer in a
thread. There the recipient is unknown, so **To** starts blank.

Everything in the dialog is editable and what you see is exactly what gets sent.
There is no server-side rewriting. Sending needs an email connection: your
organization's, set up in the portal under [Email](/docs/admin/email), or a Resend key
under [Integrations → Email (Resend)](/docs/desktop/ticket-sources#email-resend). Until
there is one, the dialog says so and you can still copy the draft out.

> **Note:** Only a human sends email
>
> The agent has no email tool. Not disabled, not gated. It does not exist. The
> read-only guarantee elsewhere is enforced at the MCP layer, and a tool running
> locally in the app would sit outside that gate, so the send path was deliberately
> left to a person clicking **Send** in a dialog they have read.

A ticket-linked send is written into that ticket's **Activity** timeline and audited
as `ticket.email_send` with the recipient and subject. A send from a Console thread
is audited against the thread instead.

## Activity [#activity]

Below the investigation, **Activity** is the ticket's paper trail: notes, logged
emails and file attachments from the ticket's source, interleaved with the emails
sent from Triagic. Emails show the subject, the author (**Customer** for inbound,
the HubSpot owner for outbound) and the body; attachments link out; long bodies
clamp behind **Show more**.

Only HubSpot supplies these records today. Zendesk, Jira, Thread and Slack tickets
show whatever was sent from here and nothing more. A form ticket keeps its customer
mail in its own section instead, described below.

Nothing re-reads a ticket after ingest, so this section is where provider-side
records get pulled: opening the ticket re-syncs live, caches what it got, and the
refresh button asks again. The cache is keyed on the provider's own record ids, so
re-syncing updates rather than duplicates. Attachment links are signed URLs that
expire; the re-sync on open is what re-signs them.

> **Note:** Missing scopes degrade one section, not the panel
>
> Notes, emails and attachments each need their own HubSpot scope:
> `crm.objects.notes.read`, `sales-email-read`, `files&#x60;. A token missing one drops
> that section only; the rest still renders, with the failure shown above the
> timeline as &#x2A;"Couldn't refresh from the source"* and the previously synced records
> still listed.

## Form tickets [#form-tickets]

A ticket from one of your organization's own [forms](/docs/admin/forms) carries the
whole customer email conversation with it, under **Customer email**: the automatic
acknowledgement, every reply from your team, every reply from the customer, and any
status notice, oldest first. Attachments on each message link out.

Below the thread is a reply box, prefilled from the investigation's suggested reply when
there is one. It sends on the existing mail thread, so the customer sees one
conversation rather than a new mail each time. Two things differ from the **Send email**
dialog above:

* It does **not** use this machine's Resend connection. Triagic's cloud sends it, so
  every threading header stays in one place. What it needs instead is this machine being
  signed in to your Triagic cloud account.
* A reply from the customer comes back onto the same ticket, within about three minutes.
  The refresh button next to the section heading checks immediately.

**Resolve** on a form ticket pushes the status up to the portal, and mails the customer
that their request is resolved when that form has status emails turned on. That is why
it asks you to confirm here and nowhere else: it is the one status change that leaves
this machine.

## Filtering [#filtering]

The inbox filters by playbook, including an explicit "no playbook" option. That is
the fastest way to find the tickets your classifier is not catching, which is the
feedback loop for improving routing hints.

## Copying things out [#copying-things-out]

Every Console prompt and answer, every chat message including your own, and the whole
investigation as markdown each have a copy button. Nothing is trapped in the app.
