REST API Overview
Kairos Nexus, the hosted service that runs the engine, provides the KAIROS REST API for stability and simulation workflows. All endpoints use the /v1 prefix and follow standard REST conventions.
Base URL
Section titled “Base URL”https://api.anankelabs.io/v1An interactive API reference powered by Scalar is available at /v1/docs, and the raw OpenAPI 3.1 specification is served at /v1/openapi.json.
Authentication
Section titled “Authentication”The API supports two authentication methods. The simulation and organization endpoints accept either method. If both are supplied, a valid API key takes precedence.
API Key
Section titled “API Key”Pass your organization’s API key in the X-Api-Key header. Keys are prefixed with krs_. Replace <api-key> in the examples with that key.
curl https://api.anankelabs.io/v1/simulations \ -H "X-Api-Key: <api-key>"API keys are scoped. When you create a key, you assign it one or more of these scopes. Scope checks do not apply to JWT callers.
| Scope | Grants access to |
|---|---|
simulation:read | List and read simulations, traces, decrees, prophecies, and events |
simulation:write | Create, run, and step simulations, submit decrees, update policies and playback |
scenario:read | No additional access on public REST scenario routes |
org:read | Read organization details and license info |
usage:read | Read usage and billing metrics |
Bearer JWT
Section titled “Bearer JWT”Pass a JWT token in the Authorization header. This method is primarily used by the Kairos web application. Replace <jwt> in the example with a JWT.
curl https://api.anankelabs.io/v1/simulations \ -H "Authorization: Bearer <jwt>"Public Endpoints
Section titled “Public Endpoints”The following endpoints require no authentication:
GET /v1/healthGET /v1/openapi.jsonGET /v1/docsGET /v1/scenariosGET /v1/scenarios/{id}GET /v1/auth/verify-emailPOST /v1/auth/resend-verificationGET /v1/lead/verifyGET /v1/lead/unsubscribe
The email and lead-link endpoints validate their own tokens or input. They serve email workflows.
An invalid supplied API key can still cause a 401 on a public endpoint.
Error Handling
Section titled “Error Handling”Simulation and organization errors use this JSON envelope. The email verification endpoint redirects, and the lead-link endpoints return HTML. POST /v1/auth/resend-verification returns { "success": true } for every request, including a cooldown rejection with status 429.
{ "error": { "code": "NOT_FOUND", "message": "Simulation not found.", "requestId": "req_abc123" }}The details field is optional. Schema validation errors include an issues array describing each field-level problem.
Malformed JSON can return 500 INTERNAL_ERROR before schema validation.
A schema validation response looks like this:
{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed.", "details": { "issues": [ { "path": "ticks", "message": "Number must be less than or equal to 10000" } ] }, "requestId": "req_abc123" }}HTTP Status Codes
Section titled “HTTP Status Codes”| Status | Meaning |
|---|---|
200 | Success |
201 | Resource created |
204 | CORS preflight response |
302 | Email verification redirect |
400 | Validation error |
401 | Missing or invalid authentication |
403 | Missing API key scope, organization membership, resource access, or usage allowance |
404 | Resource not found |
409 | Idempotency key reused with a different request body |
429 | Rate limit exceeded |
500 | Internal server error |
Rate Limiting
Section titled “Rate Limiting”Rate limits apply per identity (API key, user, or IP address). Limits scale with your organization’s license tier, with the exceptions in this section. Most responses show the limits in headers. Streams are the exception.
Route classifications:
- Create:
POST /simulations,POST /simulations/from-domain,POST /simulations/run,POST /simulations/{id}/decrees - Step:
POST /simulations/{id}/step - Read: all remaining requests, including
PUTrequests and non-SSEGETrequests - SSE:
GET /simulations/{id}/events(concurrent connection limit)
API keys use their organization’s license tier. JWT requests use an organization supplied in the path, query, or body.
Without that organization context, JWT requests use the community limits, including simulation-ID routes. Anonymous requests use the community limits.
The limiter has entries for community, professional, enterprise, and strategic. Other license tiers use the community limits.
Rate Limit Headers
Section titled “Rate Limit Headers”Responses handled by the request-window limiter include the following headers. Health, OpenAPI, and docs routes skip that limiter. Successful SSE and NDJSON streams write their headers directly and omit rate-limit metadata. An SSE limit rejection includes limit, remaining, and retry headers.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait before retrying (only on 429) |
Idempotency
Section titled “Idempotency”The following endpoints cache successful JSON responses when you pass an Idempotency-Key header:
POST /simulationsPOST /simulations/from-domainPOST /simulations/runin JSON modePOST /simulations/{id}/decrees
The step and PUT endpoints do not use this cache. NDJSON runs do not cache streaming responses.
Cached responses replay before the route’s scope and membership checks. The cache lookup still uses the caller’s authenticated identity.
Replays require a completed cached response. Concurrent requests with the same key can both execute before either response is cached.
curl -X POST https://api.anankelabs.io/v1/simulations \ -H "X-Api-Key: <api-key>" \ -H "Idempotency-Key: my-unique-key-123" \ -H "Content-Type: application/json" \ -d '{"scenarioId": "standard", "organizationId": "<organization-id>"}'Replace <organization-id> with your API key’s organization UUID. Simulation-creation requests made with an API key must explicitly include that organization ID.
Behavior:
- Same key + same body: replays the cached response (response includes
Idempotency-Replayed: trueheader) - Same key + different body: returns
409 IDEMPOTENCY_KEY_REUSE - New key: executes normally and caches a successful JSON response on the listed endpoints
Body comparison uses the parsed body’s JSON serialization. Changing object key order can cause a key-reuse conflict. The cache does not distinguish response formats: a cached JSON run can replay as JSON even when a later request asks for NDJSON.
Idempotency keys expire after 24 hours. The key is scoped to your identity, the HTTP method, and the request path.
Common Request Headers
Section titled “Common Request Headers”| Header | Required | Description |
|---|---|---|
X-Api-Key | One of | API key authentication |
Authorization | One of | Bearer JWT authentication |
Content-Type | For request bodies | application/json |
Accept | Optional | application/json (default) or application/x-ndjson on run and trace endpoints |
X-Request-Id | Optional | Custom request ID for tracing (auto-generated if omitted) |
Idempotency-Key | Optional | Retry key on the four listed JSON endpoints |
Common Response Headers
Section titled “Common Response Headers”These headers depend on the endpoint and response mode, as described in Rate Limit Headers.
The CORS configuration does not expose Idempotency-Replayed to cross-origin browser code.
| Header | Description |
|---|---|
X-Request-Id | Request identifier (echo or auto-generated) |
X-RateLimit-Limit | Rate limit ceiling |
X-RateLimit-Remaining | Requests remaining |
X-RateLimit-Reset | Window reset timestamp |
Retry-After | Seconds to wait (only on 429) |
Idempotency-Replayed | true if the response was replayed from cache |