Developer documentation
Provision agent identities, broker short-lived delegated tokens, and verify them
offline. Base URL https://ekam.olakrutrim.com.
↓ Download the Postman collection
Introduction
Krutrim Ekam is an OAuth 2.1 / OIDC authorization server for agents and humans. An owner mints an agent from a blueprint; Ekam brokers a short-lived, audience-bound, scoped, delegated token; your AI gateway verifies it offline against the published JWKS. Revocation is instant via introspection.
iss, aud
(RFC 8707), exp, signature (JWKS) and — for live revocation — call introspection.Quickstart
From an owner API key to a verified token in five calls.
BASE=https://ekam.olakrutrim.com
# 1. Create a blueprint (scopes, audience, TTL) — owner key
curl -s $BASE/v1/blueprints -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' -d '{
"name": "chat-bot",
"scopes": ["models:invoke"],
"allowedAudiences": ["https://your-gateway.example"],
"tokenTtlSeconds": 900
}'
# 2. Mint an agent from the blueprint
curl -s $BASE/v1/agents -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' -d '{"blueprintId":"bp_123","name":"support-bot"}'
# 3. Broker a token for the agent (resource-bound + scoped)
curl -s $BASE/oauth/token -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' \
-d '{"grant_type":"urn:ietf:params:oauth:grant-type:token-exchange","agent_id":"agt_123","resource":"https://your-gateway.example","scope":"models:invoke"}'
# -> { "access_token": "<ES256 JWT>", "token_type": "Bearer", "expires_in": 900 }
# 4. Use it against your gateway; the gateway verifies offline via JWKS.
# 5. Revoke instantly if needed:
curl -s -X POST $BASE/v1/agents/agt_123/revoke -H "authorization: Bearer $OWNER_KEY"Core concepts
| Object | Id | What it is |
|---|---|---|
| Tenant | ten_… / ola | A customer. Holds the set of legal entities and verified email domains. |
| Entity | string | A legal-entity boundary within a tenant (tenant-scoped, sourced from CIDM — not a global enum). |
| Owner | own_… | The human/org that owns agents and is billed. Authenticates with an Ekam API key. |
| Blueprint | bp_… | A template agents are minted from: scopes, allowed audiences, TTL, budget, classification, cost-center. |
| Agent | agt_… | A first-class, owned, revocable identity — not an API key. |
| Token | ES256 JWT | Short-lived, aud-bound, scoped, delegated (act = the owner). The thing your gateway verifies. |
Authentication
Three credential types:
- Admin bearer (
EKAM_ADMIN_TOKEN) — tenant + owner administration (/v1/tenants,/v1/owners). - Owner API key — owner-scoped operations: blueprints, agents, and the token broker.
- Human session — a
type:humantoken from Google SSO (Human SSO).
# Admin
curl $BASE/v1/owners -H "authorization: Bearer $EKAM_ADMIN_TOKEN"
# Owner
curl $BASE/v1/agents -H "authorization: Bearer $OWNER_KEY"Errors
Errors follow the OAuth shape with an HTTP status:
{ "error": "invalid_request", "error_description": "audience not allowed by blueprint" }| Status | error | When |
|---|---|---|
| 400 | invalid_request | missing/invalid field |
| 401 | invalid_token | missing/expired/revoked credential |
| 403 | access_denied | scope/audience/entity not permitted |
| 404 | not_found | unknown object |
Discovery
/.well-known/oauth-authorization-server/.well-known/openid-configuration/.well-known/jwks.json/.well-known/oauth-protected-resource/healthzMetadata per RFC 8414 / RFC 9728, the public JWKS for offline verification, and a health probe.
curl $BASE/.well-known/jwks.json
# { "keys": [ { "kty":"EC","crv":"P-256","kid":"ekam-2026-06","alg":"ES256", ... } ] }Tenants · admin
/v1/tenants/v1/tenantscurl $BASE/v1/tenants -H "authorization: Bearer $EKAM_ADMIN_TOKEN" \
-H 'content-type: application/json' -d '{
"id":"acme","name":"Acme Corp",
"entities":["acme_in"],
"domains":{"acme.com":"acme_in"}
}'Owners · admin
/v1/owners/v1/owners/v1/meCreate an owner within a tenant + entity; the response includes the one-time API key. /v1/me returns the caller's principal.
curl $BASE/v1/owners -H "authorization: Bearer $EKAM_ADMIN_TOKEN" \
-H 'content-type: application/json' -d '{"name":"Payments team","tenantId":"ola","entity":"olaelectric_india"}'Blueprints
/v1/blueprints/v1/blueprints| Field | Type | Notes |
|---|---|---|
name | string | required |
scopes | string[] | e.g. ["models:invoke"] |
allowedAudiences | string[] | RFC 8707 resources the agent may target |
tokenTtlSeconds | number | default 900 |
budgetRef | string? | gateway budget id |
maxClassification | string? | highest sensitivity tier |
costCenter | string? | cost center charged |
Agents
/v1/agents/v1/agents/v1/agents/:id/revokeMint an agent from a blueprint, list agents, and kill-switch one. Revocation propagates to introspection within seconds.
curl $BASE/v1/agents -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' -d '{"blueprintId":"bp_123","name":"support-bot"}'
curl -X POST $BASE/v1/agents/agt_123/revoke -H "authorization: Bearer $OWNER_KEY"Token broker
/oauth/tokenBrokers a short-lived ES256 token for an agent, bound to one resource and a subset of the
blueprint's scopes. The token carries act.sub = the owner (RFC 8693 delegation).
curl $BASE/oauth/token -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' -d '{
"grant_type":"urn:ietf:params:oauth:grant-type:token-exchange",
"agent_id":"agt_123",
"resource":"https://your-gateway.example",
"scope":"models:invoke"
}'{ "access_token": "<ES256 JWT>", "token_type": "Bearer", "expires_in": 900, "scope": "models:invoke" }Introspection
/oauth/introspectRFC 7662. Returns { "active": false } the moment an agent or token is revoked — the live kill-switch.
curl $BASE/oauth/introspect -H 'content-type: application/json' -d '{"token":"<JWT>"}'
# { "active": true, "sub": "agt_123", "aud": "https://your-gateway.example", "scope": "models:invoke", "exp": ... }Access requests (IGA)
/v1/access-requests/v1/access-requests/v1/access-requests/:id/approve/v1/access-requests/:id/denyRequest → approve/deny → grant. On approval the agent is created (the grant) and linked back to the request.
curl $BASE/v1/access-requests -H "authorization: Bearer $OWNER_KEY" \
-H 'content-type: application/json' -d '{"blueprintId":"bp_123","agentName":"support-bot","reason":"Q3 support automation"}'Human SSO
/auth/login/auth/callback/oauth/federate/googleBrowser login: redirect a human to /auth/login → Google → /auth/callback mints a
type:human token. Restricted to the tenant's verified domains. Programmatic federation exchanges
a Google id_token directly.
curl $BASE/oauth/federate/google -H 'content-type: application/json' \
-d '{"id_token":"<google id_token>","audience":"https://your-gateway.example","scope":"models:invoke"}'ID-JAG · cross-app delegation
Identity Assertion Authorization Grant (draft-ietf-oauth-identity-assertion-authz-grant). Issue an ID-JAG via token-exchange, then redeem it for an access token — preserving tenant + entity across apps.
# Issue (token-exchange)
curl $BASE/oauth/token -H 'content-type: application/json' -d '{
"grant_type":"urn:ietf:params:oauth:grant-type:token-exchange",
"requested_token_type":"urn:ietf:params:oauth:token-type:id-jag",
"subject_token":"<self-issued token>",
"audience":"https://app-b.example"
}'
# Redeem (jwt-bearer) at the target app's Ekam
curl $BASE/oauth/token -H 'content-type: application/json' -d '{
"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion":"<id-jag>"
}'@krutrim/ekam-verify
The SDK your gateway imports to verify Ekam tokens offline (JWKS-cached) with optional live revocation.
import { createEkamVerifier } from "@krutrim/ekam-verify";
const verify = createEkamVerifier({
issuer: "https://ekam.olakrutrim.com",
jwksUri: "https://ekam.olakrutrim.com/.well-known/jwks.json",
audience: "https://your-gateway.example",
introspectUrl: "https://ekam.olakrutrim.com/oauth/introspect", // optional: live kill-switch
});
const agent = await verify(bearerToken);
// -> { agentId, ownerId, scopes, audience, tenant, entity, budgetRef, ... }
// enforce agent.scopes -> route -> meter -> bill agent.ownerId