KRUTRIM EKAMAgent-identity control plane

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.

Tokens are ES256 JWTs. Verifiers should validate 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

ObjectIdWhat it is
Tenantten_… / olaA customer. Holds the set of legal entities and verified email domains.
EntitystringA legal-entity boundary within a tenant (tenant-scoped, sourced from CIDM — not a global enum).
Ownerown_…The human/org that owns agents and is billed. Authenticates with an Ekam API key.
Blueprintbp_…A template agents are minted from: scopes, allowed audiences, TTL, budget, classification, cost-center.
Agentagt_…A first-class, owned, revocable identity — not an API key.
TokenES256 JWTShort-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:human token 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" }
StatuserrorWhen
400invalid_requestmissing/invalid field
401invalid_tokenmissing/expired/revoked credential
403access_deniedscope/audience/entity not permitted
404not_foundunknown object

Discovery

GET/.well-known/oauth-authorization-server
GET/.well-known/openid-configuration
GET/.well-known/jwks.json
GET/.well-known/oauth-protected-resource
GET/healthz

Metadata 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

POST/v1/tenants
GET/v1/tenants
curl $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

POST/v1/owners
GET/v1/owners
GET/v1/me

Create 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

POST/v1/blueprints
GET/v1/blueprints
FieldTypeNotes
namestringrequired
scopesstring[]e.g. ["models:invoke"]
allowedAudiencesstring[]RFC 8707 resources the agent may target
tokenTtlSecondsnumberdefault 900
budgetRefstring?gateway budget id
maxClassificationstring?highest sensitivity tier
costCenterstring?cost center charged

Agents

POST/v1/agents
GET/v1/agents
POST/v1/agents/:id/revoke

Mint 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

POST/oauth/token

Brokers 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

POST/oauth/introspect

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

POST/v1/access-requests
GET/v1/access-requests
POST/v1/access-requests/:id/approve
POST/v1/access-requests/:id/deny

Request → 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

GET/auth/login
GET/auth/callback
POST/oauth/federate/google

Browser 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
Krutrim Cloud · DR enabled · access by invitation  ·  Home · Cookbook · Privacy · Terms · GitHub · Report an issue · Request a feature