Skip to main content

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.

StatusMeaning
pendingNeeds Expert action. Default state for new and reopened conversations.
awaiting_clientThe Expert has replied; we are waiting on the client.
resolvedClosed, no further action expected. Terminal state β€” the per-thread "Resolve" action moves a conversation here.
snoozedTemporarily hidden until a deferral expires; resurfaces automatically with a synthetic activity entry (#393).

"open" is accepted in a few places as a legacy alias for pending (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 pending when the deferral window expires.

Actions, endpoints, and who can trigger them​

ActionEndpointEffectWho
ResolvePATCH /conversations/:id/resolve (alias /archive, deprecated #2002)β†’ resolvedAny authenticated user β€” client, Expert, AM, SuperAdmin (IDOR enforced at the service layer, not by PlatformRolesGuard).
ReopenPATCH /conversations/:id/reopen (alias /unarchive, deprecated)resolved β†’ pendingAny authenticated user (same model as Resolve).
DismissPATCH /conversations/:id/dismissawaiting_client β†’ resolved (client-initiated)Client, from the briefing surface.
Set statusPATCH /conversations/:id/statusany of pending / awaiting_client / resolved / snoozedExpert / 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_client threads.

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​

  • #2002 β€” backend collapse of archived into resolved.
  • #2684 β€” client portal "Archive"β†’"Resolve"/"Reopen" rename + removal of the client-only archived notion.
  • #393 β€” snooze auto-resurface.