Skip to content

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-token

Default 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 via POST /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:

Terminal window
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"):

Terminal window
# 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

FeatureRead-only behavior
Send / edit / delete messagesHidden; RLS blocks writes
ReactionsSummaries visible; picker hidden
Typing / presenceNot subscribed
Mark readNot invoked
Message chime / web pushNot used in operator panel
Info sidebarMember/media tabs read-only