Skip to content

End users and identity

View as Markdown

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.

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.

  • 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.

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 with eu_.

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

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_seconds is at most 3600; the default is 900 (15 minutes).
  • Bind the token to one conversation with session (a sess_ ID or external_id), or let the client open its own.
  • variables may set only variables the agent declares as client_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: error with {"code": "token_expired"} when the token expires. Get a new token and reconnect with Last-Event-ID.

The widget guide shows the full flow with Node and Python servers.

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.

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.

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).