Three dispatch() actions for querying an account's historical (already-persisted)
conversation data via the Conversational Cloud Messaging History (msgHist) service — as
opposed to the live conversation store, which only reflects conversations the current agent
session has received over the event stream.
Clients: all examples use only the public MessagingClient API —
client.dispatch({ action, value }). dispatch() returns a Promise; always await it.
Privilege: all three actions require LP privilege 1713 on the agent. The SDK does not
block the call client-side if the privilege is missing — the request is still sent and the
account's backend enforces it — so treat a rejection as a signal to check the agent's
privileges, not as a bug.
Overview
| Action | Purpose | Typical trigger |
|---|---|---|
conversation/search-by-id |
Fetch one conversation's full data (messages, statuses, participants, SDEs, transfers, survey, summary) by conversationId
|
Conversation focus / opening a transcript |
conversation/get-consumer-conversations |
Fetch a consumer's past conversations by consumerId
|
Conversation focus / opening a "history" panel for the current consumer |
conversation/filter-history-conversations |
Search across all of an account's historical conversations with rich filters (time range, skill, agent, keyword, CSAT, SDE content, etc.) | The "All Conversations" tab in a supervisor/manager workspace |
All three resolve to the same result shape:
interface ConversationSearchResult {
conversationHistoryRecords: Record<string, unknown>[];
_metadata?: Record<string, unknown>;
}
Each entry in conversationHistoryRecords is one conversation record. The record is not a
strongly-typed SDK interface (it passes through the underlying msgHist payload largely
as-is), but its shape is consistent: info (status, start/end times, latest agent/skill),
dialogs, agentParticipants, consumerParticipants, messageRecords, messageStatuses,
and — when requested via contentToRetrieve — sdes / unAuthSdes (each an envelope object
with an events array of Structured Data Events), transfers, summary, and survey data. Plan
to write a small mapper in your app that picks the fields your UI needs, the same way the demo
agent's mapConversationHistoryRecord.ts does.
conversation/search-by-id
What it does: Looks up a single conversation by ID and returns its full historical record — messages, message delivery statuses, participants, dialogs, transfers, summary, survey results, and SDEs (both authenticated and unauthenticated).
Why: This is the only way to retrieve a conversation's complete data set (particularly SDEs and message statuses) once it's no longer live in the current session's event stream — the live conversation store doesn't carry SDEs at all, and it drops a conversation's data once the agent's session moves on.
When to use it: On conversation focus — i.e. whenever the agent opens/expands a specific
conversation (from "All Conversations" or from any other list of conversation IDs) and you need
to render its transcript or SDEs. This does not apply to rows surfaced by
conversation/get-consumer-conversations: those records already come back with their transcript
included, and their SDEs are identical to the focused conversation's SDEs (SDEs are tracked per
consumer, not per conversation) — so there's no need to re-fetch either via search-by-id.
Signature:
client.dispatch({
action: 'conversation/search-by-id',
value: {
conversationId: string,
contentToRetrieve?: string[], // defaults to a broad set (messages, statuses, sdes, transfers, summary, survey, ...)
},
}): Promise<ConversationSearchResult>
conversationHistoryRecords will contain zero or one record (matching the given
conversationId).
How to use it / flow:
- User focuses a conversation (clicks it in a list, or opens it from a notification).
- Dispatch
conversation/search-by-idwith that conversation's ID. - Take
result.conversationHistoryRecords[0]:- Map
messageRecords+messageStatuses(+dialogs,agentParticipants,consumerParticipantsfor attribution) into your transcript view. - Map
sdes/unAuthSdesinto your SDE / customer-context widget.
- Map
const result = await client.dispatch({
action: 'conversation/search-by-id',
value: { conversationId },
});
const record = result.conversationHistoryRecords[0];
if (record) {
renderTranscript(record); // messageRecords + messageStatuses
renderSdeWidget(record); // sdes / unAuthSdes
}
If both the transcript view and the SDE widget read from this same call, consider
de-duplicating concurrent calls for the same conversationId (an in-flight promise cache keyed
by conversationId) rather than firing a fresh request per consumer.
⚠️ Note:
offset/limit/query params for this endpoint are fixed by the SDK (a single-record lookup) — they are not exposed as options.
conversation/get-consumer-conversations
What it does: Looks up all previous conversations belonging to a specific consumer
(consumerId), each returned in the same record shape as above — including transcript
(messageRecords/messageStatuses) and SDEs by default, subject to the contentToRetrieve
you pass.
Why: Gives an agent (or the UI) the consumer's prior conversation history for context — "has this person contacted us before, and about what."
When to use it: On conversation focus — when an agent opens a conversation and you want to show that consumer's past conversations (not the current one), e.g. in a "history" side panel. You need the consumer's ID first (typically read from the active conversation's participants).
Signature:
client.dispatch({
action: 'conversation/get-consumer-conversations',
value: {
consumerId: string,
contentToRetrieve?: string[],
status?: string[], // defaults to ['CLOSE'] — past conversations are normally closed ones
},
}): Promise<ConversationSearchResult>
How to use it / flow:
- Resolve the consumer's ID from the active conversation's
mainDialog.participants(roleCONSUMER). - Dispatch
conversation/get-consumer-conversationswith thatconsumerId. - Render
result.conversationHistoryRecordsas a list in a "history" widget (or above the transcript) — one row per past conversation (start date, status, latest agent/skill, summary). - Each record already includes its own transcript, and its SDEs are the same SDEs as the
currently focused conversation (SDEs are per consumer, not per conversation) — so selecting a
row does not require a follow-up
conversation/search-by-idcall just to get transcript or SDE data; render directly from the record you already have.
const result = await client.dispatch({
action: 'conversation/get-consumer-conversations',
value: { consumerId },
});
renderHistoryWidget(result.conversationHistoryRecords);
Re-run this whenever the focused conversation's consumer changes (e.g. watch the active
conversation ID), and clear/replace the previous consumer's list first so stale data isn't shown
briefly for the new consumer.
conversation/filter-history-conversations
What it does: Searches across all of the account's historical conversations, with a
rich set of optional filters — time range, status, skill, agent, keyword, CSAT, duration,
response time, SDE content, agent survey answers, and pagination (offset/limit).
Why: Powers broad, ad-hoc conversation lookups — mostly a manager/supervisor need — rather than a single agent's own context. This is what backs an "All Conversations" tab where a manager filters by date range, skill, or keyword across the whole account.
When to use it: For an "All Conversations" (or "conversation search") view — as used by the
AgentWorkspace — not tied to a single focused conversation. Call it with no value at all to
get a sane default (last 7 days, OPEN+CLOSE, first 50 results).
Signature:
client.dispatch({
action: 'conversation/filter-history-conversations',
value?: AllConversationsFilter,
}): Promise<ConversationSearchResult>
AllConversationsFilter (all fields optional):
interface AllConversationsFilter {
conversationId?: string; // see note below — short-circuits everything else
status?: string[]; // default: ['OPEN', 'CLOSE']
start?: { from: number; to: number }; // epoch ms; default: last 7 days
responseTime?: { from: number; to: number };
csat?: { from: number; to: number };
duration?: { from: number; to: number };
keyword?: string;
summary?: string;
skillIds?: string[];
agentIds?: number[];
agentGroupIds?: number[];
latestAgentIds?: number[];
latestSkillIds?: number[];
latestConversationQueueState?: string;
alertedMcsValues?: string[];
sdeSearch?: { personalInfo?: string; customerInfo?: string; purchase?: string; /* ...other SDE-type keys */ };
agentSurveySearch?: { surveyId?: string[]; questionKeywords?: string[]; answerKeywords?: string[]; pendingAgentSurvey?: boolean[] };
contentToRetrieve?: string[];
cappingConfiguration?: string;
offset?: number; // default: 0
limit?: number; // default: 50
}
How to use it / flow:
- Build an
AllConversationsFilterfrom the manager's UI controls (date pickers, skill/agent dropdowns, keyword box) and dispatch it. - Render
result.conversationHistoryRecordsas the "All Conversations" table/list. - Selecting a row is a conversation-focus event — same as above, follow up with
conversation/search-by-idfor the full transcript/SDEs. -
Auto-refresh via polling: since this is a REST lookup (not a subscription), if the list
needs to stay live, poll on an interval and slide the time window forward each tick — e.g.
keep
start.topinned to "now" and re-dispatch periodically (tens of seconds, depending on how fresh the view needs to be) rather than a single one-shot fetch:
async function pollAllConversations(filter: Omit<AllConversationsFilter, 'start'>, windowMs: number) {
const now = Date.now();
const result = await client.dispatch({
action: 'conversation/filter-history-conversations',
value: { ...filter, start: { from: now - windowMs, to: now } },
});
renderAllConversationsTable(result.conversationHistoryRecords);
}
const intervalId = setInterval(() => pollAllConversations(currentFilter, sevenDaysMs), pollIntervalMs);
// clearInterval(intervalId) when the tab is closed/unmounted
⚠️ Note —
conversationIdshort-circuits the filter:AllConversationsFilteris a flat interface, so nothing stops you from passingconversationIdalongside other fields (e.g.status,keyword,skillIds). IfconversationIdis present, the SDK does not run the general filtered search at all — it performs the same single-conversation lookup asconversation/search-by-id, forwarding onlyconversationIdandcontentToRetrieve. Every other filter field is silently ignored in that case. Don't mixconversationIdinto a filter you expect to actually apply; useconversation/search-by-iddirectly for single-conversation lookups instead.
Choosing between them
- Need one specific conversation's full data (transcript, statuses, SDEs)? →
conversation/search-by-id. - Need "what else has this consumer talked to us about"? →
conversation/get-consumer-conversations. - Need to search/browse across the whole account's history with filters (a manager's view)? →
conversation/filter-history-conversations. - Have a
conversationIdand nothing else? Preferconversation/search-by-iddirectly rather thanconversation/filter-history-conversationswith justconversationIdset — same result, clearer intent.