Chat Metrics

Chat metrics let you pull the raw conversational data behind a chat AI Employee (AIE) into your own data warehouse or BI tool. Where the in-app metrics dashboard gives you charts inside Ema, the chat metrics export gives you the underlying rows — one per message — so you can join, aggregate, and analyze them however you need.

The export is served by the chat service over two endpoints: a modern, streaming messages export (32 columns) and a legacy, wire-compatible conversations export (22 columns). Both support CSV; the messages export also supports NDJSON and paginated JSON.

Admin-only, and enabled per environment. Both export endpoints require the system_admin role, and they are gated behind a feature flag (EMU_CHAT_METRICS_API_ENABLED): the routes are not registered until your environment is configured for the export. If the endpoints return a not-found, the export isn't enabled for your tenant yet.

Messages export (32 columns)

GET /metrics/messages returns row-level conversational data — one row per chat message — with the message joined to the workflow run that produced it, retrieval sources, user feedback, conversation review state, and non-PII user context. This is the export to use for new BI integrations.

Formats

Choose the format with the format query parameter (CSV is the default; the Accept header is honored as a fallback when format is omitted):

formatBehavior
csv (default)RFC 4180 CSV, streamed via chunked transfer with Content-Disposition: attachment. One header row, then one row per message. Streams the full filtered range.
ndjsonOne JSON record per line, no envelope, streamed. Streams the full filtered range.
jsonOne paginated page: { items, next_cursor, page_info }. You walk next_cursor until it's null.

Repeated or unbounded structures (citations, model breakdown, user attributes) are JSON-encoded into single columns (sources_json, model_breakdown_json, user_attributes_json) so a warehouse PARSE_JSON() works without exploding the row count.

Filters

ParameterMeaning
sinceInclusive lower bound on the message created_at (RFC 3339).
untilExclusive upper bound on created_at (RFC 3339). Defaults to now.
updated_sinceSwitches the time filter to a 30-day look-back over message, feedback, and review update times — returns rows whose message, feedback, or review state changed at or after this timestamp (RFC 3339).
workflow_idRestrict to one AI Employee (UUID).
cursorOpaque keyset cursor from the previous page's next_cursor. Only valid with format=json.
limitPage size for format=json (1–500, default 200). CSV and NDJSON ignore this and stream the full range.

Supplying cursor or limit with a streaming format returns a validation error (422) — the streaming formats always walk the entire filtered range.

Columns

The CSV header is fixed and ordered — 32 columns. Each row carries these columns:

ColumnNotes
message_id
roleMessage author role.
contentMessage text (PII rendered per tenant policy — see below).
created_at
session_id
session_title
session_started_at
session_deleted
session_deleted_at
channel_typeThe channel the conversation came through.
channel_display_name
workflow_idThe AI Employee.
run_idThe workflow run that produced the message; empty when there is none.
run_status
run_latency_ms
run_total_tokens
feedback_sentimentEmpty when no feedback.
feedback_category
feedback_text
feedback_submitted_at
feedback_updated_at
review_resolution_statusConversation review state; empty when not reviewed.
review_comment_count
review_updated_at
user_actor_type
user_external_id
user_display_name
user_department
user_attributes_jsonJSON object; {} when empty.
sources_jsonJSON array of retrieval sources; [] when empty.
model_breakdown_jsonJSON array of per-model token usage; [] when empty.
schema_versionThe export schema version. Adding a column at the end is non-breaking; reordering or removing is announced via a Sunset header and a schema_version bump.
curl -G 'https://<your-tenant>/api/v1/chat/metrics/messages' \
  -H 'Authorization: Bearer <token>' \
  --data-urlencode 'format=csv' \
  --data-urlencode 'since=2026-05-01T00:00:00Z' \
  --data-urlencode 'until=2026-06-01T00:00:00Z' \
  --data-urlencode 'workflow_id=8f3c....'

Streaming, limits, and errors

  • One streaming export per tenant. CSV and NDJSON are subject to a per-tenant concurrent-stream cap — only one streaming export runs at a time, and the cap is shared across both endpoints so you can't open /metrics/messages and /metrics/conversations in parallel to dodge it. A second concurrent request gets 429 with a Retry-After. Paginated JSON is not subject to the cap.
  • Mid-stream truncation. Because streaming sends 200 OK and the header row before all rows are known, an error after streaming has begun can't change the status code. Detect truncation by row count, not by HTTP status.
  • Slow queries. A query that exceeds the database statement timeout returns 503 with a Retry-After; narrow the time window and retry.
  • Auditing. Every export is recorded as an audit event (with format, row count, and the filters used), because content may be returned in cleartext depending on tenant PII policy.

Conversations export (legacy, 22 columns)

GET /metrics/conversations is a wire-compatible replacement for the legacy chatbot/conversations export. It returns one row per chat message in the window, denormalized with conversation, external-user, feedback, and workflow-run-error context, in a fixed 22-column order that matches the legacy exporter exactly — so existing downstream warehouses keep working without a contract change.

Parameters

ParameterMeaning
persona_id (required)The AI Employee (UUID) — the chat session's workflow_id.
start_date (required)Inclusive lower bound, UTC YYYY-MM-DD.
end_date (required)Inclusive upper bound, UTC YYYY-MM-DD. The total range must be ≤ 31 days.
formatcsv (default) or json. json returns an array with the same field names as the CSV header.

An empty range returns 200 with a header-only CSV. An invalid persona_id, malformed dates, or a range over 31 days returns 422.

Columns

The 22 columns, in order:

conversation_id, conversation_source, conversation_created_at, conversation_updated_at, encrypted_user_email, message_id, is_user_message, message_created_at, message_updated_at, workflow_run_error, external_tool_called, external_tool_call_success, external_tool_name, user_feedback_type, user_feedback_comment, feedback_message_sent, content_payload_json, user_context, unique_source_ids, country, band, events_per_message.

Which export to use. Use the messages export for new integrations — it streams, supports NDJSON and JSON, has richer columns, and carries a schema_version. Use the conversations export only when you must match the legacy 22-column contract a downstream warehouse already depends on.

A note on PII and content

Message content and user context may contain personal data. What's returned depends on your tenant's PII policy: content can be rendered in cleartext for tenants that have opted in, which is exactly why every export is audited. The export never silently falls back to raw cleartext when the PII service is unavailable — it returns 503 instead. Treat exported files as sensitive data and handle them under your organization's data-governance rules.

What's next

Last updated: Jul 3, 2026