How it works

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 adapterLeft to each channel
Identity resolution and space lookupMessage rendering and send calls
Current-session read/writeTelegram inline keyboards and form prompts
Commands — new, list, switch, history, rename, delete, location, helpWhatsApp list and interactive replies
History replay and formattingPlatform-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.