Anonymous
With no identity set, the widget stores a random visitor id inlocalStorage
so a reload resumes the same conversation in the same browser. Nothing about the
user is asserted to the server.
Soft (advisory) identity
Pass auserId to key reload continuity, so the same browser resumes the same
conversation:
Verified identity (HMAC)
Verified identity proves to Karta that your server vouches for thisuserId, so the agent can safely act on per-user data. You sign the userId
with a per-key secret; the widget forwards the signature; Karta verifies it and
binds the verified subject into the short-lived session token.
The scheme
For normal verified identity, compute the token on your server (never in the browser - the secret must not ship to a client):- The secret is the
identity_verification_secretyou generate on the agent’s Embed tab. It is server-only and never shown in a browser. - The message is the exact
userIdstring you also pass to the widget. - The output is lowercase hexadecimal (a SHA-256 HMAC, 64 hex chars).
Server-side signing
Wiring it into the widget
Pass theuserId and identityToken together - on the tag:
user_id + identity_token to the embed token mint
(POST /v1/embed/session-tokens). Karta recomputes the HMAC with the
constant-time compare and, on a match, mints a session token whose sub is
bound to that userId. A bad signature is rejected; a userId with no token
stays soft (never bound).
The
identityToken is not an auth source on its own - the embed key (or
session token) is the credential. The token only upgrades a soft userId
to verified. Its validation is entirely server-side.Telling the agent about your user
A verified identity says who the user is. Acontext bag says what the agent
should know about them, and Karta injects it into the agent’s instructions
every turn.
userinfo is the one reserved key: its name and email also drive the
widget’s “Signed in as” line. Everything else is yours.
A context-bearing token must expire. A plain identity token has no
timestamp and stays valid indefinitely, which is fine for “this is user_123” -
a fact you re-assert freely. It is not fine for a token that also asserts
facts: a captured one would keep vouching for a cancelled plan or a departed
employee. So iat, exp and jti are required whenever context is
present, and a token carrying context without them is rejected.
Keeping a long conversation alive
Because the token now expires, the widget needs a way to get a new one. Give itidentityTokenFn and it will ask for a fresh proof on every mint:
exp comfortably longer than your session-token
lifetime (default 15 minutes) either way, and under the one-hour ceiling: a
context-bearing proof whose exp - iat exceeds 3600 seconds is rejected.
If your endpoint is briefly down, the widget falls back to the token you passed
at init rather than failing: losing the context is recoverable, losing the
session is not.
Put in
context only what the agent may see and what you accept being
stored with the conversation. Prefer facts your server derived over free text
your user typed: the agent reads this like any other input, so treat it as
data you are showing the model, not as instructions it will obey.Turning it on for an agent
Injection is off by default - it costs tokens on every turn, so it is the agent’s choice. Opt in fromkarta.toml:
Context needs a session to attach to, so it works on the Managed Agents
and Responses transports. The Chat Completions adapter is stateless -
it runs each call on a fresh throwaway session - so context is not supported
there. It would apply to your first message and silently vanish from the
second, which is worse than not offering it.
Host-attested step-up
If your app has completed a stronger verification step for the user, your server can mint a structured identity token that carries that fact with the same identity signature. Karta uses this for sensitive approval flows: the browser cannot addstepped_up_at or aal next to a regular HMAC and have it
trusted.
The structured token format is:
user_id passed to the widget and may
include:
The HMAC signs the encoded payload segment:
identify again with the
new token:
identityToken invalidates the widget’s cached session token, so the
next widget request is minted with the fresh signed claims. See the
authenticated agent recipe for the full
flow.
Backend-minted tokens (alternative)
If you already mint session tokens server-side with akt_live_... API key, skip
the embed key entirely and hand the widget a token function or endpoint. Your
backend holds the secret key; the browser only ever sees the short-lived token.
POST https://karta.sh/api/agents/:slug/session_tokens with your
kt_live_... key and returns the minted token. Because Karta authenticated that
server key, the subject you put on the token is authoritative without an HMAC.
See Session tokens.
The headless client also accepts a refreshable tokenFn (re-called with
{ force: true } after a 401) - see Headless & React.
Next
Theming & config
Generate and rotate the identity secret on the Embed tab.
Security & privacy
The credential boundaries and the data path.
Authenticated agent
A worked server-side recipe for signed-in users and step-up.

