Runtime API

The Causet runtime exposes two surfaces for intent submission: the SaaS API (causet-saas-cloud, port 8085) and the direct runtime API (causet-runtime-service, port 8080). For most use cases, use the SaaS API — it handles authentication, release resolution, and tenant routing.


Authentication

All SaaS API endpoints require a Clerk JWT in the Authorization header:

Authorization: Bearer <clerk_jwt>

To obtain a JWT for anonymous users, create an anonymous session first. Server-to-server integrations can use API keys issued per platform (see Security overview).


Intent Submission

POST /v1/platforms/{platformSlug}/applications/{applicationSlug}/intents/execute
Host: causet-saas-cloud:8085
Authorization: Bearer {clerk_jwt}
Content-Type: application/json

Path parameters:

ParameterTypeDescription
platformSlugstringPlatform identifier
applicationSlugstringApplication identifier

Request body:

{
  "forkId": "main",
  "action": "CREATE_ITEM",
  "entityId": "item-1",
  "payload": {
    "item_id": "item-1",
    "title": "Hello"
  }
}
FieldTypeRequiredDescription
forkIdstringyesFork to target (e.g. "main", "staging")
actionstringyesAction name as defined in the DSL
entityIdstringyesThe entity this intent targets
payloadobjectyesAction input fields matching the DSL input: block

Response — success:

{
  "status": "ACCEPTED",
  "eventIds": ["evt_01j2x..."],
  "entityId": "item-1",
  "forkId": "main"
}

Response — preflight rejection:

{
  "status": "REJECTED",
  "code": "CANNOT_FOLLOW_SELF",
  "message": "Preflight rule 'reject_self_follow' rejected this intent"
}

HTTP status for rejection is 400.

Direct Runtime API

Use the direct runtime for internal service-to-service calls or local development without the SaaS layer.

POST /api/v1/intents/submit
Host: causet-runtime-service:8080
Content-Type: application/json

Request body includes the resolved platform and application identifiers:

{
  "platformId": "my-platform",
  "applicationId": "concert-app",
  "forkId": "main",
  "action": "FOLLOW_ARTIST",
  "entityId": "user-1",
  "payload": {
    "user_id": "user-1",
    "artist_id": "artist-pearl-jam"
  }
}

Note: The direct runtime API does not enforce Clerk authentication. Secure this endpoint at the network layer (VPC, security groups) in production.


Intent Processing Model

Intent submission is synchronous — the HTTP response reflects the outcome of preflight evaluation and event persistence. Projection materialization is asynchronous.

Processing stages:

  1. IR resolution — load active IR for the fork from Redis cache
  2. Entity load — read current entity state from ledger_events (or snapshot)
  3. Preflight — evaluate preflight rules; reject if any reject op fires
  4. Core — evaluate core rules; collect state mutations and emit ops
  5. Side effects — evaluate side effect rules; collect additional emit ops
  6. Persist — append ledger_events rows with a cursor version check
  7. Publish — push to causet.ledger-events.v1 and causet.projection-events.v1 Kafka topics

If another writer has modified the entity since it was loaded (cursor contention), the runtime returns 409 CONCURRENT_WRITE. Retry with exponential backoff.


Anonymous Session

Creates a short-lived anonymous session. Returns a JWT usable for subsequent API calls.

POST /v1/sessions/anonymous
Host: causet-saas-cloud:8085
Content-Type: application/json

Request body: {} (empty)

Response:

{
  "sessionId": "anon_01j2x...",
  "token": "<clerk_jwt>",
  "expiresAt": "2026-06-26T00:00:00Z"
}

Pass the returned token as Authorization: Bearer <token> on subsequent requests.


Entity Browser (gRPC)

EntityBrowserGrpcService on port 9090 exposes entity state for inspection. This is a gRPC service — use grpcurl or a gRPC client.

grpcurl -plaintext \
  -d '{"platformId":"my-platform","applicationId":"concert-app","forkId":"main","entityType":"user","entityId":"user-1"}' \
  localhost:9090 \
  causet.EntityBrowserService/GetEntityState

Response includes:

  • Current entity field values
  • Full ledger event history for the entity
  • Cursor version

Note: The entity browser gRPC service is intended for debugging and tooling. It is not a primary application API.


Health Check

GET /actuator/health
Host: causet-runtime-service:8080

Response — healthy:

{
  "status": "UP",
  "components": {
    "db": { "status": "UP" },
    "kafka": { "status": "UP" },
    "redis": { "status": "UP" }
  }
}

Response — degraded:

{
  "status": "DOWN",
  "components": {
    "db": { "status": "DOWN", "details": { "error": "Connection refused" } }
  }
}

HTTP status is 200 when UP, 503 when any critical component is DOWN.


Error Codes

HTTP StatusCodeDescription
400UNKNOWN_ACTIONThe action field does not match any action in the active IR
400PREFLIGHT_REJECTIONA preflight rule fired a reject op; includes the DSL-defined code
404ENTITY_NOT_FOUNDThe target entity does not exist in the ledger
409CONCURRENT_WRITECursor contention — another writer modified the entity; retry
503SERVICE_UNAVAILABLEDatabase or Kafka is unreachable

PREFLIGHT_REJECTION error body:

{
  "error": "PREFLIGHT_REJECTION",
  "code": "TICKET_LIMIT_EXCEEDED",
  "rule": "max_tickets_per_user",
  "message": "Preflight rejection: TICKET_LIMIT_EXCEEDED"
}

The code value comes directly from the DSL reject op:

then:
  - op: reject
    code: TICKET_LIMIT_EXCEEDED

Rate Limiting

Proposed: Per-platform rate limiting is on the roadmap. Currently the runtime does not enforce request-level rate limits — use an API gateway or load balancer for rate control in production.