Skip to main content
One person often reaches an agent from more than one place — a Telegram chat, a WhatsApp number, a web session. Person linking pairs those addresses so they share one conversation: after linking, every paired channel keys the same bridge:@person:{person_id} thread, and the agent remembers the person across all of them rather than treating each address as a stranger. Linking is proven, never guessed: the person shows, from a second channel, a single-use code minted on the first. Nothing is inferred from names or numbers.

Turn on multichannel for the target

Linking is opt-in per target. Enable it on the agent or tool a route points at:
With multichannel off, the pairing commands below are ordinary text passed straight through to the target, and the get_pairing_code tool refuses — the reach is bounded so a target never links people unless its operator asked for it. You can also set a first-contact greeting that hands a newcomer a code to pair with, by referencing the {pairing_code} placeholder — it is filled with a fresh code on first contact:

Mint a code

A code is a single-use token of the shape LINK-XXXXXXXX. There are three ways to mint one for a live conversation:
  • The /link command. The person sends exactly /link to the conversation; the turn engine mints a fresh code and replies with it. (/link matches only as the whole message — /link with anything after it is ordinary text.) The reply is the route’s reply_texts.pairing_code template when it sets one ({pairing_code} the code, {expires_at} its expiry), else the built-in sentence.
  • The first-contact greeting, if it carries {pairing_code} as above.
  • The get_pairing_code tool, for a programmatic mint. It takes the channel, the route’s our_identity, and the sender address to link, and returns {code, expires_at} and nothing else — the raw code once, never stored recoverably. Composing the invite around it (a typed code, or a channel-web ?tai_pair= URL) is yours to do.
Minting again for the same conversation rotates out the previous code — the newest code wins, and the old one stops redeeming.

Redeem it from the other channel

From a different channel, the person sends the code to the same agent. It redeems when the LINK-XXXXXXXX token appears anywhere in the message — typed on its own, pasted inside a sentence, or carried by a channel-web ?tai_pair= link. If a message carries more than one code, the first wins. Redeeming merges the two addresses into one person, and from then on both channels share the one thread. The person kept is the one created first; the other is absorbed into it. Erasing the kept person later (tai conversations delete-person) also erases what the absorbed person kept before the link: the chats it had on its own channels before the link, with their declared state, and its own linked thread when it had one. A chat someone continued after the link, and a chat another person also wrote in before the link, are left until their retention period. See forgetting a person. A redeem that meets a person being erased links nothing:
  • While the person who sent /link is being erased, the redeem answers “This conversation can’t be linked right now. Please try again in a moment with a new code: send /link in your other conversation.” (the route’s reply_texts.pairing_link_unavailable when it sets one), and the code is used up.
  • While the person redeeming is being erased, the redeem answers the route’s error text (reply_texts.error, by default “Sorry, something went wrong handling your message. Please try again.”), and the code is not used up.
  • A redeem already under way when the erase of the person redeeming starts answers the link-unavailable text above, with the code used up. If that erase finishes before the link is made, the redeem answers the route’s error text, the code is used up, and sending it again answers “That pairing code is not valid. It may have expired or already been used.”
To detach the sending address from its person, the person sends exactly /unlink from that channel. It affects only the address it was sent from. While the person is being erased, /unlink answers the route’s error text (reply_texts.error, by default “Sorry, something went wrong handling your message. Please try again.”), and nothing is unlinked. An /unlink already under way when the erase starts answers “This conversation can’t be unlinked right now. Please try again in a moment.” (the route’s reply_texts.pairing_unlink_unavailable when it sets one), and nothing is unlinked.

See also