Conversation Lifecycle
How conversations flow from creation to resolution — status transitions, auto-assignment, internal notes, and ticket conversion.
Overview
A conversation in ZapTicket represents a real-time chat thread between a visitor and your support team. Every conversation follows a clear lifecycle from creation through resolution.
Status Flow
┌──────────────────────────────────────────────────────────┐
│ │
│ Open ──────────────► Assigned ──────────────► Closed │
│ │ │ │ │
│ │ (auto-assign │ │ │
│ │ on first │ │ │
│ │ agent reply) │ │ │
│ │ │ │ │
│ └─────────────────────┼───────── Reopen ◄─────┘ │
│ │ │
└──────────────────────────────────────────────────────────┘Open
Initial state when a visitor starts a conversation. No agent has been assigned yet. The conversation appears in the shared inbox for all agents to see.
Assigned
An agent has taken ownership. This happens in two ways:
- Auto-assign on first reply — When an agent sends their first message in an open conversation, they're automatically assigned
- Manual assignment — An agent or admin explicitly assigns via the PATCH endpoint or dashboard UI
Closed
The conversation is resolved. The visitor's widget shows a "conversation ended" state. The agent can close conversations manually, or they can be closed programmatically via the API.
Auto-Assignment
When an agent sends their first public message (not an internal note) in a conversation that has status "open", the system automatically:
- Sets
assignedAgentIdto the replying agent - Changes status from
"open"to"assigned" - Broadcasts a
ConversationUpdatedevent to the tenant group
This eliminates the need for agents to manually claim conversations before responding — the act of responding is the claim.
Agent sends message in "open" conversation
→ if first public reply by any agent:
→ conversation.assignedAgentId = sender.agentId
→ conversation.status = "assigned"
→ emit ConversationUpdated event
Agent sends message in "assigned" conversation
→ no status change (already assigned)
Agent sends internal note
→ never triggers auto-assignmentInternal Notes
Agents can send internal notes within a conversation — messages visible only to other agents, never to the visitor. Internal notes are useful for:
- Collaborating with teammates on a tricky issue
- Leaving context for the next shift
- Documenting investigation steps
- Flagging potential escalation needs
{
"id": "msg_internal01",
"conversationId": "conv_abc123",
"content": "This customer has been charged twice — check Stripe dashboard",
"senderType": "agent",
"senderName": "Mike Lead",
"senderId": "agt_def456",
"isInternal": true,
"createdAt": "2024-01-15T14:29:30Z"
}Converting to Ticket
When a conversation requires follow-up work that extends beyond the real-time chat (bug investigation, feature request tracking, etc.), agents can convert it to a ticket:
Agent clicks "Convert to Ticket" in dashboard
→ POST /conversations/{id}/convert-to-ticket
→ New ticket created with reference (ZT-1044)
→ Ticket linked back to conversation via conversationId
→ Conversation can continue or be closed independently
→ Ticket has its own lifecycle (open → resolved → closed)A conversation can only be converted once. The ticket maintains a link back to the conversation for reference, and the conversation entity stores the linked ticket ID.
Real-Time Events
Every lifecycle transition emits SignalR events so connected clients stay in sync:
- New conversation →
ConversationCreatedto tenant group - Status change →
ConversationUpdatedto tenant + conversation group - Agent assigned →
ConversationUpdatedto tenant + conversation group - New message →
MessageReceivedto conversation group (+ tenant for notes) - Typing →
TypingChangedto conversation group