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
SaaS API (recommended)
POST /v1/platforms/{platformSlug}/applications/{applicationSlug}/intents/execute
Host: causet-saas-cloud:8085
Authorization: Bearer {clerk_jwt}
Content-Type: application/jsonPath parameters:
| Parameter | Type | Description |
|---|---|---|
platformSlug | string | Platform identifier |
applicationSlug | string | Application identifier |
Request body:
{
"forkId": "main",
"action": "CREATE_ITEM",
"entityId": "item-1",
"payload": {
"item_id": "item-1",
"title": "Hello"
}
}| Field | Type | Required | Description |
|---|---|---|---|
forkId | string | yes | Fork to target (e.g. "main", "staging") |
action | string | yes | Action name as defined in the DSL |
entityId | string | yes | The entity this intent targets |
payload | object | yes | Action 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/jsonRequest 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:
- IR resolution — load active IR for the fork from Redis cache
- Entity load — read current entity state from
ledger_events(or snapshot) - Preflight — evaluate preflight rules; reject if any
rejectop fires - Core — evaluate core rules; collect state mutations and
emitops - Side effects — evaluate side effect rules; collect additional
emitops - Persist — append
ledger_eventsrows with a cursor version check - Publish — push to
causet.ledger-events.v1andcauset.projection-events.v1Kafka 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/jsonRequest 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/GetEntityStateResponse 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:8080Response — 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 Status | Code | Description |
|---|---|---|
| 400 | UNKNOWN_ACTION | The action field does not match any action in the active IR |
| 400 | PREFLIGHT_REJECTION | A preflight rule fired a reject op; includes the DSL-defined code |
| 404 | ENTITY_NOT_FOUND | The target entity does not exist in the ledger |
| 409 | CONCURRENT_WRITE | Cursor contention — another writer modified the entity; retry |
| 503 | SERVICE_UNAVAILABLE | Database 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_EXCEEDEDRate 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.