Intents
An intent is a command submitted to causet-runtime. It expresses the caller’s desired state change — the runtime decides whether to apply it, and what events result.
What an Intent Is
Intents are the only way to write to Causet. You do not directly insert events or mutate entity state. You submit an intent — the runtime evaluates your rules and emits events.
This distinction matters:
- Intent: “I want the user to follow this artist.”
- Event: “The user followed this artist.” (fact — emitted if rules allow it)
Intent Structure
| Field | Type | Description |
|---|---|---|
action | string | The action name declared in the DSL (e.g. FOLLOW_ARTIST) |
entityId | string | The entity this intent targets |
forkId | string | The fork this intent is scoped to |
payload | object | Action-specific input fields, validated against the action’s input schema |
Example:
{
"action": "FOLLOW_ARTIST",
"entityId": "user-123",
"forkId": "production",
"payload": {
"user_id": "user-123",
"artist_id": "artist-456"
}
}The payload fields must match the input schema declared for the action in the DSL:
actions:
FOLLOW_ARTIST:
state: user
input:
user_id: { type: string, required: true }
artist_id: { type: string, required: true }Submitting Intents
Via SaaS API
POST /v1/intents
Authorization: Bearer <api-key>
Content-Type: application/json
{
"action": "FOLLOW_ARTIST",
"entityId": "user-123",
"forkId": "production",
"payload": {
"user_id": "user-123",
"artist_id": "artist-456"
}
}The SaaS API authenticates the request, resolves the fork, and proxies to causet-runtime.
Directly to causet-runtime
For server-to-server calls from trusted services:
POST /intent
Authorization: Bearer <service-credential>
Content-Type: application/json
{
"action": "FOLLOW_ARTIST",
"entityId": "user-123",
"forkId": "production",
"payload": {
"user_id": "user-123",
"artist_id": "artist-456"
}
}Intent Validation
Before rule evaluation, the runtime validates:
- Action exists — the named action must be declared in the active IR for the fork. Unknown actions return a 404.
- Required fields present — all
required: trueinput fields must be in the payload. Missing fields return a 422. - Field types — payload values must be coercible to the declared types. Type mismatches return a 422.
These checks happen before cursor lock acquisition and rule evaluation.
Preflight Rejection
After validation, preflight rules run. A reject op ends intent processing:
preflight:
rules:
- name: reject_self_follow
when: { expr: "intent.user_id == intent.artist_id" }
then:
- op: reject
code: CANNOT_FOLLOW_SELF
message: "A user cannot follow themselves."Rejection response:
{
"status": "REJECTED",
"intentId": "i-abc123",
"code": "CANNOT_FOLLOW_SELF",
"message": "A user cannot follow themselves."
}No ledger events are written on rejection.
Successful Response
On success, the runtime returns the intent ID and all emitted events:
{
"status": "SUCCESS",
"intentId": "i-xyz789",
"events": [
{
"eventType": "ARTIST_FOLLOWED",
"entityId": "user-123",
"ts": 1719331200000,
"payload": {
"user_id": "user-123",
"artist_id": "artist-456"
}
}
]
}This response is returned after the ledger commit. The events in the response are durable.
intent_status Table
Every processed intent (success or rejection) is recorded in the intent_status table in the causet database:
| Column | Description |
|---|---|
id | Intent UUID (returned as intentId) |
action | Action name |
entity_id | Target entity |
fork_id | Fork |
status | SUCCESS, REJECTED, ERROR |
rejection_code | Populated on REJECTED |
error_message | Populated on ERROR |
processed_at | Timestamp |
ir_version | IR version used for evaluation |
Intent Deduplication
Causet does not provide built-in idempotency keys at the HTTP layer. For idempotent operations, use deterministic entity IDs — submitting the same intent twice against an entity where the preflight rule rejects duplicates is the recommended pattern:
preflight:
rules:
- name: reject_already_following
when: { expr: "state.following contains intent.artist_id" }
then:
- op: reject
code: ALREADY_FOLLOWINGThe deterministic entity ID approach means: if you construct the same entityId from the same input, the entity’s current state will naturally gate duplicate processing.
Error Codes
| HTTP Status | Meaning |
|---|---|
200 | Intent processed (may be SUCCESS or REJECTED) |
400 | Malformed request body |
404 | Unknown action name for the fork’s active IR |
422 | Input validation failure (missing required fields, type mismatch) |
500 | Internal error — transient DB or Kafka failure |
503 | Runtime not ready (health check failing) |
Preflight rejections return 200 with status: REJECTED — they are a normal outcome, not an error.
Related Pages
- Rules Engine — how preflight and core rules evaluate
- The Ledger — where events are written
- Defining Events — how emitted events are declared
- Event Flow — full lifecycle after intent submission