Plans and permissions:
- Add-on: AI agents, billed separately
- Create a JWT secret and require verified user data: Admin, Manager, or Team Lead role, or a custom role with Chat agent settings set to Can edit chat agent settings
Use JWT authentication to tell the chat widget who each signed-in customer is, when your help center runs on Zendesk Guide and customers sign in to it through your own product. The chat agent can then personalize its answers, keep each customer’s conversations together across visits, and pass the customer’s details to your team when it hands off.
This article walks through the full setup. For a general introduction to installing the widget, see Chat widget installation.
How it works
The chat widget identifies users with a JSON Web Token (JWT): a short, signed piece of text that contains the user’s ID and, optionally, their name, email, and other details.
- A customer signs in to your help center.
- A script in your help center theme asks your server for a token.
- Your server confirms who the customer is from its own login session, and creates a token signed with a secret that only your server and Assembled know.
- The script passes the token to the chat widget.
- Assembled checks the token’s signature with every message. If anyone changes the token, the signature no longer matches, and Assembled ignores it.
Zendesk hosts your help center pages, so the token has to come from your own server. Never create the token in the browser or add your secret to your help center theme: visitors can read and change anything that runs in their browser.
Before you begin
You need:
- Guide Professional or Guide Enterprise, so you can edit your help center theme’s code.
- Help center sign-in that goes through your own product, such as single sign-on from your product to Zendesk. This is how your server knows who is signed in.
- A developer who can add an endpoint to your product’s backend.
We recommend hosting your help center on your own domain, such as help.yourcompany.com, rather than yourcompany.zendesk.com. Zendesk calls this host mapping. When your help center and your product share a domain, browsers send your product’s login cookie with the token request. When the help center is on a zendesk.com address, many browsers block that cookie, including Safari.
Note: If customers sign in to your help center with Zendesk accounts that aren’t connected to your product, your server can’t confirm who they are, so secure authentication isn’t possible. See If your help center doesn’t use your product’s sign-in.
Step 1: Create your JWT secret
Important: The secret is shown only once. If you lose it, you have to generate a new one, and a new secret immediately stops the old one from working.
- Go to AI support > Chat agent, open the chat agent, and select the Setup tab.
- Under User data authentication, select Manage JWT Secret.
- Select Create JWT secret.
- Select Copy to clipboard, and store the secret somewhere secure, such as a password manager.
One secret covers every chat agent in your account. Share it with your developer through a password manager, not email or chat.
Step 2: Create a token endpoint on your server
Add an endpoint to your product’s backend that your help center can call, for example:
GET https://app.yourcompany.com/api/assembled-chat-token
The endpoint should:
- Check the visitor’s login session in your product.
- Return a
401status if the visitor isn’t signed in. - If the visitor is signed in, return a token signed with your JWT secret using the HS256 algorithm, in the format
{ "token": "YOUR_JWT" }.
Token contents
| Field | Type | Required | Description |
|---|---|---|---|
user_id |
string or number | Yes | The user’s unique identifier |
email |
string | No | The user’s email address. Used to identify the user when a chat is handed off to your team. |
name |
string | No | The user’s display name |
metadata |
Record<string, unknown> |
No | Any other user data, as key-value pairs, such as plan or account type |
For example, in Node.js:
const jwt = require('jsonwebtoken');
app.get('/api/assembled-chat-token', requireLogin, (req, res) => {
const token = jwt.sign(
{
user_id: req.user.id,
email: req.user.email,
name: req.user.name,
metadata: { plan: req.user.plan },
},
process.env.ASSEMBLED_CHAT_JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '12h' }
);
res.json({ token });
});
Important: Your endpoint must identify the user from its own login session. Never take a user ID or email from the request and sign it, or anyone could sign in to the chat as someone else.
Token expiration
Setting an expiration (the exp claim) is optional. Assembled checks the token with every message, so if you set one, choose a lifetime that covers a whole conversation. We recommend several hours. You can pass a new token at any time by calling window.assembled.authenticateUser() again.
Allow your help center to call the endpoint
Your help center and your backend are on different domains, so the endpoint must allow requests from your help center (CORS). Set these response headers:
Access-Control-Allow-Origin: https://help.yourcompany.com
Access-Control-Allow-Credentials: true
Use your help center’s exact address, not *. Browsers reject responses to requests that include cookies when the endpoint allows all origins.
If your help center isn’t on your own domain, your login cookie must also be set with SameSite=None and Secure for browsers to send it, and some browsers block it anyway.
Step 3: Add the chat widget and authentication script to your theme
-
Go to your Zendesk Guide admin, at
https://[your Zendesk subdomain].zendesk.com/knowledge. -
Select the Customize design icon in the navigation bar.
-
Select the theme you want to edit, usually your live theme, and select Customize.
-
Select Edit code.
-
Open
document_head.hbs, and add the following to the bottom of the file:<script src="https://cal.assembledhq.com/static/js/public-chat.js" data-company-id="YOUR_COMPANY_ID" ></script> <script> (async function () { try { const response = await fetch('https://app.yourcompany.com/api/assembled-chat-token', { credentials: 'include', }); if (!response.ok) return; const { token } = await response.json(); await window.assembled.authenticateUser(token); } catch (error) { console.warn('[Assembled] Chat sign-in skipped', error); } })(); </script> -
Replace
YOUR_COMPANY_IDand the endpoint URL with your own. To get the right company ID, and your profile ID if you have more than one chat agent, copy the first script tag from Installation on the chat agent’s Setup tab. -
Select Publish.
The first script loads the chat widget. The second asks your server for a token and passes it to the widget. If the visitor isn’t signed in, your endpoint returns 401, the script stops, and the chat works anonymously.
Note: Don’t add async or defer to the first script tag. The authentication script needs the widget to load first.
Step 4: End the chat session when users sign out
The chat widget keeps a user’s conversations in their browser. Ending the chat session at sign-out stops the next person on that browser from seeing the previous person’s conversations.
Add this script to document_head.hbs, below the scripts from step 3:
<script>
document.addEventListener('click', function (event) {
const signOutLink = event.target.closest('a[href*="/access/logout"]');
if (!signOutLink || !window.assembled) return;
event.preventDefault();
window.assembled.teardown({ clearSession: true }).finally(function () {
window.location.href = signOutLink.href;
});
});
</script>
The script ends the chat session before the sign-out continues, which takes 2 seconds at most.
Note: The sign-out link in Zendesk’s standard themes points to /access/logout. If your theme uses a different sign-out link, change the selector to match. For more detail, see Log users out or switch users.
Step 5: Test your setup
Note: While you test, add data-debug="true" to the chat widget’s script tag to see [Assembled] messages in the browser console. Remove it before you go live.
| Test | Expected result |
|---|---|
| Sign in to your help center and open the chat | In your browser’s developer tools, on the Network tab, the request to your token endpoint returns 200. The chat opens normally. |
| Send a message, then open the conversation in Assembled | The conversation shows the user’s details. |
| Visit your help center while signed out | The token request returns 401, and the chat works anonymously. |
| Sign out, then open the chat again | The previous conversation no longer appears. |
| Sign in as a different user | None of the first user’s conversations appear. |
If a test fails, see Signed-in users aren’t identified correctly.
Step 6: Map user data to smart fields
To use user details in the chat agent’s answers and workflows, map them to smart fields:
- Go to Configure > Smart fields, and create smart fields for the user data you want to use, if they don’t exist yet.
- Go to AI support > Chat agent, open the chat agent, and select the Setup tab.
- Under User data mapping, enter each attribute’s JSON path and choose the Smart field to fill.
- Select Save changes.
Step 7: Require verified user data
Once your tests pass, make the chat accept only user details that come from a signed token:
- Go to AI support > Chat agent, open the chat agent, and select the Setup tab.
- Under User data authentication, select Only accept user data that has been securely verified through a signed token.
- Select Save changes.
With this on, the chat ignores user details passed any other way, such as with setUserData(). Visitors who aren’t signed in can still use the chat anonymously.
If your help center doesn’t use your product’s sign-in
If customers sign in to your help center with Zendesk accounts that aren’t connected to your product, you can still pass user details with setUserData(). These details aren’t verified, so visitors could change them.
await window.assembled.setUserData({
user_id: 'usr_1234',
name: 'Jane Doe',
email: 'jane@example.com',
});
If you use this approach, leave the setting from step 7 off. Otherwise, these details are ignored.