Authentication

Kapable supports two customer authentication methods: API keys for server-to-server calls and session/JWT bearer tokens for user-context requests. Operator surfaces use a separate token tier and a separate SDK — see Operator SDK.

Token Types

TokenTierHeaderUse
sk_live_* / sk_test_* / sk_org_*Customerx-api-keyServer-to-server, org-bound, scoped (read/write/admin)
kses_* (session)CustomerAuthorization: BearerUser-context calls; carries the member's role permissions
RS256 JWTCustomerAuthorization: BearerFrontend/user flows via the auth service
st_* / sk_admin_*OperatorvariesPlatform operations — only via @kapable/ops-sdk, never the customer SDK

API Key Authentication

API keys are the simplest way to authenticate. Pass the key in the x-api-key header on every request.

# API key auth
curl https://api.kapable.ai/v1/me \
  -H "x-api-key: sk_live_abc123..."

Getting an API Key

Mint keys with your session via the auth API (a console UI for key management is on the roadmap):

curl -s -X POST https://api.kapable.ai/v1/auth/api-keys \
  -H "Authorization: Bearer kses_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"production","scopes":["read","write"]}'
# → { "id": "...", "key_prefix": "sk_live_...", "secret": "sk_live_..." }   ← secret shown ONCE

# List (prefixes only) and revoke:
curl -s https://api.kapable.ai/v1/auth/api-keys -H "Authorization: Bearer kses_..."
curl -s -X DELETE https://api.kapable.ai/v1/auth/api-keys/{id} -H "Authorization: Bearer kses_..."
Keys are org-bound and scoped — not omnipotent

An API key is bound to your org and carries scopes (read/write/admin), not your member role's permissions. Scope-gated surfaces (auth, AI, the Data API, some board routes) accept keys directly; permission-gated surfaces (board stories, knowledge) return 403 permission required for keys — use session auth there. A key may optionally be bound to a project at mint time ({"project_id":"…"}) to pin its Data-API schema. Never expose keys in client-side code or commit them to version control.

SDK Usage

const client = new KapableClient({
  baseUrl: 'https://api.kapable.ai',
  apiKey: process.env.KAPABLE_API_KEY,
});
// Rust SDK
let client = KapableClient::new("https://api.kapable.ai")
    .api_key(std::env::var("KAPABLE_API_KEY").unwrap())
    .build();

JWT Bearer Token

For user-context requests (e.g. from a frontend), use a JWT token obtained via the Auth service's login endpoint. Pass it in the Authorization header.

# Bearer token auth
curl https://api.kapable.ai/v1/board/stories \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Obtaining a Token

// Login. Response fields: session_token, access_token (JWT),
// access_token_expires_in, role, org_id, member_id, email, name.
const res = await client.auth.login({
  email: 'user@example.com',
  password: '...',
});

// Use the session token (kses_) as the Bearer `token` for subsequent requests.
const authedClient = new KapableClient({
  baseUrl: 'https://api.kapable.ai',
  token: res.session_token,
});
// Rust SDK
use kapable_sdk::auth::types::LoginRequest;

let resp = client.auth().login(&LoginRequest {
    email: "user@example.com".into(),
    password: "...".into(),
}).await?;

let authed_client = KapableClient::new("https://api.kapable.ai")
    .token(resp.token)
    .build();

Org Context

All API calls are scoped to the authenticated org. API keys are org-bound. JWT tokens carry the org ID in the token claims. Multi-org users select their active org during login.

Agent Keys (Comms)

The Comms service supports a third auth model: agent keys. These are per-agent API keys minted via POST /v1/agents/{id}/keys that allow agent processes to authenticate directly for SSE streaming and message sending.

// Mint an agent key
const { key } = await client.comms.mintAgentKey(agentId, {
  label: 'production',
});

// Use it to connect the agent's SSE stream
const stream = new EventSource(
  `https://api.kapable.ai/v1/agents/${agentId}/stream`,
  { headers: { 'x-api-key': key } }
);
// Rust SDK
use kapable_sdk::comms::types::MintAgentKeyRequest;

let resp = client.comms().mint_agent_key(agent_id, MintAgentKeyRequest {
    label: "production".into(),
}).await?;
println!("Agent key: {}", resp.key);

App Tokens (Comms)

For service-to-service integration with the Comms internal endpoints, use app tokens. These are org-scoped tokens for programmatic access to intercept and send operations.

// Mint an app token for your integration
const { token } = await client.comms.mintAppToken(orgId, {
  label: 'email-worker',
});
// Rust SDK
use kapable_sdk::comms::types::MintAppTokenRequest;

let resp = client.comms().mint_app_token(org_id, MintAppTokenRequest {
    label: "email-worker".into(),
}).await?;

Error Responses

Authentication failures return 401 Unauthorized. Two envelope variants exist across services — handle both (the SDKs normalize them into KapableError for you):

// Most services (data, board, knowledge, ai, ...):
{ "error": { "code": "UNAUTHORIZED", "message": "missing auth token" } }

// Auth service (flat variant):
{ "error": "UNAUTHORIZED", "message": "authentication required via x-api-key, Authorization: Bearer, or session cookie" }