Skip to main content
🤖 LLM Friendly: This page is available in raw Markdown format for LLM consumption:sms-conversations.md|Get full documentation:llms.txt/llms-full.txt

SMS Conversations

The SMS Conversations API provides call center agents with a unified two-way messaging inbox. Conversations use a Persistent Thread model — exactly one thread exists per client phone number and system DID hotline in the workspace, ensuring complete message history across agent shifts and customer touchpoints.

info

Data Scoping & Team Assignment

Access to conversation threads is strictly scoped based on the agent's role and team assignments:

  • Leader / Supervisor / Admin: Full access to view and manage all conversations across the workspace.
  • Member (Agent): Can only access conversations where the system DID is assigned to one of their teams (shared_teams_id), or threads assigned directly to them or their team. Regular members can only send new SMS from numbers assigned to their teams.

Endpoints​

MethodEndpointDescription
GET/call-center/conversationsList conversation threads (SMS Inbox)
POST/call-center/conversationsStart a new conversation thread (New Message)
GET/call-center/conversations/:idGet details of a single conversation thread
GET/call-center/conversations/:id/messagesList message history for a conversation thread
POST/call-center/conversations/:id/messagesSend an outbound SMS reply in a thread
PATCH/call-center/conversations/:id/readMark conversation messages as read
PATCH/call-center/conversations/:idUpdate conversation status or agent assignment

Authentication​

All endpoints require a Bearer JWT with agent-api audience.

Authorization: Bearer YOUR_AGENT_JWT

The Conversation Object​

FieldTypeDescription
idstringUnique conversation identifier prefixed with conv_ (e.g. conv_a1b2c3d4e5f6)
workspace_idstringWorkspace identifier
phone_number_idstringID of the workspace DID number used for the thread (pn_...)
system_numberstringE.164 phone number of the workspace DID (e.g. +13074295456)
client_numberstringE.164 phone number of the client (e.g. +18647123123)
client_namestring | nullContact display name if matched in address books or manually set
contact_idstring | nullAssociated contact ID if resolved in contacts (con_...)
assigned_agent_idstring | nullAgent ID currently assigned to handle this thread
assigned_team_idstring | nullTeam ID assigned to handle this thread
statusstringThread lifecycle status: open | closed
unread_countnumberNumber of unread inbound messages from client
last_messageobject | nullSnippet object of the latest message in the thread (see below)
last_message_atstring | nullISO 8601 timestamp of the latest message
last_read_atstring | nullISO 8601 timestamp when an agent last marked the thread as read
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last update timestamp

The last_message Snippet Object​

Embedded inside conversation.last_message:

FieldTypeDescription
idstringMessage identifier prefixed with msg_
bodystringContent text of the latest message
directionstringMessage transmission direction: inbound | outbound
sender_typestringOriginator classification: client | agent | voice_agent | system
sender_idstring | nullAgent ID who sent the message (or null if sent by client, AI agent, or automated system)
created_atstringISO 8601 creation timestamp

The Message Object​

Represents an individual SMS or MMS message within a conversation thread.

FieldTypeDescription
idstringUnique message identifier prefixed with msg_ (e.g. msg_682d82eb0ba1ffeace018f)
conversation_idstringAssociated conversation thread ID (conv_...)
workspace_idstringWorkspace identifier
phone_number_idstringID of the workspace DID phone number used (pn_...)
fromstringSender phone number in E.164 format (e.g. +13074295456)
tostringRecipient phone number in E.164 format (e.g. +18647123123)
client_numberstringClient phone number in E.164 format
bodystringText content of the SMS message
directionstringTransmission direction: inbound | outbound
statusstringDelivery / receipt status: queued | sending | sent | delivered | received | failed | undelivered
sender_typestringOriginator entity: client | agent | voice_agent | system
sender_idstring | nullID of the agent who sent the message (null for client or system messages)
media_urlsstring[]Optional array of public HTTPS URLs for MMS attachments
costnumber | nullCost incurred in workspace credits / USD for sending the SMS
error_codestring | nullTelecom carrier or internal error code if transmission failed
error_messagestring | nullExplanatory error message if transmission failed
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last update timestamp

Enumerations & Value References​

Comprehensive reference for enumerated fields used across conversations, messages, real-time events, and webhooks.

direction​

Specifies the communication direction relative to the workspace Call Center.

Enum ValueFlowDescription
inboundCustomer âž” Workspace DIDIncoming message: Sent by an external customer/client to a workspace DID hotline. Increments unread_count on the thread and triggers incoming alert notifications.
outboundWorkspace DID âž” CustomerOutgoing message: Sent from a workspace DID to a customer's phone number by an agent, AI voice agent, or automated workflow.

status​

The status attribute is used in two contexts: Conversation Status (thread resolution lifecycle) and Message Status (carrier delivery state).

Conversation Status​

Applies to conversation.status and query filters on /call-center/conversations.

Enum ValueStateDescription
openActiveThe conversation thread is active, awaiting agent response, or undergoing live interaction. Newly created conversations or threads receiving a new customer inbound SMS are automatically set to open.
closedResolved / ArchivedThe conversation thread has been marked as resolved or closed by an agent or supervisor. If the customer sends another inbound SMS to the same DID in the future, the thread will automatically reopen (open) and bump to the top of the inbox.

Message Status​

Applies to message.status and real-time carrier status delivery updates.

Enum ValueDirectionCategoryDescription
queuedOutboundPendingMessage has been accepted by the API and enqueued for carrier dispatch.
sendingOutboundIn-flightMessage is actively being transmitted to the telecom carrier network.
sentOutboundDispatchedTelecom carrier accepted the message and dispatched it into the cellular network.
deliveredOutboundSuccessCarrier received a confirmed Handset Delivery Receipt (DLR) indicating the SMS was successfully delivered to the customer's phone.
receivedInboundSuccessInbound SMS was successfully received from the carrier and appended to the conversation thread.
failedOutboundTerminal FailureMessage delivery permanently failed before or during carrier dispatch (e.g. invalid destination number, blocked prefix, or unroutable route). Check error_code and error_message.
undeliveredOutboundTerminal FailureCarrier dispatched the SMS but the destination mobile network reported delivery failure (e.g. handset powered off, out of coverage, or unreachable).
stateDiagram-v2
direction LR
[*] --> queued: Send Message
queued --> sending: Carrier Dispatch
sending --> sent: Carrier Accepted
sent --> delivered: Carrier DLR Success
sent --> undelivered: Carrier DLR Failure
sending --> failed: Carrier Rejected
queued --> failed: Invalid Route/Number

[*] --> received: Inbound Message

sender_type​

Identifies the originator or authoring entity that created the message.

Enum ValueTypical DirectionDescription
clientinboundCustomer / Client: The external mobile phone subscriber sending an SMS to the workspace DID number.
agentoutboundCall Center Agent: A human agent sending a reply from the Firetell Console inbox or Client SDK. sender_id will contain the agent's user ID.
voice_agentoutboundAI Voice Agent: An automated conversational Voice AI agent sending an SMS during or after a phone call (e.g. sending a scheduling link, OTP verification code, address, or summary).
systemoutboundSystem / Workflow Automation: Automatically generated by backend platform workflows, API triggers, automated reminders, or webhooks.

List Conversations (Inbox)​

GET /call-center/conversations

Retrieve a paginated list of conversation threads accessible to the authenticated agent, ordered by most recent message activity.

Query Parameters​

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Number of items per page (max: 100)
statusstring—Filter by status: open or closed
phone_number_idstring—Filter by specific hotline phone number ID (pn_...)
assigned_agent_idstring—Filter by assigned agent: 'me', 'unassigned', or a specific agent ID
assigned_tostring—Alias for assigned_agent_id
assigned_team_idstring—Filter by assigned team ID
unread_onlybooleanfalseWhen set to true, only returns conversation threads that contain unread incoming messages
searchstring—Search across client_number, client_name, or system_number

Request​

curl -X GET "https://{workspace_id}.firetell.app/api/v1/call-center/conversations?status=open&page=1&limit=20" \
-H "Authorization: Bearer YOUR_AGENT_JWT"

Response 200 OK​

{
"data": [
{
"id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "John Doe",
"contact_id": "con_650000000000000000000130",
"assigned_agent_id": "agent_001",
"assigned_team_id": "team_support_tier1",
"status": "open",
"unread_count": 2,
"last_message": {
"id": "msg_682d82eb0ba1ffeace018f",
"body": "Can I reschedule my appointment?",
"direction": "inbound",
"sender_type": "client",
"sender_id": null,
"created_at": "2026-09-27T10:00:00.000Z"
},
"last_message_at": "2026-09-27T10:00:00.000Z",
"last_read_at": "2026-09-27T09:30:00.000Z",
"created_at": "2026-09-25T08:00:00.000Z",
"updated_at": "2026-09-27T10:00:00.000Z"
}
],
"meta": {
"total": 1,
"page": 1,
"limit": 20,
"total_pages": 1
}
}

Start New Conversation (New Message)​

POST /call-center/conversations

Initiate a new conversation thread or send a message to a client directly from the agent UI (e.g., clicking "New Message"). If an active thread already exists between the selected DID and the client, the message is appended to the existing thread. The thread is automatically assigned to the initiating agent if currently unassigned.

note

DID Authorization Check

For agents with the member role, from must be a DID phone number assigned to at least one of their teams. If unauthorized, a 403 Forbidden error is returned.

Request Body​

FieldTypeRequiredDescription
fromstringYesWorkspace DID phone number to send from (E.164 format, e.g. +13074295456)
client_numberstringYesClient recipient phone number (E.164 format, e.g. +18647123123)
bodystringYesText body of the SMS message (up to 1,600 characters)
media_urlsstring[]NoOptional array of media URLs for MMS messages

Request​

curl -X POST "https://{workspace_id}.firetell.app/api/v1/call-center/conversations" \
-H "Authorization: Bearer YOUR_AGENT_JWT" \
-H "Content-Type: application/json" \
-d '{
"from": "+13074295456",
"client_number": "+18647123123",
"body": "Hello John, thank you for reaching out to Firetell support."
}'

Response 201 Created​

{
"conversation": {
"id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "John Doe",
"contact_id": "con_650000000000000000000130",
"assigned_agent_id": "agent_001",
"assigned_team_id": null,
"status": "open",
"unread_count": 0,
"last_message": {
"id": "msg_682d82eb0ba1ffeace018f",
"body": "Hello John, thank you for reaching out to Firetell support.",
"direction": "outbound",
"sender_type": "agent",
"sender_id": "agent_001",
"created_at": "2026-09-27T10:05:00.000Z"
},
"last_message_at": "2026-09-27T10:05:00.000Z",
"last_read_at": "2026-09-27T10:05:00.000Z",
"created_at": "2026-09-27T10:05:00.000Z",
"updated_at": "2026-09-27T10:05:00.000Z"
},
"message": {
"id": "msg_682d82eb0ba1ffeace018f",
"conversation_id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"from": "+13074295456",
"to": "+18647123123",
"client_number": "+18647123123",
"body": "Hello John, thank you for reaching out to Firetell support.",
"direction": "outbound",
"status": "queued",
"sender_type": "agent",
"sender_id": "agent_001",
"media_urls": [],
"created_at": "2026-09-27T10:05:00.000Z",
"updated_at": "2026-09-27T10:05:00.000Z"
}
}

Get Conversation Details​

GET /call-center/conversations/:id

Retrieve full details of an accessible conversation thread by its ID.

Path Parameters​

ParameterTypeDescription
idstringConversation ID prefixed with conv_

Request​

curl -X GET "https://{workspace_id}.firetell.app/api/v1/call-center/conversations/conv_a1b2c3d4e5f6" \
-H "Authorization: Bearer YOUR_AGENT_JWT"

Response 200 OK​

{
"id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "John Doe",
"contact_id": "con_650000000000000000000130",
"assigned_agent_id": "agent_001",
"assigned_team_id": "team_support_tier1",
"status": "open",
"unread_count": 2,
"last_message": {
"id": "msg_682d82eb0ba1ffeace018f",
"body": "Can I reschedule my appointment?",
"direction": "inbound",
"sender_type": "client",
"sender_id": null,
"created_at": "2026-09-27T10:00:00.000Z"
},
"last_message_at": "2026-09-27T10:00:00.000Z",
"last_read_at": "2026-09-27T09:45:00.000Z",
"created_at": "2026-09-25T08:00:00.000Z",
"updated_at": "2026-09-27T10:00:00.000Z"
}

List Messages in Conversation​

GET /call-center/conversations/:id/messages

Retrieve paginated messages for a conversation thread. Results are sorted in chronological order (oldest first to newest last) to facilitate rendering chat feeds in agent interfaces.

Query Parameters​

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber50Number of messages to return per page (max: 100)
beforestring—ISO 8601 timestamp cursor to paginate backwards in history
afterstring—ISO 8601 timestamp cursor to paginate forwards in history

Request​

curl -X GET "https://{workspace_id}.firetell.app/api/v1/call-center/conversations/conv_a1b2c3d4e5f6/messages?page=1&limit=50" \
-H "Authorization: Bearer YOUR_AGENT_JWT"

Response 200 OK​

{
"data": [
{
"id": "msg_682d82eb0ba1ffeace018e",
"conversation_id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"from": "+18647123123",
"to": "+13074295456",
"client_number": "+18647123123",
"body": "Hi, I need assistance with my service.",
"direction": "inbound",
"status": "received",
"sender_type": "client",
"sender_id": null,
"media_urls": [],
"created_at": "2026-09-27T09:55:00.000Z",
"updated_at": "2026-09-27T09:55:01.000Z"
},
{
"id": "msg_682d82eb0ba1ffeace018f",
"conversation_id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"from": "+13074295456",
"to": "+18647123123",
"client_number": "+18647123123",
"body": "Can I reschedule my appointment?",
"direction": "inbound",
"status": "received",
"sender_type": "client",
"sender_id": null,
"media_urls": [],
"created_at": "2026-09-27T10:00:00.000Z",
"updated_at": "2026-09-27T10:00:01.000Z"
}
],
"meta": {
"total": 2,
"page": 1,
"limit": 50,
"total_pages": 1
}
}

Send Message (Reply)​

POST /call-center/conversations/:id/messages

Send an outbound SMS/MMS reply to the client in an existing conversation thread. The message automatically dispatches from the thread's configured system_number to the client's client_number.

Request Body​

FieldTypeRequiredDescription
bodystringYesText body of the SMS reply (up to 1,600 characters)
media_urlsstring[]NoOptional array of public HTTPS URLs for MMS attachments

Request​

curl -X POST "https://{workspace_id}.firetell.app/api/v1/call-center/conversations/conv_a1b2c3d4e5f6/messages" \
-H "Authorization: Bearer YOUR_AGENT_JWT" \
-H "Content-Type: application/json" \
-d '{
"body": "Sure thing, John! We have slots available tomorrow at 2:00 PM or 4:00 PM."
}'

Response 201 Created​

{
"id": "msg_682d82eb0ba1ffeace0190",
"conversation_id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"from": "+13074295456",
"to": "+18647123123",
"client_number": "+18647123123",
"body": "Sure thing, John! We have slots available tomorrow at 2:00 PM or 4:00 PM.",
"direction": "outbound",
"status": "queued",
"sender_type": "agent",
"sender_id": "agent_001",
"media_urls": [],
"created_at": "2026-09-27T10:05:00.000Z",
"updated_at": "2026-09-27T10:05:00.000Z"
}

Mark Conversation as Read​

PATCH /call-center/conversations/:id/read

Mark all unread incoming messages in a conversation thread as read. This resets unread_count to 0 and updates last_read_at to the current timestamp.

Request​

curl -X PATCH "https://{workspace_id}.firetell.app/api/v1/call-center/conversations/conv_a1b2c3d4e5f6/read" \
-H "Authorization: Bearer YOUR_AGENT_JWT"

Response 200 OK​

{
"ok": true,
"conversation_id": "conv_a1b2c3d4e5f6"
}

Update Conversation​

PATCH /call-center/conversations/:id

Update conversation metadata, such as assigning the thread to a specific agent or team, or closing/re-opening the thread status.

Request Body​

FieldTypeDescription
statusstringSet thread status: open or closed
assigned_agent_idstring | nullAssign thread to an agent ID (or null to unassign)
assigned_team_idstring | nullAssign thread to a team ID (or null to unassign)
client_namestringCustom display name for the customer

Request​

curl -X PATCH "https://{workspace_id}.firetell.app/api/v1/call-center/conversations/conv_a1b2c3d4e5f6" \
-H "Authorization: Bearer YOUR_AGENT_JWT" \
-H "Content-Type: application/json" \
-d '{
"status": "closed",
"assigned_agent_id": "agent_002",
"assigned_team_id": "team_support_tier2"
}'

Response 200 OK​

{
"id": "conv_a1b2c3d4e5f6",
"workspace_id": "yourcompany",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "John Doe",
"contact_id": "con_650000000000000000000130",
"assigned_agent_id": "agent_002",
"assigned_team_id": "team_support_tier2",
"status": "closed",
"unread_count": 0,
"last_message": {
"id": "msg_682d82eb0ba1ffeace018f",
"body": "Can I reschedule my appointment?",
"direction": "inbound",
"sender_type": "client",
"sender_id": null,
"created_at": "2026-09-27T10:00:00.000Z"
},
"last_message_at": "2026-09-27T10:00:00.000Z",
"last_read_at": "2026-09-27T10:05:00.000Z",
"created_at": "2026-09-25T08:00:00.000Z",
"updated_at": "2026-09-27T10:10:00.000Z"
}

Realtime Notifications & Synchronization​

Firetell keeps agent devices and browser call center consoles synchronized across all inbound and outbound messages in real time.

1. Mobile Push Notifications (Inbound SMS)​

When a customer sends an SMS/MMS to a workspace number, Firetell delivers a high-priority alert notification to the assigned agents' mobile apps (iOS & Android) via APNs (Alert) and FCM.

Agent Targeting & Routing​

Push notifications are routed automatically based on the conversation assignment:

  1. Assigned Agent: If the conversation has an assigned_agent_id, only that agent's devices receive the push.
  2. Assigned Team: If the conversation has an assigned_team_id, all active agents belonging to that team receive the push.
  3. DID Shared Teams (Fallback): If the conversation has no assigned agent or team, all active agents in the teams mapped to the phone number (shared_teams_id) receive the push notification.
info

Outbound SMS Policy Outbound messages sent by agents do not trigger mobile push notifications. This prevents notification spam and alert fatigue across teammates while ensuring agents only get mobile alerts when a customer needs attention.

Device Token Registration​

Mobile clients must register their regular notification token with the Agent API using POST /me/devices/notification-push-token:

  • iOS: Register the standard APNs alert token (UNUserNotificationCenter).
  • Android: Register the FCM registration token (FirebaseMessaging).

Push Payload Specification​

iOS (APNs Alert Payload)​

Delivered with apns-push-type: alert and apns-priority: 10:

{
"aps": {
"alert": {
"title": "Alice Smith (+18647123123)",
"body": "Hello, I need assistance with my reservation."
},
"sound": "default",
"badge": 3,
"thread-id": "conv_a1b2c3d4e5f6"
},
"event": "message.received",
"workspace_id": "ws_123456789",
"message_id": "msg_be52701819fa34a72ae414",
"conversation_id": "conv_a1b2c3d4e5f6",
"from_number": "+18647123123",
"to_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "Alice Smith",
"body": "Hello, I need assistance with my reservation.",
"unread_count": 3,
"created_at": "2026-09-27T10:00:00.000Z"
}
Android/iOS (FCM Notification + Data Payload)​

Sent with priority high:

{
"notification": {
"title": "Alice Smith",
"body": "Hello, I need assistance with my reservation."
},
"android": {
"notification": {
"channel_id": "firetell_messages",
"tag": "conv_a1b2c3d4e5f6"
}
},
"apns": {
"payload": {
"aps": {
"thread-id": "conv_a1b2c3d4e5f6"
}
}
},
"data": {
"event": "message.received",
"workspace_id": "ws_123456789",
"message_id": "msg_be52701819fa34a72ae414",
"conversation_id": "conv_a1b2c3d4e5f6",
"from_number": "+18647123123",
"to_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "Alice Smith",
"body": "Hello, I need assistance with my reservation.",
"unread_count": "3",
"created_at": "2026-09-27T10:00:00.000Z"
}
}
tip

Conversation Grouping (Threading)

  • iOS: Uses aps.thread-id set to conversation_id. iOS natively groups incoming notifications into an expandable conversation stack in the Notification Center.
  • Android: Uses android.notification.tag set to conversation_id. If building custom notifications via FirebaseMessagingService, pass data.conversation_id into NotificationCompat.Builder.setGroup(conversationId) for native Android notification grouping.

2. Mobile Silent / Data-Only Push Notifications (conversation.updated)​

When a conversation is modified — such as an agent marking messages as read, reassigning to another agent/team, or updating thread status (open / closed) — Firetell dispatches a silent, data-only push notification (conversation.updated) to target mobile devices.

Purpose & Architecture​

Unlike incoming SMS alerts, silent pushes contain no alert banner, no sound, and no popup. They wake the mobile operating system in the background to execute code:

  • Local Storage Synchronization: Update local databases (e.g. SQLite, WatermelonDB, Core Data, Realm) with the new unread_count, status, and assigned_agent_id.
  • Badge Count Synchronization: Update or clear the application icon badge without requiring user interaction.
  • Notification Dismissal: Dismiss existing notification center banners for this conversation when the thread has been read or resolved on another device or web portal.

Agent Targeting & Routing​

Delivered automatically to:

  1. Assigned Agent: If the conversation is assigned, the assigned agent's devices receive the silent push.
  2. Assigned Team: If assigned to a team, all active agents in that team receive the push.
  3. DID Shared Teams (Fallback): If unassigned, active agents in the teams assigned to the phone number receive the push.

Push Payload Specification​

iOS (APNs Background Silent Push)​

Delivered with apns-push-type: background and apns-priority: 5:

{
"aps": {
"content-available": 1
},
"event": "conversation.updated",
"workspace_id": "ws_123456789",
"id": "conv_a1b2c3d4e5f6",
"conversation_id": "conv_a1b2c3d4e5f6",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "Alice Smith",
"assigned_agent_id": "agent_001",
"assigned_team_id": null,
"status": "open",
"unread_count": 0,
"last_message": {
"id": "msg_05f18045cba1452204774f",
"body": "Hello Alice, I have updated your reservation for 7:00 PM.",
"direction": "outbound",
"sender_type": "agent",
"sender_id": "agent_001",
"created_at": "2026-09-28T03:39:00.000Z"
},
"last_message_at": "2026-09-28T03:39:00.000Z",
"last_read_at": "2026-09-28T03:40:00.000Z",
"created_at": "2026-09-27T10:00:00.000Z",
"updated_at": "2026-09-28T03:40:00.000Z",
"timestamp": "2026-09-28T03:40:00.000Z"
}
Android / iOS (FCM Data-Only Payload)​

Sent with priority: high and no notification block:

{
"android": {
"priority": "high",
"ttl": "60s"
},
"apns": {
"headers": {
"apns-priority": "5",
"apns-push-type": "background"
},
"payload": {
"aps": {
"content-available": 1
}
}
},
"data": {
"event": "conversation.updated",
"workspace_id": "ws_123456789",
"id": "conv_a1b2c3d4e5f6",
"conversation_id": "conv_a1b2c3d4e5f6",
"phone_number_id": "pn_99bf875464f1c096f46aff",
"system_number": "+13074295456",
"client_number": "+18647123123",
"client_name": "Alice Smith",
"assigned_agent_id": "agent_001",
"assigned_team_id": "",
"status": "open",
"unread_count": "0",
"last_message": "{\"id\":\"msg_05f18045cba1452204774f\",\"body\":\"Hello Alice, I have updated your reservation for 7:00 PM.\",\"direction\":\"outbound\",\"sender_type\":\"agent\",\"sender_id\":\"agent_001\",\"created_at\":\"2026-09-28T03:39:00.000Z\"}",
"last_message_at": "2026-09-28T03:39:00.000Z",
"last_read_at": "2026-09-28T03:40:00.000Z",
"created_at": "2026-09-27T10:00:00.000Z",
"updated_at": "2026-09-28T03:40:00.000Z",
"timestamp": "2026-09-28T03:40:00.000Z"
}
}
tip

Handling Silent Pushes in Mobile Code

  • iOS APNs (AppDelegate / UNUserNotificationCenter): Handle via application(_: didReceiveRemoteNotification:fetchCompletionHandler:). Inspect payload["event"] == "conversation.updated", sync local database, update UIApplication.shared.applicationIconBadgeNumber, and call completionHandler(.newData).
  • Android/iOS FCM (FirebaseMessagingService): In onMessageReceived(remoteMessage: RemoteMessage), check remoteMessage.data["event"] == "conversation.updated". Because there is no notification block, the Android/iOS OS will not show any system notification. Update your local database (Room / SQLite / WatermelonDB) in the background.

3. Browser Realtime Synchronization (SSE)​

Browser-based Call Center Portals subscribe to the Realtime Events (SSE) stream:

GET /stream?token=YOUR_AGENT_JWT

Message events are published to the agent's scoped unicast channels

Supported Message Events​

EventDirectionDescription
message.receivedInboundEmitted when a new message is received from a customer. Updates the chat thread and increases the unread count.
message.sentOutboundEmitted when an agent sends an outbound message. Instantly updates the conversation for all teammates in the same team to prevent collision.
message.updatedStatusEmitted when delivery status updates from the carrier (e.g., transitions from queued to sent, delivered, or failed).
conversation.updatedStateEmitted when a conversation thread is updated (assigned/reassigned agent, status open/closed, or unread count reset upon reading).

Example JavaScript Listener​

const eventSource = new EventSource(
`https://{workspace_id}.firetell.app/stream?token=${encodeURIComponent(agentJwt)}`,
);

// 1. Inbound customer message
eventSource.addEventListener("message.received", (e) => {
const { data } = JSON.parse(e.data);
console.log("New inbound SMS:", data.body, "from:", data.from_number);
// Prepend or append to conversation chat view, increment unread badge
updateConversationInbox(data.conversation_id, data);
});

// 2. Outbound message from colleague (Collision Avoidance)
eventSource.addEventListener("message.sent", (e) => {
const { data } = JSON.parse(e.data);
console.log("Teammate sent SMS:", data.body, "by agent:", data.sender_id);
// Reflect teammate's reply immediately without reloading
appendOutboundMessageToThread(data.conversation_id, data);
});

// 3. Delivery status update (Carrier acknowledgment)
eventSource.addEventListener("message.updated", (e) => {
const { data } = JSON.parse(e.data);
console.log("Message status updated:", data.id, "to:", data.status);
// Update tick icon to delivered / failed
updateMessageStatusIndicator(data.id, data.status, data.error_message);
});

// 4. Conversation state updated (Assignment / Status change / Read receipt)
eventSource.addEventListener("conversation.updated", (e) => {
const { data } = JSON.parse(e.data);
console.log(
"Conversation updated:",
data.id,
"assigned to:",
data.assigned_agent_id,
"status:",
data.status,
);
// Update conversation inbox thread item in real time
syncConversationState(data.id, data);
});