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

# Ticket mirrors

> Ticket mirrors are interactive Ravenna ticket Adaptive Cards in Microsoft Teams that update in real time as assignees, status, and priority change.

Ticket mirrors are interactive representations of Ravenna tickets that appear in Microsoft Teams as Adaptive Cards. They update automatically as the ticket changes so you can track and manage tickets without leaving Teams.

## Mental model

A Teams ticket mirror is an Adaptive Card representation of a Ravenna ticket attached to a `ChatMessage` row. There can be many mirrors per ticket (request thread, triage thread, DM to approvers, etc.), and Ravenna keeps each in sync by tracking the underlying Bot Framework `activityId` per mirror.

Outbound updates route through a delivery chooser:

* **Bot path**: when a `ChatConversation` exists for the ticket with a known `serviceUrl`, Ravenna sends via the Bot Framework Connector using a cached bot token (client credentials flow).
* **Graph path**: when no conversation exists yet, the integration would create one via Microsoft Graph. This path is currently deferred and the event is skipped.

***

## Event → card mapping

| Ticket event                                                                      | Behavior                                                                                                                                                                                                    |
| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TICKET_PUBLISHED`                                                                | Post a ticket card. If the source Channel has triage forwarding enabled, also post a card to the workspace's Teams triage channel.                                                                          |
| `TICKET_UPDATED`, `TICKET_ARCHIVED`, `TICKET_UNARCHIVED`, `MOVED`, `SET_PRIORITY` | Update existing ticket cards in place using the stored `activityId`.                                                                                                                                        |
| `MESSAGE_CREATED`                                                                 | Send the message via the outbound renderer (form card, authored card, or plain Teams markdown depending on the message type). Skipped for private messages and for inbound messages echoed back from Teams. |
| `MESSAGE_EDITED`                                                                  | Update the previously-sent activity.                                                                                                                                                                        |
| `MESSAGE_DELETED`                                                                 | Delete the previously-sent activity.                                                                                                                                                                        |
| `SET_STATUS`                                                                      | Post or update a status change card.                                                                                                                                                                        |
| `ASSIGN`, `UNASSIGN`                                                              | Post or update an assignment card.                                                                                                                                                                          |
| `APPROVAL_ROUND_APPROVER_ADDED`                                                   | DM each newly-added approver an approval request card. Does not require an existing conversation.                                                                                                           |
| `TICKET_APPROVAL_COMPLETED`                                                       | Post or update an approval-completed card showing the outcome.                                                                                                                                              |

***

## Outbound content pipeline

Outbound rendering is owned by `TeamsOutboundRenderer`:

1. **Mention translation**: Ravenna user mentions in the message are resolved to Teams users via the integration user store. Mapped users become `<at>Display Name</at>` tokens; unmapped users degrade to bold text.
2. **Content conversion**: RavMarkdown is converted to Teams markdown via `RavMarkdownToTeamsMarkdownAdapter`, preserving `<at>` tokens.
3. **Activity assembly**: `buildSendActivity` attaches Bot Framework `entities[]` for each mention so Teams renders the mention chip and notifies the user. The `<at>` token text must match the entity's `text` field exactly.
4. **Author attribution**: Human-authored messages are wrapped in an authored-message Adaptive Card (avatar + name header). AI-generated messages keep the plain-text path. `<at>` resolution inside cards is handled via `msteams.entities` on the card body, because activity-level entities don't resolve mentions inside Adaptive Card content.

***

## Idempotency

Each mirror is keyed by `(chatConversationId, ticketId, messageType)`. The triage mirror in particular checks for an existing ticket card before posting, so re-publishing a ticket never creates duplicates.

***

## Deep links

Ravenna constructs Teams deep links to channel messages from the inbound activity rather than calling the Graph API. The link format is:

```
https://teams.microsoft.com/l/message/{channelId}/{messageId}?tenantId=...&groupId=...&parentMessageId=...
```

Deep links are only generated for **channel** messages. 1:1 and group chats don't have a reliable public deep-link format, so Ravenna omits the link rather than producing a broken one.

***

## Constraints and gotchas

* The bot path requires a `serviceUrl` recorded on the `ChatConversation`. If it's missing, the event is skipped (Graph path is deferred).
* Private messages are not mirrored to Teams.
* Messages that originated in Teams are not echoed back to Teams (unless `aiGenerated` is true).
* Bot Framework activities are always sent as the bot. There is no API to post as another user, so author attribution uses authored-message cards instead.
