End users and identity
An end user (eu_…) is the person talking to your agent — your customer. End users belong to a project and carry your own external_id, an optional name and free-form traits (plan, city, language, account tier…). Sessions, the agent’s action memory and per-user limits all hang off the end user.
Three identity tiers
Section titled “Three identity tiers”| Tier | How it happens | Verified | Typical use |
|---|---|---|---|
| Anonymous | A publishable key plus a device token, from the widget | No | Visitors on your public website |
| Server-asserted | A secret-key call that includes end_user |
Yes | Your backend talking to K-Agent |
| Client token | A ct_… token minted by your server for one end user and one agent |
Yes, for up to 60 minutes | Signed-in users in your web or mobile app |
Verification is a property of the credential, not of the end user. A request is verified when your server vouched for it — with a secret key or with a client token it minted. The same person chatting anonymously in the widget is not verified in that conversation.
What verification unlocks
Section titled “What verification unlocks”- Identity-bound HTTP tools. A tool with
requires_verified_user, or one that uses{{end_user.*}}placeholders, only runs for verified users. The model never supplies identity: K-Agent fills it from the credential. See HTTP tools. - Actions for known people. Tools with side effects are removed for anonymous users unless the agent sets
tools.allow_anonymous_actions. - Memory across sessions. The action ledger (“already done, don’t repeat”) and per-user rate limits follow a verified end user across all their sessions. For anonymous users they are scoped to the session.
Tell K-Agent who the user is
Section titled “Tell K-Agent who the user is”From your server, pass end_user on ask or POST /v1/sessions:
{ "agent": "store-assistant", "external_id": "order-8812", "end_user": { "external_id": "cus_1042", "name": "Fahad", "traits": { "plan": "gold", "city": "Riyadh" } }, "input": "Can I change the delivery address?"}- The end user is created on first sight and updated later (traits merge).
- A session’s end user is fixed when the session is created; a different one returns
409 session_end_user_mismatch. - End-user
external_ids follow the same format as session IDs (^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$) and must not start witheu_.
You can also manage end users directly:
| Method and path | Purpose |
|---|---|
POST /v1/end_users |
Create or update by external_id |
GET /v1/end_users |
List |
GET /v1/end_users/{eu} |
Read ({eu} is the eu_ ID or your external_id) |
PATCH /v1/end_users/{eu} |
Update the name or traits |
DELETE /v1/end_users/{eu} |
Erase the end user and their data |
Client tokens
Section titled “Client tokens”A client token lets a browser or mobile app talk to the agent as one verified end user, without ever seeing your secret key. Your server mints it with a secret key (scope runs:write) and hands it to the client:
curl https://api.k-agent.kerneltics.com/v1/client_tokens \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "agent": "store-assistant", "end_user": { "external_id": "cus_1042", "name": "Fahad" }, "ttl_seconds": 900 }'{ "token": "ct_5RfQ0mXwJ8bT2nLk9pY4vC7hD1sG6aE3uZ0iWqNxb7K", "expires_at": 1791272820, "end_user": { "id": "eu_01k6rz3t5v7w9x1y2z4a6b8c0d", "object": "end_user", "external_id": "cus_1042", "name": "Fahad" }}ttl_secondsis at most 3600; the default is 900 (15 minutes).- Bind the token to one conversation with
session(asess_ID orexternal_id), or let the client open its own. variablesmay set only variables the agent declares asclient_settable; secret variables are never stored in tokens.- Tokens are stored hashed and can be revoked. A token stops working when it expires (
401 token_expired), when it is revoked, when the key that minted it is revoked, or when its end user is erased. - An open stream receives
event: errorwith{"code": "token_expired"}when the token expires. Get a new token and reconnect withLast-Event-ID.
The widget guide shows the full flow with Node and Python servers.
What browser credentials may call
Section titled “What browser credentials may call”Publishable keys and client tokens are limited to a short list of routes. Anything else returns 403 principal_not_allowed, and a session or run that belongs to another end user returns 404.
| Credential | Allowed routes |
|---|---|
Publishable key (kt_pk_…) |
GET /v1/widget/config?agent=…, POST /v1/widget/sessions |
Client token (ct_…) |
POST /v1/widget/sessions · GET /v1/sessions/{session} · GET and POST /v1/sessions/{session}/messages · GET /v1/sessions/{session}/events · POST /v1/sessions/{session}/handoff · POST /v1/sessions/{session}/close · GET /v1/runs/{run} · GET /v1/runs/{run}/events · POST /v1/runs/{run}/cancel · POST /v1/runs/{run}/submit_tool_outputs |
With a client token, messages are sent as role: "user" only and cannot carry overrides, a version, another end user or session settings. Session metadata and variables are left out of responses, and listings never include internal notes or system messages. Run steps (the trace) are never available to browser credentials.
Anonymous widget visitors
Section titled “Anonymous widget visitors”When the widget starts without a client token, POST /v1/widget/sessions creates an anonymous end user tied to a device token (dt_…). The device token is returned once, stored hashed, and lets the same browser resume its conversation later. Anonymous end users are never matched or merged with an external_id.
Anonymous traffic has its own limits: per IP, 20 widget sessions and 60 messages an hour; at most 40 messages per anonymous session; and, per agent, widget.anonymous_daily_conversations (default 200). Past a limit, visitors see a polite “try again later” notice.
Erasure and retention
Section titled “Erasure and retention”DELETE /v1/end_users/{eu} erases a person in one transaction, for data-subject requests under Saudi Arabia’s PDPL:
- their sessions and runs, with all messages, steps, events, handoffs and tickets, are deleted;
- usage records stay for billing but lose the link to the person;
- the deletion is written to the audit log by ID only;
- their client tokens stop working at once.
It needs a secret key with sessions:write, or an admin in the dashboard. Separately, whole sessions are deleted automatically once they have been inactive longer than the project’s retention_days (default 365).