On this page
Identify Signed-in Visitors#
When a shopper or member is signed in to your site, your chatbot can answer questions only that person should see — “where is my order?”, “what’s on my account?” — without asking them to prove who they are again. To do that safely, your website tells Gydr who is signed in with a short-lived identity token that only your server can create.
You need this only if a connected plugin looks up personal data (for example WooCommerce order status). A chatbot that only answers general questions works without it.
Running WordPress or WooCommerce?
How the token works#
- You create an identity key in Console → Settings → Identity verification. The key has an id (the
kid, e.g.idk_7Qm2) and a secret. The secret is shown once — store it in your server’s configuration. - When a visitor is signed in, your server signs a small JSON Web Token (JWT, HS256) with that secret, naming the user and your Gydr account.
- The widget fetches the token from your site, keeps it in memory, and sends it with each chat message. It never puts it in a URL or in browser storage.
- Gydr checks the signature, the account and the expiry. A valid token proves who the visitor is; anything else is treated as a guest.
The token#
A standard JWT: a header, a payload of claims, and a signature.
{ "alg": "HS256", "typ": "JWT", "kid": "idk_7Qm2" }{
"sub": "42",
"aud": "<your Gydr account id>",
"iat": 1790000000,
"exp": 1790003600,
"email": "siti@example.com",
"email_verified": false,
"name": "Siti Rahman",
"attrs": { "crm_id": "c_91" }
}Claims#
- Claim
sub- Required
- Yes
- What to put in it
- Your user id for this person — 1 to 255 printable characters. It must never change for the same person.
- Claim
aud- Required
- Yes
- What to put in it
- Your Gydr account id, shown in Console → Settings → Identity verification.
- Claim
iat- Required
- Yes
- What to put in it
- When you signed it, in whole Unix seconds. Most JWT libraries add it for you.
- Claim
exp- Required
- Yes
- What to put in it
- When it expires, in whole Unix seconds. At most 24 hours after iat; 1 hour is recommended.
- Claim
email- Required
- No
- What to put in it
- The person's email address.
- Claim
email_verified- Required
- No
- What to put in it
- true ONLY if your site confirmed the person owns that address (for example with a confirmation link). Plugins use the email only when this is true.
- Claim
name- Required
- No
- What to put in it
- The person's display name.
- Claim
phone_number- Required
- No
- What to put in it
- A phone number, if a plugin needs it.
- Claim
attrs- Required
- No
- What to put in it
- An object of extra ids other plugins need, e.g. a CRM id. Plain values only (text, numbers, true/false).
| Claim | Required | What to put in it |
|---|---|---|
sub | Yes | Your user id for this person — 1 to 255 printable characters. It must never change for the same person. |
aud | Yes | Your Gydr account id, shown in Console → Settings → Identity verification. |
iat | Yes | When you signed it, in whole Unix seconds. Most JWT libraries add it for you. |
exp | Yes | When it expires, in whole Unix seconds. At most 24 hours after iat; 1 hour is recommended. |
email | No | The person's email address. |
email_verified | No | true ONLY if your site confirmed the person owns that address (for example with a confirmation link). Plugins use the email only when this is true. |
name | No | The person's display name. |
phone_number | No | A phone number, if a plugin needs it. |
attrs | No | An object of extra ids other plugins need, e.g. a CRM id. Plain values only (text, numbers, true/false). |
Limits Gydr enforces
- The whole token is at most 8 KB, with at most 50 claims, each at most 256 characters.
- Claims are plain values only — text, numbers, true/false — also inside
attrs. No lists or nested objects. algmust beHS256. Tokens carrying acrit,jku,jwk,x5uorx5cheader are refused.- Up to 60 seconds of clock difference is allowed.
Sign the token on your server#
Keep the account id, key id and secret in server configuration (environment variables, a secrets manager, wp-config.php).
// npm install jsonwebtoken
import jwt from 'jsonwebtoken';
export function gydrIdentityToken(user) {
return jwt.sign(
{
sub: String(user.id),
aud: process.env.GYDR_ACCOUNT_ID,
name: user.name,
email: user.email,
email_verified: user.emailVerified === true,
},
process.env.GYDR_IDENTITY_SECRET,
{
algorithm: 'HS256',
expiresIn: '1h', // sets exp; iat is added automatically
header: { kid: process.env.GYDR_IDENTITY_KID },
},
);
}Give the widget a token endpoint (recommended)#
Add one URL on your own site that returns the current visitor’s token, and point the widget at it with data-identity-endpoint. The widget calls it itself — with the visitor’s cookies, never cached — before the first message and again whenever the token is under a minute from expiring.
<script
src="https://cdn.gydr.ai/widget.js"
data-api-key="pk_live_YOUR_KEY"
data-identity-endpoint="/gydr/identity-token"
async
></script>- The endpoint must be on the same site (same origin) as the page. A path like
/gydr/identity-tokenis simplest. - Return
{ "token": null }for a guest. An error status or a network failure changes nothing — the widget keeps what it has and tries again shortly, so a blip never signs anyone out. - Never let a page cache or CDN store this response — it belongs to one person.
Or supply the token from JavaScript#
For a single-page app or a site where a plain endpoint doesn’t fit, register a function with Gydr.setIdentityTokenProvider(). Register it from the script tag’s load event so it is in place before the first message. Return the token, or null when nobody is signed in. Throw on errors instead of returning null — null means “signed out” and clears the conversation.
const script = document.createElement('script');
script.src = 'https://cdn.gydr.ai/widget.js';
script.dataset.apiKey = 'pk_live_YOUR_KEY';
script.async = true;
script.addEventListener('load', () => {
window.Gydr.setIdentityTokenProvider(async () => {
const res = await fetch('/api/gydr-token', { credentials: 'same-origin', cache: 'no-store' });
if (!res.ok) throw new Error('token unavailable'); // keeps the current state
const { token } = await res.json();
return token; // a string, or null for a guest
});
});
document.head.appendChild(script);A site with no way to refresh can pass a fixed token with Gydr.identify({ identityToken }) or the data-identity-token attribute. It stops working when it expires, so prefer the endpoint. All options are listed in the JavaScript API and Data Attributes references.
Sign-out is automatic#
You don’t need to tell the widget when someone signs out. It starts a fresh visitor and a new, empty conversation when:
- your endpoint or provider returns
null; - a token was active and now none is supplied;
- the token names a different person (a different
sub).
A conversation that began signed in continues only for that same person — if someone else signs in on the same browser, it is cleared. A signed-in person’s past chats are shown only while they are signed in, and conversations that showed an order are never listed in past chats. Gydr.reset() still clears everything on demand.
Test a token in the Console#
Console → Settings → Identity verification has a tester. Paste a token your server produced; it tells you whether Gydr accepts it and, if not, why (see Troubleshooting). Then open your site signed in, ask the chatbot something personal (“where is my order?”), and ask again signed out.
That is the whole setup
Rotate a key without signing anyone out#
An account can have two active keys at once, so you can switch without a gap:
- Create a second key in the Console.
- Deploy its
kidand secret to your server; new tokens are signed with it. - Once the old tokens have expired (1 hour if you follow the recommendation), retire the first key.
A lost secret can’t be shown again — create a new key and retire the old one. If you think a secret leaked, rotate immediately.
Troubleshooting#
Each reason the Console tester reports, and the fix:
- Tester says
bad_signature- Why
- The token was signed with a different secret, or changed after signing.
- Fix
- Use the secret for the key named in kid; check nothing trims or re-encodes the token.
- Tester says
expired- Why
- exp is in the past (beyond the 60-second allowance).
- Fix
- Sign a fresh token for each request to your endpoint; check your server clock.
- Tester says
wrong_audience- Why
- aud is not your Gydr account id.
- Fix
- Copy the account id from Settings → Identity verification.
- Tester says
unknown_key- Why
- kid is missing, mistyped, or the key was retired.
- Fix
- Put the active key's id in the header's kid.
- Tester says
too_many_claims- Why
- More than 50 claims.
- Fix
- Send only what a connected plugin needs.
- Tester says
lifetime_too_long- Why
- exp is more than 24 hours after iat.
- Fix
- Use a lifetime of 1 hour.
- Tester says
missing_subject- Why
- No sub, or it is empty or over 255 characters.
- Fix
- Set sub to the user's id as text.
- Tester says
unsupported_algorithm- Why
- alg is not HS256 (for example RS256 or none).
- Fix
- Sign with HS256.
- Tester says
invalid_claims- Why
- A claim is a list or nested object, or longer than 256 characters.
- Fix
- Use plain values; put extra ids in attrs as text.
- Tester says
too_large- Why
- The token is over 8 KB.
- Fix
- Remove claims no plugin uses.
- Tester says
not_yet_valid- Why
- iat (or nbf) is more than 60 seconds in the future.
- Fix
- Fix your server clock (use NTP).
- Tester says
forbidden_header- Why
- The header carries crit, jku, jwk, x5u or x5c.
- Fix
- Send only alg, typ and kid in the header.
- Tester says
malformed- Why
- Not three base64url parts, not JSON, or iat/exp missing or not whole seconds.
- Fix
- Use a JWT library, or copy the PHP example exactly.
| Tester says | Why | Fix |
|---|---|---|
bad_signature | The token was signed with a different secret, or changed after signing. | Use the secret for the key named in kid; check nothing trims or re-encodes the token. |
expired | exp is in the past (beyond the 60-second allowance). | Sign a fresh token for each request to your endpoint; check your server clock. |
wrong_audience | aud is not your Gydr account id. | Copy the account id from Settings → Identity verification. |
unknown_key | kid is missing, mistyped, or the key was retired. | Put the active key's id in the header's kid. |
too_many_claims | More than 50 claims. | Send only what a connected plugin needs. |
lifetime_too_long | exp is more than 24 hours after iat. | Use a lifetime of 1 hour. |
missing_subject | No sub, or it is empty or over 255 characters. | Set sub to the user's id as text. |
unsupported_algorithm | alg is not HS256 (for example RS256 or none). | Sign with HS256. |
invalid_claims | A claim is a list or nested object, or longer than 256 characters. | Use plain values; put extra ids in attrs as text. |
too_large | The token is over 8 KB. | Remove claims no plugin uses. |
not_yet_valid | iat (or nbf) is more than 60 seconds in the future. | Fix your server clock (use NTP). |
forbidden_header | The header carries crit, jku, jwk, x5u or x5c. | Send only alg, typ and kid in the header. |
malformed | Not three base64url parts, not JSON, or iat/exp missing or not whole seconds. | Use a JWT library, or copy the PHP example exactly. |
Next: Guests & data never to send · WordPress & WooCommerce setup
