How it works
If your website has user accounts, you can tell the Deskcord widget who your visitors are. Identified users:
- skip the "introduce yourself" form,
- show up in Discord with their name, email and profile picture,
- find their current and past conversations on every device and browser, not just the one they first chatted from.
To make sure nobody can pretend to be one of your users, your server signs each user's id with a secret only you and Deskcord know. The widget sends that signature to Deskcord, which checks it before showing any conversation.
- You get a secret by running a command in your Discord server.
- Your server computes a signature (the
userHash) for each logged-in user. - Your website passes the user and the signature to the widget.
- When the user logs out, the widget forgets them.
Not a developer?
Let an AI coding agent do it for you
This feature needs a few lines of code on your website's server and in its pages. If you don't write code, any AI coding assistant that can open your website's code can set it up for you:
- In Discord, run
/identity-verification enableand keep the secret it shows you somewhere safe (step 1). - Open your website's project in your AI coding tool and paste the prompt below. It works with any agent: it describes the whole setup, backend and frontend, without assuming a language or tool.
- The agent tells you where to put the secret. Paste it there yourself, in your hosting provider's settings or your environment file. Never paste the secret into the AI chat.
- Follow the test checklist the agent gives you (also below).
1. Enable it in Discord
In your Discord server, run:
/identity-verification enableOnly members with the Manage Server permission can use it. The bot replies with a message only you can see, containing your identity secret: a long string of letters and numbers. Running enable again shows the same secret.
/identity-verification rotatereplaces the secret. Use it if it ever leaks. The old one stops working immediately, so update your server right away./identity-verification disableturns the feature off. From then on,identify()only prefills the intro form.
2. Store the secret on your server
Store it like any other password or API key, in an environment variable called DESKCORD_IDENTITY_SECRET: in your hosting provider's settings (Vercel, Netlify, Railway, Heroku, Render...) or your server's .env file.
DESKCORD_IDENTITY_SECRET=paste-the-secret-from-discord-here/identity-verification rotate.3. Sign your users (backend)
For each logged-in user, your server computes HMAC-SHA256(secret, userId) as a hex string: the userHash. Use an id that never changes, such as your database id, as a string. Don't use the email: users can change it.
Then hand the user to your pages, in one of two ways:
- Server-rendered pages (PHP, WordPress, Rails views, Django templates...): print the user in the embed snippet.
- Single-page apps (React, Vue, Angular...): add an endpoint, for example
GET /api/deskcord-identity, that returns the logged-in user and their hash. Always use the user from the session, never a user id sent by the browser.
Pick your language:
Works with Express, Fastify, NestJS, Next.js and any other Node.js framework: only the way you get the logged-in user changes.
const crypto = require("node:crypto");
function deskcordUserHash(userId) {
return crypto
.createHmac("sha256", process.env.DESKCORD_IDENTITY_SECRET)
.update(String(userId))
.digest("hex");
}
module.exports = { deskcordUserHash };const { deskcordUserHash } = require("./deskcord");
app.get("/api/deskcord-identity", (req, res) => {
const user = req.user; // however your app exposes the logged-in user
if (!user) {
return res.json({ user: null });
}
res.json({
user: {
userId: String(user.id),
userHash: deskcordUserHash(user.id),
name: user.name,
email: user.email,
avatarUrl: user.avatarUrl, // optional, https only
},
});
});import { createHmac } from "node:crypto";
import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth"; // your own auth helper
export async function GET() {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ user: null });
}
return NextResponse.json({
user: {
userId: String(user.id),
userHash: createHmac("sha256", process.env.DESKCORD_IDENTITY_SECRET!)
.update(String(user.id))
.digest("hex"),
name: user.name,
email: user.email,
avatarUrl: user.image ?? undefined,
},
});
}Using something else? Any language with HMAC-SHA256 works: sign the user id (as a UTF-8 string) with the secret and hex-encode the result. Upper- or lowercase hex both work.
4. Identify them (frontend)
Your site already has the Deskcord embed snippet (the one /setup gave you):
<script>
window.deskcord = {
settings: {
app_id: "YOUR_APP_ID",
},
};
</script>
<script src="https://cdn.deskcord.chat/deskcord.min.js"></script>Server-rendered pages
Add a user to the settings, printed by your server. The user is identified before the widget even shows up, so the intro form never flashes. When nobody is logged in, print user: null: that's also what logs a user out of the chat after they log out of your site.
<script>
window.deskcord = {
settings: {
app_id: "YOUR_APP_ID",
// Printed by your server for the logged-in user...
user: {
userId: "42",
userHash: "9f2c4e...", // HMAC-SHA256(secret, "42"), computed on the server
name: "Ada Lovelace",
email: "ada@example.com",
avatarUrl: "https://example.com/avatars/ada.png", // optional
},
// ...or, when nobody is logged in:
// user: null,
},
};
</script>
<script src="https://cdn.deskcord.chat/deskcord.min.js"></script>The PHP, WordPress and Django tabs above show how to print it. Encode the values as JSON so names with quotes or < can't break your page.
Single-page apps
Keep the embed snippet as it is (in your index.html or root layout). When your app starts with a logged-in user, and right after they log in, fetch your endpoint and call window.deskcord.identify().
Call identifyDeskcordUser() when your app starts if the user is logged in, and right after they log in.
// identify() and logout() exist once the widget script has loaded.
function deskcordReady() {
return new Promise((resolve) => {
(function check() {
if (window.deskcord && window.deskcord.identify) {
resolve(window.deskcord);
} else {
setTimeout(check, 100);
}
})();
});
}
async function identifyDeskcordUser() {
const res = await fetch("/api/deskcord-identity", { credentials: "include" });
const { user } = await res.json();
if (!user) {
return; // not logged in: leave the visitor anonymous
}
const deskcord = await deskcordReady();
await deskcord.identify(user);
}async function logout() {
await yourLogout(); // your existing logout
window.deskcord?.logout?.();
}Call identify() on every page load or app start: a verified chat session lasts 24 hours and is renewed each time. Calling it repeatedly with the same user is cheap.
Without identity verification
You can call identify() without userId and userHash. The widget skips the intro form and Discord shows the details, but conversations stay tied to the visitor's browser, like an anonymous visitor. No server code needed.
window.deskcord.identify({
name: "Ada Lovelace",
email: "ada@example.com",
avatarUrl: "https://example.com/avatars/ada.png", // optional
});5. Log them out
When a user logs out of your site, the widget must forget them, so the next person on a shared computer doesn't see their conversations.
- Single-page apps: call
window.deskcord.logout()in your logout handler, right after your own logout (examples above). - Server-rendered pages: print
user: nullwhen nobody is logged in. The first page after logout clears the chat session. You can also callwindow.deskcord.logout()from your logout button. - WordPress: nothing to do, the snippet prints
user: nullfor logged-out visitors.
window.deskcord.logout();logout() removes the user's chat session and conversations from that browser and brings back the intro form. Their conversations stay in Discord and come back when they log in again. Don't call it for visitors who were never logged in: it would also clear an anonymous visitor's ongoing conversation.
API reference
window.deskcord.identify(user)
Identifies the visitor. Returns a promise. Same object as window.deskcord.settings.user.
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | For verification | Your user's stable id, e.g. their database id. Never their email (it can change). |
userHash | string | For verification | HMAC-SHA256 of userId with your identity secret, hex encoded. Computed on your server. |
name | string | Yes | Shown in Discord and as the thread name. |
email | string | Yes | Must be a valid email address. Shown in Discord. |
avatarUrl | string | No | An https:// image URL, shown next to the user's messages in Discord. |
- If the visitor chatted anonymously before logging in, that conversation moves to their account. If they already had an open conversation, the anonymous one moves to their past conversations.
- Calling it with a different user logs the previous one out first.
- If the hash is rejected, the widget logs a warning in the browser console and falls back to prefilling the intro form.
- It exists once the widget script has loaded. Before that, use
settings.useror wait for it (seedeskcordReady()in the examples).
window.deskcord.logout()
Forgets the current user on this browser: chat session, conversations and unread count.
window.deskcord.settings.user
The user object, set in the embed snippet before the widget script loads, or null when nobody is logged in (ends a verified chat session).
Test your setup
- Log in to your site and open the chat: there's no "introduce yourself" form.
- Send a message. In Discord, the new thread shows your avatar and a ✅ Verified user id field with your user id.
- Log in with the same account on another browser or your phone: the conversation is there.
- Log out: the conversation disappears from the widget and the intro form comes back.
No "Verified user id" in Discord? Open your browser's developer console and see Troubleshooting.
Security
- Without a valid hash, nobody can read another user's conversations, even if they know that user's id.
- Only the user id is signed. Name, email and avatar come from your page, so Discord marks the user id as verified, not the email.
- Pages and responses containing a user hash must never be served from a shared cache (CDN, full-page cache), or one user could receive another's identity. Most caching plugins already skip logged-in users.
- The widget's chat session is stored in a cookie your page's scripts can read, like with any chat widget, so protect your site against XSS.
- Combine it with
/set-allowed-domainsso your embed only runs on your own websites.
Troubleshooting
"Identity verification failed" in the browser console
- Run
/identity-verification enableif you haven't yet. - Hash exactly the
userIdyou pass to the widget: same string, no extra spaces, the id and not the email. - Check that your server reads the right secret (no extra quotes or spaces), and update it after a
rotate.
"identify() needs at least a name and a valid email"
Both are required. Fall back to the username if your users have no display name.
window.deskcord.identify is not a function
The widget script hasn't loaded yet. Wait for it with deskcordReady(), or use settings.user.
The avatar doesn't show in Discord
avatarUrl must start with https:// and be publicly reachable. Other values are ignored.
The previous user still shows up after logging out
Call window.deskcord.logout() on logout, or print user: null for logged-out visitors.
Prompt for AI coding agents
Paste this into any AI coding assistant that has access to your website's code. It covers the backend and the frontend, works whatever your language or framework, and asks the agent to explain what it changed and how to test it.
I use Deskcord (https://deskcord.chat), a live chat widget, on my website. Set up its "identity verification" feature in this codebase, backend and frontend, so that my logged-in users are identified in the chat widget and find their conversations on any device.
## How the feature works
- The widget is loaded by an embed snippet that should already be on my site: a <script> that sets `window.deskcord = { settings: { app_id: "..." } }`, followed by `<script src="https://cdn.deskcord.chat/deskcord.min.js"></script>`. Find it in the codebase and keep its app_id. If you can't find it, ask me for it.
- For each logged-in user, the backend computes `userHash = HMAC-SHA256(secret, userId)`, hex encoded. `secret` is read from the environment variable `DESKCORD_IDENTITY_SECRET`. `userId` is the user's stable id as a string (database primary key), never their email.
- The widget receives `{ userId, userHash, name, email, avatarUrl }`. `name` and a valid `email` are required. `avatarUrl` is optional and must be an https URL. There are two ways to pass it:
- server-rendered pages: as `window.deskcord.settings.user` in the embed snippet, set to `null` when nobody is logged in;
- single-page apps: by calling `window.deskcord.identify({ ... })`, which returns a promise. `window.deskcord.identify` and `window.deskcord.logout` only exist once the widget script has loaded.
- When the user logs out, the widget must forget them: call `window.deskcord.logout()`. On server-rendered pages, rendering `user: null` on the next page also does it.
## What to do
1. Inspect the codebase and tell me what you found: the backend language and framework, how the current logged-in user is obtained, whether pages are server-rendered or a single-page app (or both), where the Deskcord embed snippet is, and where login and logout happen.
2. Backend: add a small helper that computes the hash with the secret from `DESKCORD_IDENTITY_SECRET`. Never hardcode the secret, never send it to the browser, never log it. Add `DESKCORD_IDENTITY_SECRET` with an empty value to the environment example/config files the project already uses.
3. Server-rendered pages: render the embed snippet with `user` set to the logged-in user's object (including the hash), or `null` when nobody is logged in. Encode the values safely for an inline <script> (escape <, >, & and quotes).
4. Single-page app: add an endpoint `GET /api/deskcord-identity` that uses the existing session and returns `{ "user": { "userId", "userHash", "name", "email", "avatarUrl" } }`, or `{ "user": null }` when not logged in. Always use the session's user, never a user id sent by the client. On the frontend, when the app starts with a logged-in user and right after login, fetch it and call `window.deskcord.identify(user)`, waiting for `window.deskcord.identify` to exist first. Don't call `logout()` for visitors who were never logged in.
5. Logout: wherever the user logs out, call `window.deskcord.logout()` (guard against it being undefined).
6. Make sure pages and responses that contain a user hash are never stored in a shared cache (CDN, full-page cache).
7. Leave the rest of the embed snippet unchanged, follow the project's existing conventions, and don't add dependencies unless they're really needed.
## When you're done
- List the files you changed.
- Tell me exactly where to set `DESKCORD_IDENTITY_SECRET` (which file, or which setting in my hosting provider's dashboard) for local development and for production. Remind me to get its value by running `/identity-verification enable` in my Discord server. Don't ask me to paste the secret into this chat.
- Give me a short test checklist: log in and open the chat (no "introduce yourself" form), send a message and check that the Discord thread shows my avatar and a "Verified user id" field, open the same account on another device or browser and see the conversation, then log out and check that the conversation is gone from the widget.Stuck somewhere?
Open the chat on this page, we'll help you set it up.
Back to Help & Documentation