Skip to main content

Interaction Search deep dive

The Interactions page is the unified, cross-channel search surface for your workspace. It lists every messaging conversation and every voice call in one recency-ordered feed, applies the same filters to both arms, and exports the result to CSV. This guide covers the surface end to end: navigation, auth, the data model, filter combinations, the export contract, and how it relates to global search. Open it from the dashboard sidebar under Interactions, or call GET /api/v1/interactions directly.

1. Where the surface lives and who can use it

  • Dashboard: Interactions in the left sidebar, at /interactions.
  • API: GET /api/v1/interactions for the paginated search; GET /api/v1/interactions/export.csv for the bulk CSV export.

Auth posture

The page splits read and export into two deliberate gates: The search list inherits the same read posture as the conversations list, call log, and /search/global: any authenticated workspace member can read it. The CSV export is a bulk, org-wide pull of contact-linked interaction history, so it is gated more tightly.

The friendly 403 for viewers

When a viewer clicks Export CSV, the API returns 403 Permission denied and the dashboard shows a toast instead of a raw error page:
The search itself keeps working. If you see this toast, ask an owner or admin to either run the export or widen your role to include contacts:read.

2. The unified cross-channel model

An interaction is not a new record type. It is a derived projection that unions two existing record families:
  • Conversation rows — inbox threads from every messaging channel (SMS, WhatsApp, email, RCS, Viber, Instagram, Messenger, Telegram, LINE, web chat, Apple Messages, WeChat, Kakao, Zalo, and the AI agent channel). Each row carries the thread lifecycle status and the delivery status of its latest message.
  • Call rows — voice call-log entries, synthesized with channel: "voice". Each row carries the call disposition and direction, and deep-links into the call-detail page.
Because both arms read the same underlying tables, a filter decision you make here holds true on the inbox and call-log surfaces. There is no third collection to keep in sync.

How recency ordering works

Rows sort by last_activity_at descending:
  • A conversation row is stamped by its latest message time (last_message_at), falling back to created_at.
  • A call row is stamped by the call start (ended_at → answered_at → started_at → created_at), so a long running call enters the list when it begins, not when it ends.
The result is a single timeline where the most recent customer touch — regardless of channel — appears first. Pagination is a composite (last_activity_at, id) keyset cursor, so two rows that share the same second page through in a stable order without skips or duplicates.

Voice transcripts and message exchanges together

Voice calls do not carry a message preview; they render as a Call badge with direction. The transcript and recording live on the call-detail page behind the row. Message exchanges render a plain-text snippet of the latest message (HTML stripped for email). The list’s job is to point, not replay. Both surfaces answer “find something” across pillars, but they differ in scope and output:
  • /search/global is the Cmd-K command palette. It returns ranked matches across contacts, conversations, calls, agents, numbers, and segments, capped per pillar, with no export.
  • Interactions is a dedicated page over the conversation + call union. It returns one recency-ordered list, supports deep filters, and exports to CSV.
Use global search when you want the fastest ranked match across the whole workspace. Use Interactions when you need to browse, filter, or export a cross-channel timeline.

3. Filter combinations

All filters combine with AND across axes and OR within one chip row. Unknown channel, type, or status values are ignored rather than rejected.

By channel

The channel chip row mirrors the inbox vocabulary: sms, whatsapp, email, rcs, viber, instagram, messenger, telegram, line, web_chat, apple_messages, wechat, kakao, zalo, video, agent, voice. voice is the gate on call rows. Select only messaging channels and calls drop out. Select voice to include calls; select only voice to see calls exclusively.

By direction

Direction applies only to call rows. Conversation rows have no single direction, so their direction is null. Filter by direction in the API with inbound or outbound to isolate incoming or outgoing calls.

By date window

The dashboard offers presets: Last 24h, Last 7 days (default), Last 30 days, All time. The API accepts explicit ISO-8601 UTC bounds via since and until. until must be on or after since.

Per contact

  • Use the search box for a prefix-anchored match against the linked contact’s name, phone, or email.
  • Use the contact_id query parameter to restrict to one contact exactly.
  • Click the Contact column in the dashboard to open the contact record.

By agent

Filter by the agent channel to surface AI agent conversations in the unified list. The channel acts like any other channel chip: combine it with a date window or contact to see everything an agent handled in a given period.

By status

The statuses filter is free-form because conversation and call vocabularies differ. A status that exists on one arm matches zero rows on the other rather than failing the request. Common conversation statuses include active and closed; common call statuses include completed, no_answer, busy, and declined.

API example: combine channel, type, and date

4. CSV export

Select Export CSV in the page header to download the currently filtered result set. Narrow first, then export.

What is gated

Only owner, admin, and developer roles with contacts:read can export. Viewers see the friendly 403 toast described above. The export is rate-limited to 5 per minute.

Column contract

Columns are emitted in this order:

Row-order guarantee

CSV rows follow the same recency ordering as the on-screen list: last_activity_at descending, with id as the tie-breaker. The export walks the same cursor chain the dashboard uses.

Limits and truncation

  • Ceiling: 20,000 rows per export.
  • If the result set exceeds the ceiling, the CSV is truncated and the response carries X-Export-Truncated: true.
  • Filename: <stem>-YYYY-MM-DD.csv. The optional filename query parameter is sanitized to [A-Za-z0-9._-] and capped at 80 characters.
  • Audit: every export writes an audit entry with row count, truncation flag, and applied cap.

PII redaction

If your workspace has opted into transcript PII redaction under Settings → Privacy, the same masking applies to the CSV and the on-screen list. contact_name, contact_phone, contact_email, and preview are masked identically in both surfaces.

5. Walkthrough: find the call where the customer disputed the charge

A customer emailed support saying they disputed a charge during a call last week. You need the call row, not the email thread.
  1. Open Interactions.
  2. Type the customer’s name or phone into the search box.
  3. Chip Call under type and voice under channel.
  4. Pick Last 7 days.
  5. Look for the call row around the date the customer mentioned. The Preview column shows Voice call; the Channel / Status column shows voice and the disposition.
  6. Click the row to open the call-detail page and review the recording or transcript.
If the customer had also emailed about the dispute, switching the type chip back to All types would show the email thread and the call in the same recency-ordered list.

6. Walkthrough: export a slice as a viewer and hit the 403 fallback

A viewer is asked to pull last month’s WhatsApp interactions for a QA review.
  1. Open Interactions.
  2. Chip Conversation under type and whatsapp under channel.
  3. Pick Last 30 days.
  4. Click Export CSV.
  5. The dashboard shows the friendly 403 toast because the viewer role cannot export.
  6. The viewer copies the current filter state from the page and asks an owner or admin to run the export, or requests the contacts:read scope.
The same export from an admin session succeeds:

7. Troubleshooting

If the list or export behaves unexpectedly, start with these checks:
  • Empty results — Loosen one filter at a time. A status value from one arm matches zero rows on the other. Confirm the date-range preset is not too narrow.
  • Calls are missing — voice must be in the channel chip set, or no channel filter at all. The type filter must also include Call.
  • Export fails with 403 — You hold a viewer role or lack contacts:read. Search is unaffected.
  • CSV has fewer rows than expected — You hit the 20,000-row ceiling; narrow the filters and re-export. The X-Export-Truncated header confirms truncation.
For the full troubleshooting guide, including cursor pagination edge cases and the flat 403 response shape, see Interactions list CSV export troubleshooting.

See also