Skip to content

Embed chat widget

Show your Wabot inbox inside your own CRM, helpdesk or admin panel. Users who are signed in to your app open the chat automatically, with no second login, and only see your account.

How it works: you create an embed secret and keep it on your server. Your server uses it to sign a short-lived token. Your page loads the chat in an iframe and gives it the token.

This step needs an admin.

  1. In Wabot, open Chat, click the ⋮ menu and choose Embed chat in your app.
  2. Under Allowed sites, add each origin that will show the chat, with no path, for example https://crm.yourcompany.com. Add http://localhost:3000 while you test. You can add up to 20.
  3. Click Generate secret. The secret starts with wbe_ and is shown only once. Store it on your server as WABOT_EMBED_SECRET.

Create an HS256 JWT, signed with your embed secret:

Claim Required Description
account_id Yes Your Wabot account ID
exp Yes Expiry in Unix seconds. At most 24 hours ahead; we recommend 1 hour
user.email, user.name No If the email matches a Wabot user in your account, replies are sent as that user. Otherwise they’re sent as the account owner
channel_id No Open a specific channel. If you leave it out, the default channel is used
// npm i jsonwebtoken
const jwt = require('jsonwebtoken');
app.get('/wabot-embed-token', requireLogin, (req, res) => {
const token = jwt.sign(
{
account_id: YOUR_ACCOUNT_ID,
user: { email: req.user.email, name: req.user.name }, // optional
// channel_id: 123, // optional
},
process.env.WABOT_EMBED_SECRET,
{ algorithm: 'HS256', expiresIn: '1h' }
);
res.json({ token });
});
<iframe id="wabot-chat" src="https://app.wabot.shop/embed-chat"
style="width:100%;min-height:640px;border:0"
allow="clipboard-write; autoplay; encrypted-media"></iframe>
<script>
// Hand the chat a fresh token (keeps it out of the URL), and again when asked.
const frame = document.getElementById("wabot-chat");
async function sendToken() {
const { token } = await fetch("/wabot-embed-token").then(r => r.json());
frame.contentWindow.postMessage({ type: "wabot:embed-token", token }, "*");
}
window.addEventListener("message", (e) => {
if (e.source !== frame.contentWindow) return;
if (["wabot:embed-waiting", "wabot:embed-token-expiring"].includes(e.data?.type)) sendToken();
if (e.data?.type === "wabot:embed-error") console.warn("Wabot chat:", e.data.message);
});
</script>

Give the iframe a height of at least 600 px. The width adapts automatically.

Message Direction Meaning
wabot:embed-token Your page → chat {type, token}. Sends a token, or a fresh one to extend the session
wabot:embed-waiting Chat → your page The chat loaded without a token and waits up to 4 seconds for one
wabot:embed-ready Chat → your page The session is ready. Includes {account_id}
wabot:embed-token-expiring Chat → your page About 2 minutes before the session ends. Send a new token
wabot:embed-error Chat → your page Something went wrong. Includes {message}

Always check event.source === frame.contentWindow before you act on a message.

  • Read and reply to conversations, and send templates
  • Manage contacts, tags and notes

Settings, billing, campaigns and other admin areas aren’t available in the embed. Each session is locked to one account and lasts until the token’s exp, up to 12 hours.

  • Only sign tokens for users who are signed in to your app.
  • Keep expiries short (1 hour), and sign a new token on each page load.
  • Serve your app over HTTPS, and only list your real sites as allowed.
  • Rotate the secret if it leaks, or when people who had access leave.
Terminal window
curl -X POST https://app.wabot.shop/embed/session \
-H "Content-Type: application/json" \
-d '{"token":"<signed token>","parentOrigin":"https://crm.yourcompany.com"}'

A working token returns 200 with account_id, channel_id and expiresAt.

Message Fix
Chat embedding is not enabled for this account Generate the embed secret (step 1)
No sites are allowed to embed this chat yet Add your site under Allowed sites
This site is not allowed to embed the chat Add the exact origin: scheme, host and port
Invalid embed token Wrong secret, the algorithm isn’t HS256, or the token was changed
Embed token expired Sign a new token
Embed token must have an exp Set an expiry
Embed token lifetime is longer than 24 hours Use a shorter expiry
Embed token has no valid account_id Include your numeric account_id
Too many requests, slow down Token exchanges are limited to 30 per minute per IP. Don’t sign a token on every render