Authentication
The Agent SDK runs in your frontend and talks directly to Kapa's backend services. To do this without exposing your API key in the browser, the SDK uses short-lived session tokens: your server mints a token from your API key, and the SDK then uses that token for all its calls to Kapa.
How it works
- The browser calls your server endpoint (e.g.
POST /api/session) - Your server calls the Kapa session API with the
X-API-Keyheader - Kapa returns a session token (valid for 1 hour)
- Your server forwards the token to the browser
- The SDK uses the token for all subsequent API calls
The API key never leaves your server.
Session endpoint
POST https://api.kapa.ai/agent/v1/projects/{projectId}/agent/sessions/
Headers:
X-API-Key: your-api-key
Response:
{
"session_token": "NRd60UqDpLSeeIFzCfmj5dxiRDOJL8G7aSXVzpQ0pPusbe9kHIjEymznutrJu6uf",
"expires_at": "2026-03-17T09:54:51.165812Z"
}
Server-side proxy example
Your getSessionToken function should call your own server, which proxies the request:
getSessionToken={async () => {
const res = await fetch('/api/session', { method: 'POST' });
if (!res.ok) throw new Error('Session creation failed');
return res.json();
}}
Your server just passes through the Kapa API response:
const response = await fetch(
`https://api.kapa.ai/agent/v1/projects/${projectId}/agent/sessions/`,
{ method: 'POST', headers: { 'X-API-Key': apiKey } },
);
res.json(await response.json());
The SDK accepts the raw Kapa API response ({ session_token, expires_at }) directly. No transformation needed. It also accepts the normalized format ({ token, expiresAt }).
Token lifecycle
The SDK manages tokens automatically:
- Lazy fetching.
getSessionTokenis not called until the user sends their first message - Caching. Tokens are cached and reused for subsequent requests
- Auto-refresh. Tokens are refreshed 30 seconds before expiry
- Retry on 401. If a request fails with 401, the token is cleared and a fresh one is fetched
- Deduplication. Concurrent refresh calls are deduplicated (only one
getSessionTokencall at a time)
Conversation history
To enable conversation history, include external_owner_id in the request body when calling the Kapa session API. This is an opaque string that identifies which end user owns the session, for example an internal user ID or a hashed email address.
Why it works this way. Conversation history is scoped to the session. When a session is created with external_owner_id, Kapa records every conversation thread under that owner. The history endpoints (listThreads, resumeThread, deleteThread) then only return threads owned by the session making the request. One session cannot access another user's threads.
Why it is safe. The external_owner_id must be set by your trusted backend using your API key. The frontend never passes this value. It only holds a short-lived session token, so there is no way for a user to forge or modify their external_owner_id from the browser.
Sessions created without external_owner_id can still chat normally, but cannot access history endpoints.
const response = await fetch(
`https://api.kapa.ai/agent/v1/projects/${projectId}/agent/sessions/`,
{
method: 'POST',
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({ external_owner_id: currentUser.id }),
},
);
// forward the response to your client as-is
res.json(await response.json());
The value must be stable across sessions for the same user. All threads created under the same external_owner_id are returned by listThreads.
Getting your credentials
API key
- Log in to your Kapa dashboard
- Navigate to Settings → API Keys
- Create or copy an existing API key
- Store it as a server-side environment variable (e.g.
KAPA_API_KEY)
Project ID and Integration ID
- In your Kapa dashboard, go to Integrations → Agent
- Create an Agent integration if you don't have one
- Copy the Project ID and Integration ID from the integration setup page
- The Project ID is used both in the session endpoint URL and as the
projectIdprop onAgentProvider - The Integration ID is passed as the
integrationIdprop onAgentProvider