On this page
- Identify Signed-in Visitors
- Pick how your customers sign in
- A WordPress site
- The connection code
- An identity provider
- Sign it yourself
- How the token works
- The token
- Claims
- Sign the token on your server
- Give the widget a token endpoint (recommended)
- Or supply the token from JavaScript
- Sign-out is automatic
- Test a token in the Console
- Rotate a key without signing anyone out
- Troubleshooting
- Ready to get started?
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. Your website tells Gydr who is signed in, in a way only your site can do.
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.
Pick how your customers sign in#
In the Console, open Settings → Signed-in visitors and press Add sign-in source. It asks one question: “Where do your customers sign in?” Each answer is set up in the same panel, without leaving the page.
- Your website
- A WordPress site
- What you do
- Type your site address, install the WordPress plugin and press Connect WordPress.
- What you never touch
- Code, wp-config.php, script tags, token signing.
- What you do
- Register the provider in the Console and hand the widget the token your page already holds.
- What you never touch
- Secrets, token signing, a token endpoint.
- Your website
- Your own sign-in (a custom site)
- What you do
- Create a key, sign a short token on your server, and serve it from a same-origin endpoint.
- What you never touch
- Nothing is hidden — you own the signing code.
| Your website | What you do | What you never touch |
|---|---|---|
| A WordPress site | Type your site address, install the WordPress plugin and press Connect WordPress. | Code, wp-config.php, script tags, token signing. |
| An identity provider (Auth0, Clerk, Firebase, Supabase, Amazon Cognito, or any provider that publishes a JWKS) | Register the provider in the Console and hand the widget the token your page already holds. | Secrets, token signing, a token endpoint. |
| Your own sign-in (a custom site) | Create a key, sign a short token on your server, and serve it from a same-origin endpoint. | Nothing is hidden — you own the signing code. |
A WordPress site#
The Gydr WordPress plugin puts the chat widget on your site and tells Gydr who is signed in. There is no code to edit.
- In Console → Settings → Signed-in visitors, press Add sign-in source, choose WordPress or WooCommerce and type your site’s address.
- Download the plugin (a zip file), press Open your WordPress upload page, choose the zip, then Install and Activate.
- Press Connect WordPress. Your site asks “Connect this site to …?” — press Connect, then Approve on your dashboard. Your site and your dashboard exchange the connection directly: nothing is copied or pasted.
The plugin updates itself through the normal WordPress updater. After the first signed-in chat, the Console shows “Recognised a visitor on” with the date. A plugin such as WooCommerce Storefront only recognises customers from the sign-in sources it trusts; the WordPress site on its store address is ticked for you. For a full WooCommerce walk-through, see WordPress & WooCommerce setup.
The connection code#
If your host blocks outbound requests, or your plugin is older than 1.1.0, use the fallback: in the Console panel open Have an older plugin? Paste a connection code instead. The connection code is one line that starts with gydr1.. It carries your account id, a signing key and the public key of the widget you chose, so the plugin has everything it needs.
An identity provider#
If your customers sign in through Auth0, Clerk, Firebase, Supabase, Amazon Cognito, or any provider that signs tokens with RS256 or ES256 and publishes its public keys (a JWKS), Gydr can verify the provider’s own tokens. You keep no secret on your side.
- In Console → Settings → Signed-in visitors, add your provider with its issuer, its public keys URL (JWKS) and the audience your tokens carry. If your provider names the user in a different claim, set the optional user-id claim (for example
cognito:username). - Gydr fetches the key set live when you save, so a wrong URL is caught straight away.
- On your page, hand the widget the token the page already holds:
Gydr.setIdentityTokenProvider(async () => (await auth.getToken()) ?? null)- Prefer a token minted with a Gydr-specific audience, so your site’s own API token is not shared with Gydr.
- Claims the provider adds that are not plain values are ignored.
emailcounts only when the token saysemail_verifiedistrue. - The same limits, sign-out behaviour and tester apply as below; see supplying the token from JavaScript.
An account can have at most 4 connections in total (WordPress sites, providers and keys together).
Sign it yourself#
For a custom site with its own sign-in, your server signs the token. The rest of this page covers that path in detail; the token format, claims and limits also describe what a provider’s tokens must contain (with RS256 or ES256 instead of HS256).
How the token works#
- You create an identity key in Console → Settings → Signed-in visitors (Add sign-in source → “Your own sign-in”). 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 → Signed-in visitors.
- 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 → Signed-in visitors. |
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 beHS256for a website key (RS256 or ES256 for an identity provider). 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).
// 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 → Signed-in visitors has a token check in each source’s details. 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 → Signed-in visitors.
- 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
unknown_issuer- Why
- The token's iss is not a registered identity provider.
- Fix
- Register the provider in Settings → Signed-in visitors with exactly the issuer its tokens carry.
- Tester says
jwks_unavailable- Why
- The provider's public keys URL (JWKS) could not be fetched.
- Fix
- Check the URL opens in a browser and the provider is up, then try again.
- 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 allowed for this connection (for example none, or RS256 on a website key).
- Fix
- Sign a website key's tokens with HS256; a provider's with RS256 or ES256.
- 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 → Signed-in visitors. |
unknown_key | kid is missing, mistyped, or the key was retired. | Put the active key's id in the header's kid. |
unknown_issuer | The token's iss is not a registered identity provider. | Register the provider in Settings → Signed-in visitors with exactly the issuer its tokens carry. |
jwks_unavailable | The provider's public keys URL (JWKS) could not be fetched. | Check the URL opens in a browser and the provider is up, then try again. |
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 allowed for this connection (for example none, or RS256 on a website key). | Sign a website key's tokens with HS256; a provider's with RS256 or ES256. |
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
