Skip to content

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.

https://api.anankelabs.io/v1

An interactive API reference powered by Scalar is available at /v1/docs, and the raw OpenAPI 3.1 specification is served at /v1/openapi.json.

The API supports two authentication methods. The simulation and organization endpoints accept either method. If both are supplied, a valid API key takes precedence.

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.

Terminal window
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.

ScopeGrants access to
simulation:readList and read simulations, traces, decrees, prophecies, and events
simulation:writeCreate, run, and step simulations, submit decrees, update policies and playback
scenario:readNo additional access on public REST scenario routes
org:readRead organization details and license info
usage:readRead usage and billing metrics

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.

Terminal window
curl https://api.anankelabs.io/v1/simulations \
-H "Authorization: Bearer <jwt>"

The following endpoints require no authentication:

  • GET /v1/health
  • GET /v1/openapi.json
  • GET /v1/docs
  • GET /v1/scenarios
  • GET /v1/scenarios/{id}
  • GET /v1/auth/verify-email
  • POST /v1/auth/resend-verification
  • GET /v1/lead/verify
  • GET /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.

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"
}
}
StatusMeaning
200Success
201Resource created
204CORS preflight response
302Email verification redirect
400Validation error
401Missing or invalid authentication
403Missing API key scope, organization membership, resource access, or usage allowance
404Resource not found
409Idempotency key reused with a different request body
429Rate limit exceeded
500Internal server error

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 PUT requests and non-SSE GET requests
  • 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.

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.

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying (only on 429)

The following endpoints cache successful JSON responses when you pass an Idempotency-Key header:

  • POST /simulations
  • POST /simulations/from-domain
  • POST /simulations/run in JSON mode
  • POST /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.

Terminal window
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: true header)
  • 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.

HeaderRequiredDescription
X-Api-KeyOne ofAPI key authentication
AuthorizationOne ofBearer JWT authentication
Content-TypeFor request bodiesapplication/json
AcceptOptionalapplication/json (default) or application/x-ndjson on run and trace endpoints
X-Request-IdOptionalCustom request ID for tracing (auto-generated if omitted)
Idempotency-KeyOptionalRetry key on the four listed JSON endpoints

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.

HeaderDescription
X-Request-IdRequest identifier (echo or auto-generated)
X-RateLimit-LimitRate limit ceiling
X-RateLimit-RemainingRequests remaining
X-RateLimit-ResetWindow reset timestamp
Retry-AfterSeconds to wait (only on 429)
Idempotency-Replayedtrue if the response was replayed from cache