Identity Verification

Prove that a signed-in visitor really is the user they claim to be — sign user IDs on your server, pass the hash to Yaplet.identify(), and rotate the secret when you need to.

Why identify() Needs a Signature

The widget has to work on pages where nobody is signed in, so any browser can obtain a visitor session for a public widget. That makes an unsigned user ID a claim rather than a fact: without a signature, anyone could call Yaplet.identify("user-4821") from their own browser, land in your inbox looking exactly like that customer, and be handed their details by an agent or by the AI.

Identity verification closes that hole. Your server signs the user ID with a secret only you and Yaplet hold, and Yaplet checks the signature before it writes anything onto the visitor.

Yaplet.identify() is rejected outright without a valid hash. Nothing is saved — not even a partial identity — because an agent looking at the inbox cannot tell a verified visitor from an unverified one. If you ship identify() without this, every signed-in user stays anonymous.

Where to Find the Identity Secret

In your Yaplet dashboard, go to Brand → Chat widget, open the Install tab, and scroll past the embed snippet to Identity verification. Press Show identity secret to load it — it is only fetched from the server when you ask for it, and even then it arrives partly hidden. The eye icon beside the value reveals the whole secret, and the clipboard icon next to it copies it.

Each chat widget has its own secret, and you need the Brands permission to see it.

Keep the secret on your server — an environment variable is the usual place. Never put it in front-end code, a mobile app bundle, or anything else a visitor can read: whoever holds it can impersonate any of your users.

Set It Up in Two Steps

Sign the user ID on your server

Compute an HMAC-SHA256 of the user's ID, keyed with the identity secret, and return the hex digest to your front end together with the user's profile.

import crypto from "crypto";

const userHash = crypto.createHmac("sha256", process.env.YAPLET_IDENTITY_SECRET).update(String(user.id)).digest("hex");

Pass the hash to identify()

Give the hash to Yaplet.identify() as its third argument, alongside the user ID and the profile:

JavaScript
Yaplet.identify(
    user.id,
    {
        name: user.name,
        email: user.email,
    },
    userHash,
);
Sign exactly the same string you pass as the first argument — if your backend signs a number and your front end sends a string with different formatting, the hashes will not match. The hash itself is compared case-insensitively and surrounding whitespace is ignored.

What Happens When Verification Fails

A rejected call is visible, but only to a developer looking in the right place:

  • The request is refused and nothing is written to the visitor record.
  • The SDK prints an error to the browser console, starting with [Yaplet] identify() was rejected, followed by the reason.
  • The affected people appear in your inbox as anonymous visitors.
  • There is no warning in the dashboard. The browser console is the only signal you get, so check it while integrating.

Two other refusals are worth recognising. The widget named in your visitor token has to belong to the same organisation as the visitor record — if it does not, the call is refused with "Visitor does not belong to this widget", which is what stops a token minted against one organisation's widget from writing an identity onto another organisation's visitor. And if we cannot look up the visitor or the widget at all, you get "Could not verify identity right now" instead; that is a temporary fault on our side, not a broken integration.

The rejection message currently names an old menu path. It tells you to look under "Widgets → your widget → Embed", a screen that no longer exists. The real location is Brand → Chat widget → Install → Identity verification.

Rotating the Secret

Once the secret is loaded, a Rotate secret button appears below it, behind a confirmation dialog.

Rotation takes effect immediately. The old secret stops verifying the moment you confirm, and every identify() call fails — with your signed-in users showing up as anonymous — until the new secret is deployed to your backend. There is no overlap period, so rotate at a quiet moment and deploy the new value straight away.

Rotate if the secret has been exposed: committed to a repository, pasted into a ticket, or shipped in front-end code.

What Else identify() Can Carry

The profile object is more than a name and an email:

  • Recognised fields are stored on the visitor: name, email, phone, plan and value. Anything else you send is kept as custom data and shown in the inbox's visitor panel.
  • Repeat calls merge. Calling identify() again enriches the stored custom data rather than replacing it.
  • Custom attributes reach your API tools as placeholders named custom_<key> — up to 50 of them, each cut to 512 characters. Nested objects and arrays are skipped, because there is no sensible way to put them in a URL or a header.
  • Values are truncated, not rejected, if they are unreasonably long, so a legitimate call never fails on length alone.
Not installed yet? The Installation guide covers the embed snippet that has to be on the page before identify() can do anything.