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 contentToRetrievesdes / 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:

  1. User focuses a conversation (clicks it in a list, or opens it from a notification).
  2. Dispatch conversation/search-by-id with that conversation's ID.
  3. Take result.conversationHistoryRecords[0]:
    • Map messageRecords + messageStatuses (+ dialogs, agentParticipants, consumerParticipants for attribution) into your transcript view.
    • Map sdes / unAuthSdes into your SDE / customer-context widget.
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:

  1. Resolve the consumer's ID from the active conversation's mainDialog.participants (role CONSUMER).
  2. Dispatch conversation/get-consumer-conversations with that consumerId.
  3. Render result.conversationHistoryRecords as a list in a "history" widget (or above the transcript) — one row per past conversation (start date, status, latest agent/skill, summary).
  4. 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-id call 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:

  1. Build an AllConversationsFilter from the manager's UI controls (date pickers, skill/agent dropdowns, keyword box) and dispatch it.
  2. Render result.conversationHistoryRecords as the "All Conversations" table/list.
  3. Selecting a row is a conversation-focus event — same as above, follow up with conversation/search-by-id for the full transcript/SDEs.
  4. 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.to pinned 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 — conversationId short-circuits the filter: AllConversationsFilter is a flat interface, so nothing stops you from passing conversationId alongside other fields (e.g. status, keyword, skillIds). If conversationId is present, the SDK does not run the general filtered search at all — it performs the same single-conversation lookup as conversation/search-by-id, forwarding only conversationId and contentToRetrieve. Every other filter field is silently ignored in that case. Don't mix conversationId into a filter you expect to actually apply; use conversation/search-by-id directly 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 conversationId and nothing else? Prefer conversation/search-by-id directly rather than conversation/filter-history-conversations with just conversationId set — same result, clearer intent.