Operator Conversation View
Project Admins and Host Operators can open any tenant conversation in a read-only chat UI: full message history, attachments, reactions, in-thread search, and live updates — without send, edit, react, typing, or mark-read side effects. In direct (two-person) threads, sender names and avatars appear above each message run so operators can read who said what without mirroring participant left/right layout.
Dashboard (Project Admin)
From Conversations, click View on a row to open a large drawer (~80% width) with the restricted thread.
The dashboard mints a review JWT via:
POST /v1/tenants/:tenantId/users/:externalId/review-tokenDefault identity is the tenant system user (__system). The drawer lists only operator-capable provisioned users (appMetadata.tchatRole: "operator") plus system. The token TTL is 1 hour; the SDK refreshes it automatically while the drawer stays open.
Operator identities (host apps)
Review-token mint is allowed only for:
- The reserved system sender (
__system), or - Provisioned users with
appMetadata.tchatRole: "operator"(set viaPOST /v1/users)
Normal end-user identities can still receive participant JWTs (POST /v1/token) but cannot mint review tokens. Provision separate admin ids for Host Operator embeds:
curl -X POST "$API_URL/v1/users" \ -H "Authorization: Bearer $TENANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"externalId":"admin-ops","displayName":"Support Admin","appMetadata":{"tchatRole":"operator"}}'Review sessions are read-only by construction — the reviewer is not added as a conversation member and participants are not notified when someone views a thread.
Host app embed (Host Operator)
Never mount OperatorChatPanel with a participant JWT. Mint a review token on your server for an operator-capable identity (__system or a user provisioned with appMetadata.tchatRole: "operator"):
# Provision operator identity (once)curl -X POST "$API_URL/v1/users" \ -H "Authorization: Bearer $TENANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"externalId":"admin-ops","displayName":"Support Admin","appMetadata":{"tchatRole":"operator"}}'
curl -X POST "$API_URL/v1/review-token" \ -H "Authorization: Bearer $TENANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"externalUserId":"admin-ops"}'Response (1 hour TTL):
{ "accessToken": "eyJ...", "tokenType": "Bearer", "expiresIn": 3600}Decode sub from the JWT for userId. Wire the client:
import { ChatProvider, OperatorChatPanel,} from "@tesark/chat-react";import "@tesark/chat-react/styles";
<ChatProvider accessToken={reviewAccessToken} userId={reviewUserId} tenantId={tenantId} apiUrl={API_URL} refreshAccessToken={async () => { const response = await fetch("/api/chat/review-token"); const body = await response.json(); return body.accessToken; }}> <div className="h-[600px]"> <OperatorChatPanel initialConversationId={conversationId} /> </div></ChatProvider>What is disabled in read-only mode
| Feature | Read-only behavior |
|---|---|
| Send / edit / delete messages | Hidden; RLS blocks writes |
| Reactions | Summaries visible; picker hidden |
| Typing / presence | Not subscribed |
| Mark read | Not invoked |
| Message chime / web push | Not used in operator panel |
| Info sidebar | Member/media tabs read-only |