KRNLA API contract
Four separate surfaces, versioned independently: the control plane configures, the gateway enforces, agents communicate, telemetry is read back. Control-plane routes authenticate with the dashboard session; gateway routes authenticate with an agent credential.
Authentication
Gateway calls carry the agent credential issued in the dashboard. The organization is derived from the credential — there is no organization parameter on any gateway endpoint.
Authorization: Bearer krn_live_xxxxxxxx_... # or X-KRNLA-Agent-Key: krn_live_xxxxxxxx_...
Response envelope
Every response is { ok, data } on success and { ok: false, error } on failure. Gateway responses always include the decision envelope in data, so an SDK can read data.allowed whether the request was allowed or blocked.
{
"ok": false,
"error": { "code": "POLICY_BLOCKED", "message": "Request blocked by an organization policy." },
"data": {
"allowed": false,
"code": "POLICY_BLOCKED",
"reason_code": "PII_DETECTED",
"policy_id": "6a91...",
"decision": "block",
"trace_id": "tr_a1b2c3"
}
}The response never contains the detected value, the payload that triggered it, or internal error details.
Gateway · POST /api/gateway/v1/evaluate
Governance only: KRNLA decides, the caller keeps its own transport.
curl -X POST https://krnla.com/api/gateway/v1/evaluate \
-H "Authorization: Bearer $KRNLA_AGENT_KEY" \
-H "content-type: application/json" \
-d '{
"destination": "https://api.openai.com/v1/chat/completions",
"model": "gpt-4o-mini",
"payload": { "prompt": "customer SSN is 123-45-6789" }
}'Gateway · POST /api/gateway/v1/messages
KRNLA-mediated agent-to-agent delivery. Agent A names Agent B; KRNLA authenticates, governs and delivers to Agent B's inbound URL. No retry is performed — an agent message may be non-idempotent, so the delivery status is reported instead.
curl -X POST https://krnla.com/api/gateway/v1/messages \
-H "Authorization: Bearer $KRNLA_AGENT_KEY" \
-H "content-type: application/json" \
-d '{ "to": "billing-agent", "payload": { "task": "reconcile invoice 88213" } }'KRNLA delivers to the destination agent as:
POST <inbound_url>
X-KRNLA-Trace-Id: tr_a1b2c3
X-KRNLA-Source-Agent: support-agent
{ "from": "support-agent", "trace_id": "tr_a1b2c3", "payload": { ... } }Gateway · POST /api/gateway/v1/llm
Governed agent-to-LLM proxying. The provider credential stays yours: pass it in X-KRNLA-Upstream-Authorization and KRNLA forwards it upstream without logging or storing it. With stream: true the response is governed chunk by chunk and never fully buffered.
curl -N -X POST https://krnla.com/api/gateway/v1/llm \
-H "Authorization: Bearer $KRNLA_AGENT_KEY" \
-H "X-KRNLA-Upstream-Authorization: Bearer $OPENAI_API_KEY" \
-H "content-type: application/json" \
-d '{
"destination": "https://api.openai.com/v1/chat/completions",
"stream": true,
"payload": { "model": "gpt-4o-mini", "stream": true,
"messages": [{ "role": "user", "content": "summarize ticket 4412" }] }
}'Response headers carry the decision: X-KRNLA-Decision, X-KRNLA-Reason-Code, X-KRNLA-Policy-Id, X-KRNLA-Trace-Id.
Control plane
| GET | /api/control/v1/me | Session identity and its organization |
| GET / PUT | /api/control/v1/organization | Gateway configuration for the tenant |
| GET / POST | /api/control/v1/agents | List agents · register an agent (returns the key once) |
| GET / PUT / DELETE | /api/control/v1/agents/:id | Read, update status or inbound URL, delete |
| POST | /api/control/v1/agents/:id/rotate-key | Issue a new credential, invalidate the old one |
| GET / POST | /api/control/v1/policies | List and create structured policies |
| GET / PUT / DELETE | /api/control/v1/policies/:id | Read, edit, enable/disable, delete |
| GET | /api/control/v1/activity | Paginated, server-side-filtered security events |
| GET | /api/control/v1/overview | Aggregated metrics from rollups |
Operations
| GET | /api/gateway/v1/health | Public liveness check, no tenant data |
| GET | /api/internal/v1/metrics | KRNLA infrastructure metrics (session-authenticated) |
Policy model
A policy is data, not text for a model to interpret. Conditions reference normalized facts produced by the deterministic detector; the engine evaluates policies in ascending priority and the first match decides.
{
"name": "Customer data protection",
"priority": 10,
"action": "block",
"reason_code": "PII_DETECTED",
"conditions": { "match": "all",
"conditions": [{ "fact": "PII_DETECTED", "operator": "eq", "value": true }] },
"scope": { "channels": ["agent_to_llm"], "source_agents": [], "models": [] }
}Facts available today: PII_DETECTED, PII_TYPES, PII_MATCH_COUNT, SECRET_DETECTED, SECRET_TYPES, PROMPT_INJECTION_SUSPECTED, DESTINATION_ALLOWLISTED, DESTINATION_HOST, SOURCE_AGENT_NAME, MODEL, TOOL, REQUEST_BYTES, TOKEN_ESTIMATE, CHANNEL. Actions today: ALLOW, BLOCK, REDACT.
Error codes
| 401 | UNAUTHENTICATED | Missing or unknown agent credential |
| 403 | POLICY_BLOCKED | A policy blocked the request |
| 403 | AGENT_DISABLED | Agent disabled or credential revoked |
| 404 | DESTINATION_NOT_FOUND | Destination agent not in this organization |
| 413 | REQUEST_TOO_LARGE | Above the configured request size limit |
| 429 | RATE_LIMITED | Organization or agent limit exceeded |
| 503 | CONFIG_UNAVAILABLE | Policy configuration unavailable and fail mode is closed |
| 504 | UPSTREAM_TIMEOUT | Destination exceeded the configured timeout |