Customer Chat Integration
This section covers everything specific to building a chat integration app for customers, an app that bridges an external messaging platform (e.g. Telegram) with the CRM, routing two-way conversations between platform users and CRM agents.
This guide builds on the generic third-party app foundation. Before diving in, make sure you have read the Third-Party App Foundation docs (app setup, OAuth, webhook security, and distribution).
Key Entities
Channel
A Channel in the CRM represents a communication inbox (e.g. "Telegram Support TH", "Telegram Sales"). Every conversation belongs to exactly one channel.
Your third-party app currently cannot create channels via the API. The agent must create them through the CRM Dashboard UI. Your app then discovers them by calling GET /v1/channels and presents a channel picker to the admin during setup.
A single installation of your third-party app can link multiple platform accounts to the same CRM workspace. Each platform account is mapped to a channel, multiple accounts can share the same channel if the agent configures them that way.
Warning: Do not link multiple platform accounts from the same platform to the same CRM channel.
The CRM enforces a one active conversation per contact per channel rule: a contact can only have one non-finalized conversation open on a given channel at a time. Contact identity is derived from the platform user ID scoped to the workspace, so the same person (e.g. the same Telegram user ID) across two different Telegram bots resolves to the same CRM contact. If both Telegram bots are linked to the same CRM channel, that contact's second message will attempt to open a second conversation on the same channel and will be rejected with
409 Conflict. The integration cannot route both message streams simultaneously.If you need to manage multiple accounts from the same platform, map each one to a different CRM channel.
Channel IDs are workspace-scoped, a channel ID from one workspace has no meaning in another.
Contact
A Contact is a person in the CRM - typically a customer. Contacts have a name, phone number, email, and optional custom attributes.
For chat integrations, each external platform user (e.g. a Telegram user ID) must be mapped to a CRM contact. Your third-party app is responsible for maintaining this mapping in its own database.
Agent
An Agent is a human support staff member who belongs to the CRM workspace. Agents handle conversations assigned to them, send replies, and resolve or finalize conversations. In message payloads, an agent appears as sender.type = AGENT.
Bot
A Bot is an automated chatbot configured within the CRM workspace. Bots can send messages in conversations, for example, to greet a contact or answer common questions, without human intervention. In message payloads, a bot appears as sender.type = BOT. Your third-party app should deliver bot messages to the platform user the same way it delivers agent messages.
Conversation
A Conversation is a thread of messages between a contact and the workspace's agents on a specific channel. Conversations have a status:
| Status | Meaning |
|---|---|
OPEN | Active - agents can send and receive messages |
RESOLVED | Closed by an agent, but can be reopened |
FINALIZED | Permanently closed - no more messages can be added. Attempting to send a message will return a 4xx error (as documented in the OpenAPI spec). A new conversation must be created if the contact messages again |
Message
A Message is a single item in a conversation. Messages have a sender (who sent it) and a message_type.
These are the message types supported for the third-party app:
| message type | Meaning |
|---|---|
MESSAGE | Plain text |
ATTACHMENT | A file, image, video, or audio |
The sender type field tells you who sent the message:
| sender type | Meaning |
|---|---|
AGENT | A CRM agent |
BOT | Chatbot from the CRM system |
CONTACT | The customer |
SYSTEM | Automated system message (from automation rule) |
Entity Relationships
Chat integration adds two more entities on top of the general App → Installation → Workspace foundation: Platform Account and Conversation. A Platform Account (one per Telegram bot) is linked to a CRM Channel and is the origin of conversations, each conversation tracks which Platform Account it came from.
For the base layer, how App, Installation, Access Token, and Workspace relate, see Third-Party App Foundation - Entity Relationships.
| Relationship | Cardinality | Notes |
|---|---|---|
| Platform Account → CRM Channel | Many-to-one | Multiple platform accounts can funnel into the same channel, but only if they are from different platforms. Linking two accounts from the same platform (e.g. two Telegram bots) to the same channel causes 409 Conflict when the same contact messages both: see the warning above |
| Platform Account → Conversation | One-to-many | Each conversation records which platform account it originated from, stored as platform_channel_id in your conversation mapping, needed to look up the correct access token for outbound messages |
| CRM Channel → Conversation | One-to-many | Every conversation belongs to exactly one channel |
Architecture
Your third-party app backend sits between the messaging platform and the CRM.
Inbound (user → CRM): The platform pushes a webhook when a user sends a message. Your backend looks up (or creates) the corresponding CRM contact and conversation, then POSTs the message to the CRM API. The agent sees it in real time.
Outbound (agent → user): When an agent replies, the CRM fires a message.created webhook event to your backend. Your backend looks up the platform user ID and the originating platform account (both from the conversation mapping), then calls that platform account's push API to deliver the reply to the user.
Your database holds everything that bridges the two systems, the CRM does not store any of this on your behalf:
| What to store | Details |
|---|---|
| CRM access token | Per workspace. Used to call the CRM API on behalf of that workspace. |
| Platform account credentials | Per platform account (bot / OA / phone number): the CRM channel ID it is linked to, the platform app/channel ID, webhook secret (for signature verification), and access token (for outbound push messages). |
| Contact mappings | platform_user_id → crm_contact_id, scoped to workspace. Used to look up (or create) the CRM contact on inbound messages, and to identify the platform user on outbound events. |
| Conversation mappings | crm_conversation_id → platform_user_id + platform_channel_id + crm_channel_id. platform_channel_id is the specific platform account (e.g. a Telegram bot) the conversation originated from, required to look up the correct platform_access_token when forwarding outbound messages. |
| Pending conversation records | contact_id + platform_user_id + channel_id + platform_channel_id + workspace_id, written immediately after POST /v1/conversations. Because the conversation_id is not returned in the response, this record is what the conversation.created webhook handler uses to look up the originating request, verify the contact_id match, and complete the conversation mapping. Delete the record once the mapping is saved. |
| Message queue | Inbound messages that arrive after POST /v1/conversations but before conversation.created fires. These cannot be forwarded yet because there is no conversation_id. Queue them against the pending record (keyed by contact_id + channel_id + workspace_id) and forward them in order once the webhook is received and the mapping is complete. |
| Idempotency records | Processed platform message IDs to prevent duplicate handling when the platform retries delivery. Only recent records need to be kept, platform retry windows are typically hours, not days. |
Typically, your backend will receive HTTP requests from two different sources:
| Source | What It Is | How to Verify |
|---|---|---|
| Messaging platform (e.g. Telegram) | Platform webhook - user sent a message | Platform's own security mechanism - refer to your messaging platform's security documentation |
| CRM | Webhook event - agent replied, conversation status changed, etc. | X-Data-Signature header using your app's CRM Signing Secret |
It is recommended to verify both webhooks before acting on the payload. See Webhook Handling.
Documents in This Section
| # | Document | What It Covers |
|---|---|---|
| 1 | Incoming Messages | Receiving platform webhooks, creating contacts & conversations, idempotency |
| 2 | Outgoing Messages | Receiving CRM webhook events, forwarding agent replies to the platform, delivery status, all event payloads |
| 3 | File and Media Messages | Handling image, video, audio, and file attachments in both directions; limits and allowed types |
| 4 | Contact Mapping | Mapping platform user IDs to CRM contacts, enrichment, custom attributes |
| 5 | Platform Account Setup | Admin setup flow, platform account data model, multi-account management, and channel validation |
| 6 | Error Handling | Common failure scenarios and recommended strategies |
| 7 | CRM API Reference | All CRM REST endpoints your third-party app will call: full OpenAPI specifications available in the API Requests guide |
Quick-Start Checklist
Before you write a single line of code, complete these steps:
On the CRM Dashboard (see App Setup in the Dashboard):
- Create your app and copy all four credentials (
App ID,Client ID,Client Secret,Signing Secret) to your backend environment variables. - Add your OAuth callback URL to Redirect URLs.
- Select the minimum required scopes:
chat:read,chat:write,contact:read,contact:write,channel:read. - Enable webhook events and set your Request URL.
- Subscribe to webhook events:
message.created,conversation.created,conversation.state-changed.
On the messaging platform side:
- Create a developer app / Official Account on the platform.
- Obtain credentials: App ID, App Secret, Channel Access Token.
- Configure the platform's Webhook URL to point to your third-party app backend.
On your backend:
- Expose public HTTPS endpoints for platform webhooks, CRM OAuth callback, and CRM webhook events.
- Implement signature verification for both sources (see Webhook Handling).
- Implement the OAuth callback and token exchange (see OAuth Flow).
- Implement the incoming message handler (see Incoming Messages).
- Implement the outgoing message / webhook event handler (see Outgoing Messages).
Message Sent Webhook
Triggered when a message is successfully delivered. This webhook is only fired for messages sent via the API, and you can match it to the request by comparing the request_id value with the one returned in the API response.
Incoming Messages
What happens when a user on the external platform sends a message, and how your webhook handler creates or continues a conversation in the CRM.