Secure Mode
Secure mode ties chat sessions to a verified user in your app. Without it, the widget uses an anonymous browser-local sessionId. With it, the widget sends a verifiable username plus a cryptographic signature (HMAC-SHA256), and the server scopes the user's sessions and history to that identity, so prior messages can reappear across visits and devices.
When to use secure mode
You know who the end user is (logged-in app, Shopify admin, your dashboard's customer).
You want chat history to persist per-user across page loads, devices, or browsers.
You want to protect sessions from being impersonated by changing the
usernameattribute.
If none of that matters, you don't need secure mode.
Keys and secrets
Every assistant has a widget API key with two parts:
Public key
yk_…
Embedded in HTML as the key attribute. Sent as X-Yedric-Key.
Secret
yks_…
Server-side only. Never should be sent the browser.
Never put your yks_… secret in HTML, client-side JavaScript, or version control. Only the HMAC output (signature) is safe to render into the page. If a secret is ever exposed, rotate it immediately via Agents → Widget → Code → Refresh key.
Get the pair from Agents → Widget → Code in the dashboard, or create/refresh via the API.
Domain restrictions
A widget key can be limited to a list of allowed domains. That check now fails closed: a request carrying no Origin or Referer header at all is rejected, where previously the check was skipped.
Browsers always send one of those headers, so ordinary embeds are unaffected. This does matter for anything calling the widget API outside a browser, such as a server-side script or an integration test, since those send no origin by default. Either send an Origin header that is on the list, or use a key with no domain restrictions for that caller. A key with an empty domain list stays unrestricted, so this only affects keys you have actually limited.
See Yedric API Keys and Agents for the full key management flow.
Signing the username
The signature is HMAC-SHA256(secret, username) encoded as lowercase hex.
The username is whatever stable identifier makes sense for your app: an email, a UUID, a Shopify shop UUID, a numeric user id, as long as the exact same string goes into the HMAC on the server and the username attribute on the widget.
Node.js (server)
PHP (server)
A real Shopify embed computes the signature from the shop UUID and passes the full config to the view:
Then the view renders:
Browser SubtleCrypto (edge functions, workers)
Useful when your "server" is a Cloudflare Worker, Deno Deploy function, or anywhere without node:crypto.
Never run this in the end-user's browser, because it needs the raw secret.
Wiring it to the widget
Render the signed bundle into HTML attributes:
Carrying a conversation across widget instances
Each <yedric-widget> is its own instance. A second widget (on another page, or in a separate iframe such as a fullscreen editor opened from a list view) loads the user's default thread on mount, not whatever conversation was active in the first instance. To carry the active conversation across, hand off its session id.
Read the active session id on the source page:
Mount the second instance with that id via the
resume-session-idattribute. Because it's an attribute, you can server-render it into the iframe URL or set it before the element connects:The second instance resumes that exact thread instead of the user's default thread. The server still validates that the thread belongs to the verified user.
Re-sync the first instance when the second one closes, so messages added in it appear:
getSessionId() and refresh() are also available on the element (document.querySelector('yedric-widget')). All three are secure-mode features: getSessionId() falls back to the anonymous session id outside secure mode, and refresh() is a no-op when there is no active secure session.
Common pitfalls
Never ship
yks_…to the browser. Only the HMAC output (signature) goes into HTML. If your secret is ever exposed, rotate it viaPOST /api/agents/:id/key/refresh.Username must match exactly. A trailing space, different capitalization, or a different field (
idvsemail) will produce a different HMAC and a401 Invalid signatureerror. Log the exact string you pass to the HMAC and to theusernameattribute. It must be byte-for-byte identical.One signature per username, not per session. You don't re-sign for every page or session; the signature is static until the username changes.
Storing the secret in version control. Prefer env vars or a secrets manager. Even in a private repo, committing
yks_…makes rotation painful and broadens the blast radius of a leak.Session prefix matters.
session-id-prefix="preview"scopes secure sessions under a different prefix than the defaultwidget. A user's "widget" history and "preview" history are separate, so don't mix them.Key/secret pair mismatch. If you rotate the assistant's key but forget to redeploy the new secret on your signing server, every request will return
401 Invalid signature. Keep them in sync.
Last updated