Skip to main content

Export conversation sessions API

Returns one row per (agent session × matched ticket) for a date window — the same shape as Filum's scheduled Google Sheets export, exposed as a paginated JSON endpoint. A single conversation may produce several rows when it is handed between multiple agents and/or produces multiple tickets.

Prerequisites

To use the API, you need a token. Tokens can be generated by an admin in the settings section of the Filum CX platform. The token's permission set must include the $export_conversation_session resource with the list action.

API

Access the API via the endpoint: https://contact-center.filum.ai/

GET /external/conversation-sessions

Summary

List per-agent conversation sessions joined with their tickets, filtered by conversation updatedAt window.

Query parameters

NameDescriptionRequiredSchema
startDateInclusive lower bound on the conversation updatedAt (ISO date, YYYY-MM-DD). Conversations created before this date are still returned if they had activity (new message, status change, etc.) inside the window.Yesstring (date)
endDateInclusive upper bound on the conversation updatedAt (ISO date).Yesstring (date)
timezoneIANA timezone for interpreting startDate / endDate boundaries (start at 00:00:00, end at 23:59:59.999 in this tz) and for computing business-hours metrics. Defaults to Asia/Singapore.Nostring
organizationIdOrganization id. Defaults to the authenticated member's organization.Nostring
sessionAgentEmailsComma-separated list of session agent emails to filter by.Nostring
assigneeAccountIdsComma-separated list of agent account ids (matches either the session-derived agent or the ticket's assignee_account_id).Nostring
sessionEndReasonsComma-separated list of session end reasons. Options: handoff_to_ai, terminal, replaced_by_other_handoff, service_account_takeover, open_at_end.Nostring
pagePage number to retrieve.Nointeger (>= 1, default: 1)
perPageResults per page.Nointeger (1..100, default: 50)

Header parameters

NameDescriptionRequiredSchema
authorizationThe authorization token. Format: Bearer <your_api_token>Yesstring
organization-idThe unique identifier of your organizationYesstring
auth-typeThe type of authorization. Options: token (for API tokens), account (for user sessions).Yesstring

Responses

CodeDescriptionContent type
200Successful responseapplication/json
401Unauthorizedapplication/json
422Validation errorapplication/json
429Rate limit exceededapplication/json

200 Successful response

Top-level envelope (same shape as /external/conversations):

FieldTypeDescription
itemsarray of ConversationSessionThe matching conversation-session rows
totalintegerTotal number of rows matching the filter (across all pages)
pageintegerCurrent page number
perPageintegerNumber of items per page
pagesintegerTotal number of pages

ConversationSession item:

FieldTypeDescription
SessionOne row per agent engagement window within a conversation.
sessionIndexinteger | nullZero-based index of this session within its conversation. null for rows that represent a ticket with no matched session.
sessionAgentEmailstringAgent who handled this session. Blank when the session activity carried no agent identity (e.g. an unattended route_back_to_human).
sessionAgentNamestringAgent display name.
sessionAgentAccountIdinteger | nullAgent account id from the activity stream.
sessionStartAtstring (date-time) | nullWhen the agent's session window opened (assign / route_back_to_human / human_take_over).
sessionTookOverAtstring (date-time) | nullWhen the agent explicitly took over, if distinct from the start.
sessionEndAtstring (date-time) | nullWhen the session closed (terminal action or handoff).
sessionStartActionstringThe activity action that opened the session.
sessionEndActionstringThe activity action that closed it (end_conversation, route_to_ai, route_to_rating, timed_out, …).
sessionEndReasonstringHigher-level reason: handoff_to_ai, terminal, replaced_by_other_handoff, service_account_takeover, open_at_end.
ConversationConversation-level fields (same value across every session row of the same conversation).
conversationIdstringThe Mongo _id of the conversation.
conversationCreatedAtstring (date-time) | null
conversationRoutedToAiAtstring (date-time) | nullFirst route_to_ai / route_back_to_ai event on the conversation.
conversationRoutedToAgentAtstring (date-time) | nullFirst route_to_human / route_back_to_human event.
conversationAssignedAtstring (date-time) | nullFirst assign event.
conversationAgentTookOverAtstring (date-time) | nullFirst human_take_over event.
sentToRatingAtstring (date-time) | nullThe route_to_rating event that closed this session (per-session, not conv-level).
conversationClosedAtstring (date-time) | nullThe conversation's close timestamp from Mongo.
conversationResolvedAtstring (date-time) | nullThe conversation-level "resolved" anchor used by Resolution Time (= conversationClosedAt).
CustomerSnapshot of the user object on the conversation.
customerFilumIdstring
customerNamestring
customerPhonestring
customerEmailstring
TicketThe ticket matched to this session by (assignee_account_id, time window). Fields are blank when the row is a session with no matched ticket.
ticketIdinteger | null
ticketCreatedAtstring (date-time) | null
ticketUpdatedAtstring (date-time) | null
ticketSubjectstring
ticketDescriptionstring
ticketStatusstring
ticketPrioritystring
ticketDueDatestring (date-time) | null
ticketAssignedAtstring (date-time) | null
ticketResolvedAtstring (date-time) | null
installedSourceIdinteger | null
installedSourceNamestring
channelTypestringE.g. chat.
Metric endpointsThe anchor timestamps used to compute durations.
botHandoffAtstring (date-time) | nullLatest route_to_human / route_back_to_human at or before the session start (bounded to "after the previous session ended" and within 24h of the session start). Falls back to sessionStartAt if no qualifying handoff exists.
firstAgentAssignedAtstring (date-time) | nullsessionTookOverAt ?? sessionStartAt.
firstAgentMessageAtstring (date-time) | nullFirst senderType=agent message strictly after firstAgentAssignedAt, inside the session window.
lastAgentMessageAtstring (date-time) | nullLast senderType=agent message inside the session window.
Metrics (calendar hours)Plain elapsed time, in seconds. null when an endpoint is missing.
waitTimeSecondsinteger | nullfirstAgentMessageAt − botHandoffAt.
firstResponseTimeSecondsinteger | nullfirstAgentMessageAt − firstAgentAssignedAt.
resolutionTimeSecondsinteger | nullconversationResolvedAt − botHandoffAt.
Metrics (business hours)Same spans intersected with the org humanSchedule (from BrandConfig) in timezone.
waitTimeBusinessHoursSecondsinteger | null
firstResponseTimeBusinessHoursSecondsinteger | null
resolutionTimeBusinessHoursSecondsinteger | null
CSAT
ratinginteger | nullSurvey star rating, if the customer responded.
resolutionYesNostringYes if the survey carried an explicit resolution=1 metric or the rating was ≥ 4; No if resolution=0 or rating ≤ 3; empty when no survey.
feedbackstringFree-text feedback, if any.
submittedAtstring (date-time) | nullWhen the survey was submitted.
Custom fields
customFieldsobject ({ [name: string]: string })Per-ticket custom fields, keyed by the field's display name.

Response sample

{
"items": [
{
"sessionIndex": 0,
"sessionAgentEmail": "alice@yourorg.com",
"sessionAgentName": "Alice Tan",
"sessionAgentAccountId": 1799,
"sessionStartAt": "2026-05-08T15:02:33.000Z",
"sessionTookOverAt": "2026-05-08T15:08:55.000Z",
"sessionEndAt": "2026-05-08T15:09:56.000Z",
"sessionStartAction": "assign",
"sessionEndAction": "route_to_rating",
"sessionEndReason": "terminal",

"conversationId": "69fdf7f9e1e9bcb8cf2bc4b9",
"conversationCreatedAt": "2026-05-08T14:49:00.000Z",
"conversationRoutedToAiAt": "2026-05-08T14:49:00.000Z",
"conversationRoutedToAgentAt": "2026-05-08T14:52:00.000Z",
"conversationAssignedAt": "2026-05-08T15:02:00.000Z",
"conversationAgentTookOverAt": "2026-05-08T15:08:00.000Z",
"sentToRatingAt": "2026-05-08T15:09:38.000Z",
"conversationClosedAt": "2026-05-08T23:45:00.000Z",
"conversationResolvedAt": "2026-05-08T23:45:00.000Z",

"customerFilumId": "fil_123",
"customerName": "FaFa",
"customerPhone": "+60129666852",
"customerEmail": null,

"ticketId": 705854,
"ticketCreatedAt": "2026-05-08T15:09:54.741Z",
"ticketUpdatedAt": "2026-05-08T15:14:45.354Z",
"ticketSubject": "Request from WhatsApp - FaFa",
"ticketDescription": "",
"ticketStatus": "closed",
"ticketPriority": "low",
"ticketDueDate": null,
"ticketAssignedAt": "2026-05-08T15:02:00.000Z",
"ticketResolvedAt": "2026-05-08T15:09:54.741Z",
"installedSourceId": 6537,
"installedSourceName": "WhatsApp MY",
"channelType": "chat",

"botHandoffAt": "2026-05-08T14:52:21.000Z",
"firstAgentAssignedAt": "2026-05-08T15:08:55.000Z",
"firstAgentMessageAt": "2026-05-08T15:09:03.000Z",
"lastAgentMessageAt": "2026-05-08T15:09:32.000Z",

"waitTimeSeconds": 1002,
"firstResponseTimeSeconds": 8,
"resolutionTimeSeconds": 31959,

"waitTimeBusinessHoursSeconds": 1002,
"firstResponseTimeBusinessHoursSeconds": 8,
"resolutionTimeBusinessHoursSeconds": 28800,

"rating": 5,
"resolutionYesNo": "Yes",
"feedback": "",
"submittedAt": "2026-05-08T15:10:30.000Z",

"customFields": {
"Outlet Name": "ZUS KLCC",
"Ticket Category 1": "Order",
"Ticket Category 2": "Refund"
}
}
],
"total": 1,
"page": 1,
"perPage": 100,
"pages": 1
}

Usage examples

Pull all conversation sessions for last week

curl -X GET "https://contact-center.filum.ai/external/conversation-sessions?startDate=2026-06-02&endDate=2026-06-08&timezone=Asia/Kuala_Lumpur&perPage=100" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token"

Filter to a single agent's sessions

curl -X GET "https://contact-center.filum.ai/external/conversation-sessions?startDate=2026-06-01&endDate=2026-06-08&sessionAgentEmails=alice@yourorg.com&perPage=200" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token"

Only completed (rated) sessions, by account id

curl -X GET "https://contact-center.filum.ai/external/conversation-sessions?startDate=2026-06-01&endDate=2026-06-08&assigneeAccountIds=1799,1798&sessionEndReasons=terminal" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token"

Paginate through a large window

PAGE=1
while : ; do
RESP=$(curl -sX GET "https://contact-center.filum.ai/external/conversation-sessions?startDate=2026-04-01&endDate=2026-04-30&page=$PAGE&perPage=100" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token")
echo "$RESP"
PAGES=$(echo "$RESP" | jq -r .pages)
[ "$PAGE" -ge "$PAGES" ] && break
PAGE=$((PAGE+1))
done

Notes

Date filter semantics

startDate and endDate filter on the conversation's updatedAt, not createdAt. A long-running conversation that was created last month but had activity (new messages, status changes, etc.) inside your window is still returned. This matches what an analyst pulling "what happened this week" typically wants.

Multi-row conversations

A single conversation can appear in multiple rows when:

  • It is handed off between multiple agents — each handler gets one session row.
  • It produces multiple tickets — each ticket is paired with its session by (assignee_account_id, time window).
  • An agent's session ends before the ticket is finally resolved — conversationResolvedAt is shared across every session row of that conversation, so resolution-time calculations are anchored on the chat close (customer POV), not the per-session end.

Sessions that never carry an agent identity (e.g. an unattended route_back_to_human) are still emitted with blank session-agent fields and no matched ticket. Filter them out with WHERE session_agent_email != '' if you only want rows representing a real human handler.

Window guidance

The pipeline joins the data in memory over the date window. Days-to-weeks queries return quickly; multi-month historical pulls take longer and are best run as a one-off export instead — contact your Filum representative.

Rate limiting

The endpoint is rate-limited:

  • Per second: 5 requests
  • Per minute: 50 requests

Exceeding these limits returns 429 Too Many Requests. Implement retry with exponential backoff.

Error responses

401 Unauthorized

{ "detail": { "message": "Unauthorized" } }

Either the token is invalid, the organization-id header is missing, or the token's permission set does not include $export_conversation_session:list.

422 Validation error

{
"detail": [
{
"loc": ["query", "startDate"],
"msg": "field required",
"type": "value_error.missing"
}
]
}

429 Too many requests

{ "detail": "Rate limit exceeded" }