Put inTouch on your site
One tag, and your visitors can use inTouch on your page — QR linking and all. The design is entirely yours, down to individual radii. The feature set is not negotiable: an embedded chat can do everything the full one can.
Script
https://web.in-touch.cc/embed.js
The whole thing
<script src="https://web.in-touch.cc/embed.js" async></script>
<intouch-chat style="height:620px">
<script type="application/json">
{
"colorScheme": "light",
"colors": { "accent": "#b4541f", "bubbleOut": "#b4541f", "panel": "#fffdf9" },
"radius": { "bubble": 14 },
"typography": { "fontFamily": "Georgia, serif" }
}
</script>
</intouch-chat>
That is the complete integration. No account, no key, no registration — this endpoint is public and stays public. Build your theme in the theme studio and copy the snippet from there.
Whose chat is it
The visitor's. They scan the QR code with their own phone and talk to their own contacts. Your page provides the frame and the design; it never becomes a party to the conversation. There is no mode in which the site owner reads along.
Three ways to embed
| Form | Use when |
|---|---|
| Web component (above) | Default. Readable theme, live updates, events, dark-mode sync. |
Plain <iframe> | Your CMS will not let you add a <script> — Webflow blocks, WordPress, Notion, Confluence. |
| Hosted theme id | Your theme got long, or you want to reuse it across pages. |
<!-- no script of your own -->
<iframe src="https://web.in-touch.cc/embed?theme=%7B%22colors%22%3A%7B%22accent%22%3A%22%23b4541f%22%7D%7D"
style="width:100%;height:620px;border:0"
allow="camera; microphone; clipboard-write"></iframe>
<!-- hosted theme -->
<intouch-chat theme="t_9fb2c1a4e803"></intouch-chat>
Hosting a theme
POST the theme and get a short id back. The id is derived from the content, so the same theme always yields the same id, an id can never be overwritten, and no account is involved.
curl -X POST https://web.in-touch.cc/embed/theme \
-H 'Content-Type: application/json' \
-d '{"colors":{"accent":"#b4541f"}}'
{ "id": "t_1465f38d3f1d", "reused": false, "bytes": 30,
"url": "https://web.in-touch.cc/embed/theme/t_1465f38d3f1d",
"snippet": "<intouch-chat theme=\"t_1465f38d3f1d\"></intouch-chat>" }
Limits: 32 KB per theme, 400 fields, 60 new themes per hour and IP. Changing a theme produces a new id — the old one keeps working, which is what you want once it is pasted into somebody else's page.
What you can change, and what you cannot
You can change every visual property: 159 documented fields,
plus customCss for anything not covered. See the
theme reference.
You cannot change behaviour. There is no field that removes voice messages, polls or groups, and there never will be. A visitor who links their account through your page gets the same messenger as everyone else — that is the deal that makes it safe to link an account in an unfamiliar frame.
Layout options move things, they do not delete them. Hiding the icon rail
(layout.rail: "hidden") moves theme switching, language and sign-out into the
sidebar menu automatically, and the unread counter into the list header. The validator
enforces this; you do not have to think about it.
Showing a single chat
Set layout.mode to "conversation" and the frame shows one
conversation instead of list and chat side by side — the shape you want in a sidebar or a
support corner.
<intouch-chat style="height:560px">
<script type="application/json">
{ "layout": { "mode": "conversation" } }
</script>
</intouch-chat>
Which chat? By default the one the visitor had open last, otherwise their most recent. The chat list is not gone: a button in the header slides it over. That is not a nicety, it is the rule — an embedded chat never cuts the visitor off from their own conversations.
Pointing at one person
You can name the counterpart. peer takes a phone number in international
form; the frame opens that conversation.
<intouch-chat peer="+4915112345678" style="height:560px">
<script type="application/json">
{ "layout": { "mode": "conversation" } }
</script>
</intouch-chat>
If the visitor already has that chat, it opens. If not, nothing is created: they see the header, an empty history and a line naming who they are about to write to. The chat comes into existence when they send — never because a page loaded. Otherwise any embedded page could quietly plant conversations in a visitor's account.
Resolving a number requires a linked session and is rate limited, because an open
“is this number on inTouch?” lookup would be a directory oracle. Your page is not told
the outcome: whether the person exists, whether a chat exists and what gets written all
stay inside the frame. On failure you receive intouch:error with
peer_not_found or peer_rate_limited, and the visitor gets their
normal chat list.
Why it is a frame
The chat runs in an iframe on the inTouch origin, not as a script inside your page. That is deliberate: the visitor's session token and their private end-to-end key live in that frame's storage. If the messenger ran in your origin, your page — and any XSS on it — could read both and every message with them.
The consequence for you: your CSS cannot reach inside, which is why the theme schema is as detailed as it is. The consequence for your visitors: embedding inTouch does not make you a party to their conversations, and nobody has to trust you for that to hold.
Your page does learn three things, and only these: that somebody is linked, how many unread chats they have, and whether a chat is open. No names, no chat ids, no message content. The list is exhaustive and is documented on the embed API page.
The linking notice
While the QR code is shown, the frame displays a line naming the real domain the visitor is on. You can position and colour it; you cannot remove it. Without it, any site could present inTouch as its own and collect real account links behind a familiar-looking facade. The domain comes from the browser, not from a parameter you pass.
Storage in embedded frames — read this
Browsers partition storage per embedding site. Practical consequences:
| Browser | Behaviour | Effect |
|---|---|---|
| Chrome, Edge | Partitioned, works | Linking applies per website |
| Firefox | Partitioned (dFPI), works | Same |
| Safari | Partitioned, cleared after ~7 days idle | Occasional re-linking |
This cannot be configured away. If storage is unavailable the frame shows an explanation
and a button that calls requestStorageAccess(); granting it gives the visitor
one linking that works across every site. Your page is told through
intouch:error with code: "storage_blocked".
If someone misuses this
The embed is public on purpose, and that cuts both ways: a phishing site can put the real inTouch linking screen inside a convincing facade. Two things stand against it.
First, the linking notice always names the domain the visitor is actually on, read from the browser. It cannot be removed, and CSS that hides it costs the site its entire custom styling — the frame checks afterwards whether the notice is really visible.
Second, a site can be blocked. Report one with the URL and, if you have it, a screenshot — the reporting address is set up before this goes public and will be named here. A blocked site no longer gets a QR code: the frame shows an explanation and a way to open inTouch Web directly. Blocking is keyed to the embedding origin and read from the browser, not from a header the site controls — a site cannot escape it by suppressing its referrer.
Blocking is a last resort and never a reaction to design we dislike. Themes are not moderated.
The studio
https://web.in-touch.cc/studio builds the theme against the real interface with sample data, shows validator warnings including contrast ratios, and hands you the finished snippet. Import and export are plain JSON files.