Runtime API
The Runtime REST API is generated from a published DomainConfig and includes app-scoped platform endpoints for user-visible channel interaction. The generated OpenAPI contract for the target app is canonical.
OpenAPI
Fetch the contract:
The Production OpenAPI document is public metadata. Add
?workspaceId=<workspace-id> when the contract should describe a Workspace
runtime source instead; because that response can expose unpublished model
metadata, the Workspace request requires normal app-scoped Runtime bearer
authentication. Use the response to discover endpoint names, schemas, auth
requirements, response shapes, and errors. Do not infer app-specific endpoint
names from another app or mix Production and Workspace contracts.
Workspace OpenAPI and agent discovery can describe agents that exist only in the selected Workspace. The channel runtime still resolves a new DM target from the published app configuration, so a Workspace-only agent cannot receive a DM until that change is promoted.
Commands
Published commands become REST endpoints. A command request:
- authenticates the caller
- checks permissions
- validates input
- loads decision state
- emits events if the command succeeds
- returns a structured result
Keep cross-command orchestration, lookup-before-create, and cross-app
coordination outside the command request itself. For delivery safety, send an
optional Idempotency-Key header containing a client-owned UUID. One key names
one exact command intent:
- Repeating the same command and body with the same key returns the canonical
committed receipt with
idempotentReplay: true; it does not dispatch the handler or append the command events again. - Reusing a key with different input returns
409 COMMAND_IDEMPOTENCY_CONFLICTandeffectState: rejected. - A typed
503 COMMAND_UNAVAILABLEwitheffectState: rolled_backconfirms that the request did not append. Start a new intent only if the operator still wants the action. - A lost connection or unclassified
5xxhas an unknown outcome. Retry the identical request with the same key until the canonical receipt or an authoritative terminal result is returned. Do not switch to a new key while the original outcome is unknown.
The executionId in the response equals the supplied intent UUID and, together
with correlationId, locates the Production or Workspace execution trace.
Idempotency covers transport retries for one intent; business-level dedupe
across distinct intent keys still belongs in the domain or caller workflow.
New receipt-bound commands retain their original scope and decision-boundary evidence with the committed events. Recovery after a lost commit acknowledgement returns that recorded decision without running the handler again. Historical receipts without this context remain readable; Cessy does not invent missing decision evidence. Conflicting or malformed recorded context fails closed.
A successful command proves the canonical command execution boundary and the append of any events reported by that execution. It does not prove that every downstream projection has applied those events. Diagnose projection delivery separately; a missing projection row is not proof that the command never ran, and retrying with a new intent can create a second business operation. Reuse the same idempotency key when the original outcome is unknown. For events covered by durable runtime intake, Cessy automatically retries a missing projection phase without replaying the command or appending another source event.
Policy fan-out
A custom_code policy may return at most 100 ordinary inline commands. A larger
ordinary result is rejected before any child command write begins. Use a
homogeneous durable batch for more than 100 similar commands, or model explicit
durable stages when command types are mixed or the workflow needs a final
completion barrier.
The 10-100 range is an admission ceiling, not an automatic recommendation. Use
inline fan-out in that range only when the complete downstream cascade,
including triggered policies and projection callbacks, is bounded and measured
below 60 seconds at p95 and below the 90-second hard segment regression budget. Prefer
__batch even for 100 items or fewer when homogeneous high-volume work needs
operational isolation, independent retries, rate limiting, progress, or
dead-letter handling. For mixed work or final synchronization, model explicit
durable stages now; a reusable durable completion barrier is tracked for future
work in #3005.
Inline fan-out also bounds durable identity by UTF-8 bytes: 1,000 bytes for a command type and 2,000 bytes each for a resolved scope key or child ID. This validation covers explicit, command-data, boundary-derived, and trigger-default scopes before child dispatch. Oversized untrusted identities are represented by bounded hash-suffixed failure evidence; derived parent prefixes use deterministic hashes.
Prepared children with the same resolved decision scope execute sequentially in declaration order. Independent scope groups use a sliding window, so each free lane starts the next ready scope instead of waiting for a fixed chunk to complete.
For admitted inline fan-out, runtime persists a complete child evidence census
before the first write and reserves handler time for terminalization. The census
contains stable child identity, index, command type, resolved scope, and status;
it does not contain command payloads or replay intent. Terminal evidence accounts
for every child as succeeded, failed, unknown, or not_started. unknown
means the child was issued but its append outcome was not confirmed; reconcile it
against stable business or canonical event evidence before retrying. When the
dispatch deadline is reached, runtime records the exhaustive policy failure and,
when onError is configured, a leased compensation owner before invoking that
command. The failure becomes terminal only after compensation is durably
completed or explicitly terminated by an audited abort. Operator aborts use
OPERATOR_ABORTED only before dispatch starts; terminating a started attempt
retains DISPATCH_OUTCOME_UNKNOWN. Recovery may use WORKSPACE_EXPIRED or
WORKSPACE_UNAVAILABLE when the exact Workspace source definitively no longer
exists. An abort records actor, reason, and timestamp without claiming that the
compensation command ran. This is an evidence contract, not a replayable child
outbox.
If a configured onError command cannot be mapped, normalized, or encrypted,
runtime has not created a compensation owner and does not fabricate one merely
to mark it aborted. It first persists a queryable ErrorLog diagnostic with code
POLICY_FANOUT_COMPENSATION_PREPARATION_FAILED under a deterministic identity;
only after that write is confirmed may it terminalize the original fan-out
without compensation, retaining the fan-out’s primary error code. Diagnostic
failure leaves the lifecycle running (or missing before a manifest exists) and
fails closed. The diagnostic contains controlled references and a phase only,
never mapping expressions, command/event data, receipt errors, crypto messages,
or credentials.
For criteria-based child commands, the persisted fan-out cutoff is also the absolute PostgreSQL statement cutoff. The runtime overrides a shorter pooled session default for that transaction, then uses the reserved terminal window to finish rollback and an indexed raw-tag probe while retaining half of that window for durable terminal lifecycle persistence. Exact canonical rows produce a successful receipt and are published with their stored identities; a confirmed rollback with canonical absence produces a failed receipt; unresolved, unreadable, partial, or mismatched evidence remains unknown. Reconciled receipts surface only operation class, elapsed time, and reconciliation state, never SQL, criteria, tags, command data, or event payloads. Production and Workspace probes remain isolated by their effective app IDs.
Inline fan-out requires a stable trigger event ID. Redelivery before the
persisted deadline reports the execution as already in progress without
reissuing children or running onError. At or after the deadline, runtime can
atomically record failure on the same execution without replaying children.
Because a running census is not a per-child checkpoint, interruption recovery
marks every child unknown; reconcile canonical evidence before a business
retry. onError is a separate durable compensation phase: redelivery waits for
its live lease or claims an expired lease, then checks indexed canonical child
evidence and verifies the encrypted intent’s exact materialized-runtime
fingerprint before dispatch. The fingerprint covers full config, including
subscriptions and downstream policies, plus a versioned executor contract. An
append already present after an ambiguous failure is recorded as reconciled
without fabricated execution or event-count evidence.
Before mutable worker execution, the canonical lifecycle pre-read uses the same
deterministic execution ID through a bounded transient-connection retry
sequence. Only a successful read with no lifecycle proves that the execution
has not started. Retry exhaustion, cancellation, deadline expiry, or an
unavailable canonical probe fails closed before worker code or business
onError instead of risking duplicate work. Exhaustion does not schedule an
automatic retry job; explicit/source redelivery of the durable event reuses the
same deterministic lifecycle ID and canonical fence.
One process-wide authority admits at most ten covered runtime operations and at most seven ordinary command executions. One slot remains available for each missing protected class: lifecycle, projection callback, and durable diagnostic evidence. Owner-fair queues and bounded control priority allow control work to progress without starving commands. These are process-local admission ceilings, not cross-replica or physical database-pool reservations.
Every ordinary command entry point uses this authority, including public API,
batch, onError, and compensation execution. Each physical attempt owns its
command slot through handler dispatch, event persistence, and synchronous
projection publication, then releases it before retry backoff or synchronous or
asynchronous post-policy handlers dispatch nested commands. Inline fan-out uses
the same executor-owned admission for every physical attempt; caller-authored
fan-out identity metadata cannot bypass it. If a caller’s deadline expires after
work starts, the caller returns promptly but the slot remains occupied until
that physical attempt and synchronous projection work settle.
Async policy queue wait is measured separately and counts toward the supported
end-to-end segment budget. The handler execution deadline starts only after
admission; expiring a queued subscription after its source event was persisted
would silently skip that delivery. Once admitted, worker execution, projection
callbacks, command admission and execution, and terminal persistence share the
handler deadline. Projection callbacks are capped at 30 seconds and below the
remaining handler budget, with cooperative cancellation and a terminal guard.
Structured telemetry separates handler queue wait, central command admission
(runtime.command_admission.wait_ms), fan-out command-permit wait, lifecycle
phase/retry timing, diagnostic persistence, and projection-callback timing.
Protected lifecycle diagnostics include pool-acquire/query durations, the
configured deadline, a before/during-query termination phase, and one correlated
recovery outcome after a successful retry. Deterministic lifecycle IDs appear in
that protected diagnostic, never in aggregate metric attributes.
PolicyExecutionLogged version 1 uses the optional receipt properties
operationClass, elapsedMs, and reconciliationState for append diagnostics.
Historical version-1 receipts without those properties remain valid. The first
Production release that both reads and persists the expanded shape is commit
13ce8501d6c98a17491220d22cc207a88f53606b. That exact release is the permanent
rollback floor for expanded receipts: never deploy or roll back a reader below
it, because earlier strict readers cannot replay expanded version-1 events.
Recovery-loop tracking is independent of ordinary __cascadeDepth, so a deep
valid saga can still invoke its first configured onError. That command and
its downstream events carry a stable __onErrorLineage and
__onErrorDepth: 1 (plus __isOnError for compatibility). Runtime currently
allows one recovery boundary. A later failure in the same lineage does not
launch another onError; durable fan-out instead records terminal
fan-out errorCode: ON_ERROR_SUPPRESSED evidence. Before that terminal is
committed, ErrorLog durably records reason RECOVERY_DEPTH_LIMIT_REACHED, the
target command, lineage, and observed/maximum recovery depths under a stable
diagnostic identity. Execution diagnostics expose the terminal marker
separately from compensation.
The complete immutable onError intent is persisted in a bounded, versioned
AES-256-GCM envelope. The global cron tick durably rotates through a bounded
window of authoritative Production and Workspace runtime scopes, then scans the
canonical latest pending lifecycle under each effective app ID. Runtime loading
starts only after an authoritative candidate page contains due compensation;
empty or incomplete pages do not require a published model. Failures loading a
runtime for due work remain visible and leave the compensation pending. It re-reads the
stream and claims expired leases without waiting for the original worker, and
it never replays fan-out children or substitutes Production config for a
Workspace.
If exact Workspace materialization reports typed not-found, an expired or
missing unstarted compensation is terminalized with WORKSPACE_EXPIRED or
WORKSPACE_UNAVAILABLE. A started attempt is never dispatched again and uses
DISPATCH_OUTCOME_UNKNOWN. Transient storage failures, invalid Workspace
config, and missing or incorrect CREDENTIAL_ENCRYPTION_KEY material fail
closed and leave compensation pending. Drain or explicitly resolve pending
compensations before rotating that key; pending intent does not currently
support a multi-key or key-ID fallback.
Batch commands
Batch command execution is useful for controlled automation. A batch declaration
is homogeneous: it contains one commandType and many item payloads. It does not
encode a mixed command sequence or guarantee that a final item acts as a
completion barrier. Segment those workflows explicitly and advance the modeled
stage only after the preceding batch reaches a terminal outcome whose progress
and dead-letter evidence satisfies the business completion rule. Use this
operational isolation for homogeneous high-volume work even when it fits below
the 100-command inline ceiling. Continue using explicit staged completion for
mixed/final synchronization; the reusable durable barrier is future work
tracked in #3005.
Keep batches bounded and give target commands stable business identities when retries must be deduplicated. Batch tracking does not itself make commands idempotent, promise that the triggering event plus all job/item records share one atomic transaction, or make successive progress reads an isolated snapshot. Each processing claim is ownership-fenced. Crash recovery retries only claims that provably never began execution; a started claim without durable outcome proof becomes an explicit dead letter and must be reconciled before deliberate operator replay. A thrown/ambiguous command result is never retried blindly. Before a replacement processor publishes a running lease, it takes a durable yielding lease and settles every stale claim from the expired owner. This same admission boundary applies to an operator resume, so resumed work cannot advance alongside claims left behind by its previous processor.
Native batch item diagnostics use five payload-free effect states:
not_started, rejected, rolled_back, committed, and outcome_unknown.
Each new attempt carries a deterministic execution ID and canonical request
hash that activates EventStore idempotency. After a lost response, only a
complete canonical receipt matching the execution ID, request hash, and
command type can settle success. An absent or unavailable receipt remains
unknown, including a zero-event command’s lost response. Confirmed rollback
from stream, tag, and criteria appends is retryable.
get_batch_job and list_batch_jobs expose stored cancelled jobs as
legacy_cancelled_unsettled. This means cancellation control is recorded but
there is no immutable terminal census. Pending items and unknown attempts
still require reconciliation; the cancelled flag is not no-effect evidence.
The Design MCP list_batch_job_items tool pages stable item IDs/indexes,
technical identity hashes, attempt/category/error fields, execution and decision
receipts, reconciliation references, and replay eligibility without returning
command data, scope values, raw errors, or business event data. Inspection is
fenced to the tenant, effective app, and selected Production or Workspace
source.
For an outcome_unknown dead letter, reconcile the execution reference against
canonical command/event/projection evidence. Record the conclusion with
record_batch_item_effect_verification, echoing the item hash, replay generation,
attempt, and command execution ID when present: effect_absent makes that exact
attempt eligible for replay, while effect_committed permanently blocks it. Then call
replay_dead_letter_items with exact item IDs and technical identity hashes.
Bulk replay preflights the complete selection and makes no changes if any item
is ambiguous or committed.
For a trusted custom_code policy that always returns exactly one homogeneous
batch, set typeConfig.durableRecovery to native_batch_v1 and declare a
finite, non-empty targetCommands set. This opt-in binds the completed batch
manifest to the durable source-policy work ID and registers its Production or
Workspace runtime in a dedicated recovery catalog. In the isolated production
topology, a minutely deduplicated runtime-worker producer assigns the manifest
to a long-lived, lease-fenced recovery worker. That owner is independent of Cron
lane deadlines and can honor the full per-item attempt budget; inline-only
deployments retain a fair alternating Cron fallback. The custom handler remains
an opaque, at-most-once boundary:
uncertainty before a complete manifest requires reconciliation, while
post-manifest recovery uses the Batch claim fences described above. Configured
onError remains supported.
Projections
Projection endpoints read current state. They support filtering, sorting, pagination, and output shaping according to the projection contract.
Projection delivery for durable runtime events is recoverable per source occurrence. Each projection handler atomically commits its document changes and an exact settlement receipt. If a transient PostgreSQL failure interrupts a phase, the matching runtime revision reloads the canonical event, skips handlers that already settled, and retries only the missing projection handlers. It does not rerun the command or publish post-event policies. Production and each Workspace use separate recovery and receipt scopes.
Recovery is bounded and fail-closed. Deterministic handler failures, exhausted retries, an unavailable source row, or an ended Workspace become poisoned operational state for diagnosis; Cessy does not infer projection success from elapsed time. A read immediately after command success can therefore still lag while recovery is pending.
Policy projection callbacks read the exact active Production or Workspace source and never substitute Production for a Workspace. Their bounded, value-free diagnostics record both successful and failed callback outcome, phase, timing, remaining budget, response reach/status, and query shape. Callbacks also carry the worker’s exact runtime-config revision. Invalid app ownership, unknown projection, stale worker snapshot, and transient app/runtime registry failures therefore produce distinct retry and reconciliation guidance.
See Projection Queries.
Agent discovery
Runtime clients can discover the safe agent catalog before opening a chat:
The response contains only stable public metadata:
Prompts, models, MCP configuration, credentials, container settings, provider
secrets, and private scope configuration are not part of this response. Use the
returned id as agentId in POST /channels/dm only when that agent is already
published. A Workspace-selected catalog can also include preview-only agents;
promote those agents before opening a DM. The catalog uses normal Runtime REST
authentication; the channel call itself has the stricter user-principal
requirement described below.
Channels
Runtime REST exposes channel endpoints for user-delegated chat experiences, including partner BFFs and custom frontends. Every channel and user-channel-view operation requires an OAuth/JWT bearer token that resolves to the authenticated human user. API keys and agent service tokens are rejected. The endpoints apply app access checks and only expose channels tied to agents from the target app.
Common channel calls:
Every successful POST /channels/dm creates a fresh human-to-agent conversation
channel and linked session and returns 201 with created: true. Repeating the
call creates another conversation; it does not deduplicate by user and agent.
The supplied agentId must exist in the published app configuration even if
the request selects a Workspace runtime source.
To continue a prior conversation, retain its returned channel id or select it
from the user’s channel views and post through that channel’s messages endpoint.
Use /channels/{channelId}/read and /user-channel-views for inbox state.
Channel endpoints are not a replacement for modeled commands. Business mutations should still execute commands; channel messages should explain or coordinate the user-visible work.
Private turn context
POST /channels/dm and POST /channels/{channelId}/messages accept an optional
agentTurnContext object. Use it when the visible user message should remain
natural, but the agent needs private orientation for this turn, such as the
current product surface, focused document id, draft/published status, projection
pointers, write/proposal constraints, or small namespaced extensions for
builder-specific hints. agentSessionContext is accepted as a compatibility
alias; send only one field.
The context is not returned by channel message APIs and is not copied into the
public channel log. It is injected privately for the associated agent wake and
monitoring evaluation, with scope: "next_message_only".
For POST /channels/dm, provide it only when the request also posts content;
createOnly DM opens do not have an associated turn.
permissions inside the context are orientation only. Runtime authorization,
delegation, domain commands, and explicit user action requirements still
control reads and writes.
Event import
Event import is for migrations, repairs, and controlled operational workflows. Normal user behavior should execute commands.
Ordinary cascade depth (__cascadeDepth), recovery markers (__isOnError,
__onErrorDepth, __onErrorLineage), and the internal compatibility marker
__legacyRecoveryOriginUnknown are platform-owned metadata and are rejected at
the public import boundary.
Projection rebuild
Projection rebuild reprocesses event history through a replacement read model for projection definition changes, migrations, and deliberate bulk repair. Transient delivery gaps for unchanged durable projections use automatic occurrence recovery instead. Verify output after a rebuild and communicate production impact before running it on large event histories.
Adapter call route
The adapter call route exists for controlled platform workflows. Treat it as platform-managed support, not as the normal public app API. External consumers should use commands, projections, webhooks, subscriptions, or a BFF.