Conversation Status Lifecycle
Authoritative reference for the lifecycle states a conversation (a "thread" in the client portal) can be in, the transitions between them, and which actions move a thread where.
Source of truth: ConversationStatus in api/src/common/entities.ts. The
queue mirrors it — QueueItemStatus = ConversationStatus.
States
There are four states. There is no archived state — it was collapsed
into resolved in #2002.
| Status | Meaning |
|---|---|
pending | Needs Expert action. Default state for new and reopened conversations. |
awaiting_client | The Expert has replied; we are waiting on the client. |
resolved | Closed, no further action expected. Terminal state — the per-thread "Resolve" action moves a conversation here. |
snoozed | Temporarily hidden until a deferral expires; resurfaces automatically with a synthetic activity entry (#393). |
"open"is accepted in a few places as a legacy alias forpending(e.g. status-pill rendering); it is not a canonical value and should not be used in new code or docs.
Transitions
pending ◄────► awaiting_client ────► resolved
│ │ │
│ │ └─ Resolve (terminal)
│ └─ Expert replies → awaiting_client
└─ Client sends a new message → pending
snoozed ──[deferral expires]──► pending (auto-resurface, #393)
resolved ──Reopen──► pending (Dismiss also lands at resolved)
- New + reopened conversations start at
pending. - Resolve is terminal until someone Reopens it (→
pending). - Snooze is a temporary hide that auto-returns to
pendingwhen the deferral window expires.
Actions, endpoints, and who can trigger them
| Action | Endpoint | Effect | Who |
|---|---|---|---|
| Resolve | PATCH /conversations/:id/resolve (alias /archive, deprecated #2002) | → resolved | Any authenticated user — client, Expert, AM, SuperAdmin (IDOR enforced at the service layer, not by PlatformRolesGuard). |
| Reopen | PATCH /conversations/:id/reopen (alias /unarchive, deprecated) | resolved → pending | Any authenticated user (same model as Resolve). |
| Dismiss | PATCH /conversations/:id/dismiss | awaiting_client → resolved (client-initiated) | Client, from the briefing surface. |
| Set status | PATCH /conversations/:id/status | any of pending / awaiting_client / resolved / snoozed | Expert / AM / SuperAdmin only (PlatformRolesGuard). |
resolvedAt is stamped on the first transition into resolved.
UI surfaces
Client portal (/client/chat)
- Sidebar filter pills: All / Active / Resolved (
Active= anything not resolved). - Per-thread row actions: Resolve (open thread) and Reopen (resolved thread). These were renamed from "Archive"/"Unarchive" in #2684; there is no separate "Archived" list.
- The briefing surface groups active threads and a "resolved this week" set, and
offers Dismiss on
awaiting_clientthreads.
Expert workspace (/workspace/queue)
- Status tabs: All / Pending / Awaiting Reply / Resolved / Snoozed
(
GET /expert-queue?status=all|pending|awaiting_client|resolved|snoozed). - Status dropdown on the ticket header writes via
PATCH /conversations/:id/status.
Not this field
The organization lifecycle (organizations.status: pending_am_setup,
active, deactivated, plus "auto-archived" stale-org handling) is a separate
concept. "Archived" wording in docs/archive/ and docs/_archive/ refers to
archival snapshots, not conversation status.
See also
- Expert Queue — Item States — the same four states from the Expert-queue-item perspective (
/workspace/queue).