One agent, every channel
How the web widget, Telegram and WhatsApp reach the same agent with the same memory — identity resolution, the shared control flow, and what stays channel-specific.
A customer asks something on your website, then follows up from WhatsApp two weeks later. Whether that is one relationship or two strangers is decided by how identity resolves — not by the chat UI.
The shape of it
Channels are entry points, not silos. Each one resolves to the same three things underneath:
The agent is not duplicated per channel. Retrieval, memory and follow-ups all hang off the space, so arriving through a different app changes the transport and nothing else.
Identity resolution
Each channel supplies an external identifier — a widget token's end-user id, a Telegram user id, a WhatsApp number. Resolution is by (tenant, external_user_id): a lookup, and a provision on first sight.
Two details worth knowing:
- First contact is racy, and handled. Two messages arriving at once would both find no user and both try to create one. The unique constraint decides, and the loser re-reads rather than failing — so a burst of simultaneous first messages produces one user, not an error.
- A space is resolved, or created. Every user has a default space; an explicitly requested space must belong to that user, or the request is refused. That check is what stops a crafted request from reading someone else's conversations.
Linking one person across channels is the tenant's decision, not a guess. The platform does not merge a website visitor with a WhatsApp number because they look similar — that inference is how systems leak one customer's history to another. Where you can identify the same person (a signed-in user, a link with an end-user id), pass the same identifier and the history follows them; where you can't, they stay separate.
What the adapter shares, and what it doesn't
Telegram and WhatsApp look different but do the same job. The adapter layer holds everything that should not diverge:
| Shared in the adapter | Left to each channel |
|---|---|
| Identity resolution and space lookup | Message rendering and send calls |
| Current-session read/write | Telegram inline keyboards and form prompts |
| Commands — new, list, switch, history, rename, delete, location, help | WhatsApp list and interactive replies |
| History replay and formatting | Platform-specific attachment handling |
| The slow-response hint | |
| Running a turn |
The rule is that conversation semantics live in one place. If session switching worked slightly differently on WhatsApp than on Telegram, that difference would be invisible until a customer hit it.
A practical consequence: these are private-chat integrations. In a Telegram private chat the chat id and the sender id are the same, so the send target and the identity key collapse into one value — which is why the adapter can treat "who is this" and "where do I reply" as a single concept.
Long-running answers
Some replies cannot be produced inside a turn. The agent can schedule the work and let the answer arrive later, pushed to the channel the request came from — a bubble in the web widget, a message in Telegram or WhatsApp. See Scheduled follow-ups.
What this means in practice
- One customer, one history, regardless of where they message from.
- Adding a channel does not fork the conversation logic.
- Retrieval and memory behave identically everywhere, because they are attached to the space rather than to the channel.
Setting the channels up is covered in Telegram & WhatsApp and Embed widget.