Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.43.0.
Registry Coordinator serves one HTTP surface for starting a run of a served workflow, following and
recovering that run, operating the deployment, and taking Casework completion wake-ups.
Every route except /health, /ready, /v1/review-completions, and
/v1/events/{workflow}/{trigger} takes a caller access token, and the caller policy that token
resolves to decides what the caller may do. A Casework completion authenticates with its
completion token, and a product event with its source’s delivery signature. The contract is
pre-1.0 and carries no frozen compatibility promise.
Open the Registry Coordinator API operations for every route, parameter, schema, and reachable problem code.
Contract boundary
Section titled “Contract boundary”Every caller request presents Authorization: Bearer <token> holding an RFC 9068 access token,
typ: at+jwt, signed with RS256 or ES256 by the one issuer the deployment names, for its audience,
to an exact client keyed in authentication.policies, with a lifetime of at most one hour. The token’s scopes
are read from registry_scopes unless the deployment sets scopeClaim. A missing, malformed, or
unverified token answers 401 authentication.refused with WWW-Authenticate: Bearer.
Each exact verified client identity resolves its keyed caller policy, which lists the scopes it requires and the actions it allows:
- The run actions
start,status,inspect,retry-same,cancel, andreconcilereach only the workflows the policy lists inworkflows. - The deployment-wide actions
doctor,restore-hold,release-restore-hold,release-admission-hold,complete-execution-recovery, andretainact on every workflow at once and needoperator: trueon the policy.
A resolved caller refused an action answers 403 operation.not-authorized. Run ownership and the
idempotency scope come from the token’s exact verified iss and sub, never from input fields;
input identifiers and grant references manufacture no caller authority. A caller allowed to start
a run gains no authority over the products its workflow calls: each call uses the identity the operator
configured for that connection, and the receiving product decides it.
Discover a workflow
Section titled “Discover a workflow”GET /v1/workflows/{workflow} returns the active permitted contract, including workflowRevision,
input, and declared outcomes. It requires the caller’s start action for that Workflow.
Use that revision for fresh starts or a generated revision-pinned TypeScript/Python wrapper.
Generated wrappers provide typed start, get, and cancel over the maintained public generic
clients; they validate returned workflow identity and declared outcome names. They add no authority.
Start a run
Section titled “Start a run”POST /v1/runs takes a Start body with exactly three members: workflow, its name of 1 to 128 characters;
workflowRevision, the expected computed revision; and input, checked against the declared schema. It requires an
Idempotency-Key header of 1 to 256 bytes. An admitted start answers 200 with the run’s status,
admitted under the named workflow’s active definition only when its revision matches.
The same key from the same verified issuer and subject returns the original run for the
same workflow revision and input. A conflicting revision or input answers
409 idempotency.key-reused. Retain the exact original revision during unknown-admission recovery,
even if activation changes. A new workflow version
cannot reinterpret a repeated key. A fresh key is a fresh run, so a client that does not know
whether a start was admitted resends it under its original key.
A start is refused before admission when:
- the caller’s policy does not list the workflow, with
403 operation.not-authorized; - the policy lists the workflow but the deployment does not serve it, with
404 workflow.not-served; - the expected revision differs from the active revision, with
409 workflow.revision-mismatch; - the input does not satisfy the workflow’s input schema and value bounds, with
400 input.invalid; - the instance runs in recovery-only mode, with
409 admission.recovery-only; - a restore left admissions held, with
409 admission.held.
Start a run from a product event
Section titled “Start a run from a product event”POST /v1/events/{workflow}/{trigger} admits a run from a signed product event, when the workflow
declares that trigger and the runtime file lists its source under eventSources. The body is the
platform hook envelope, at most 1,048,576 canonical JSON bytes, above which it answers
413 request.body-too-large; the admitted event value must then fit the 131,072-byte value
profile, or it answers 413 event.too-large. The X-Registry-Signature header carries the HMAC delivery signature over
the envelope, the signed delivery headers, the method, and the exact request target; the
ce-* headers must match the body and the trigger’s authored source, event type, and data
schema. The delivery time must be within 30 seconds of the Coordinator’s clock, so a producer
signs each delivery attempt afresh.
The exact source, event identity, and trigger bind admission across retries: a redelivered event
returns the run it already admitted, and the run belongs to the authenticated source, not to a
caller. An admitted event answers 204 with an empty body and an X-Coordinator-Run-Id header.
An unknown workflow or trigger, or a signature that does not verify, answers
401 event.unauthenticated, a malformed envelope 400 event.invalid, and a source whose key is
not configured or cannot be read 503 event.source-unavailable. The event grants no authority for
the calls the run then makes.
Read and inspect runs
Section titled “Read and inspect runs”GET /v1/runs/{run} answers the run’s RunStatus: its workflow, computed workflowRevision,
bindingDigest, its current step and state, when it was admitted and when
its deadline falls, whether an effect is uncertain, and whether it needs restore review.
cancelRequested records ordinary stop cancellation; authoredCancelRequested records cancellation
that took a completion wait’s authored cleanup path. A finished run adds its outcome and the reviewed output its
workflow declares. A failed run adds its failureCode, and failurePath when the failure concerns a
call input: an RFC 6901 pointer into that input, never the value.
A run that does not exist, a run another caller owns, and a run in a workflow the caller’s policy does
not list all answer the same 404 run.not-visible. An operator policy reaches every owner’s runs
in the workflows it lists.
GET /v1/runs answers at most 100 of the most recently admitted runs the caller may see, newest
first. limit takes 1 to 100 and defaults to 20; any other value answers
400 request.limit-out-of-range. Optional state accepts running, finished, failed,
cancelled, cancelled-after-effect, expired, attention, or cancel-requested.
attention=true selects effective states attention and failed; attention=false excludes both.
Filters compose. Results are ordered by admission time and run ID, newest first. When the response
includes nextCursor, pass it as cursor with the same filters to continue. Every page reapplies
current caller and workflow permissions. An invalid cursor or one from a different caller or filter
answers 400 request.invalid. Keep the run ID a start returns for direct status lookup.
GET /v1/runs/{run}/inspect answers the run, one entry per step with its state, attempt count,
whether its command is prepared, whether its effect is uncertain, and whether its receipt expired,
and a recovery block. recovery.retryAllowed and recovery.reason describe whether the run can
be scheduled again. For a compatible current call, recovery.operation names the pinned operation’s
ID, version, product, effect, key and preparation requirements, recovery semantics, and
readReceipt capability. Neither proves current product authority or that an original-key receipt
exists. No status, list, or inspection answer carries original inputs, raw product responses, or prepared
commands. Terminal output is only the authored outcome projection.
Inspection also includes effects with accepted or unknown state and pinned Operation revisions,
and waits with their accepted step, named completion, completion revision, timeout, next
observation, durable floor, observation count, and limit. Protected correlation values and
commands remain absent. A wait’s blocked or uncertain evidence cannot become a business-failure
outcome. Persistent read floors and original deadlines survive wake-up, restart, and recovery.
Recovery and operator requests
Section titled “Recovery and operator requests”Every recovery and operator POST carries a JSON body:
cancel,reconcile,retry-same,restore-hold, andrelease-restore-holdtake aReason, whosereasonis a bounded investigation reference of 1 to 256 characters without personal data, such asincident-2026-10-09. The audit journal records a keyedreasonRef, never the reference./v1/retentiontakes aRetention: abeforecutoff in the past and alimitof 1 to 100. It answers how many payloads iterased./v1/release-admission-holdtakes anAdmissionRelease: arecoveryReferenceand the two attestationsadmissionHistoryCompleteandpriorDeploymentFenced. A false or omitted attestation refuses the release./v1/complete-execution-recoverytakes anExecutionRecovery: arecoveryReferenceand the attestationsexecutionHistoryCompleteandpriorDeploymentFenced.
The attestations are audited operator statements, not automated proof. A cancel answers the run’s
status, a reconcile its inspection, and a retry-same the run ID and its scheduled status.
An authorized cancellation of a parked completion wait with onCancel takes that authored path
durably. A leased result read settles or expires before the path advances. Repeating cancellation
preserves the cleanup, which can withdraw the original Casework request under current producer
authority and the original deadline. Without that edge, or after the wait advanced, ordinary
cancellation stops future calls where possible. It never reverses a product effect already accepted.
These routes authenticate and authorize before they report a malformed body. A body is
application/json or another application media type ending in +json; any other media type
answers 415 request.unsupported-media-type, after authorization. Bodies are bounded to 65,536
bytes, and duplicate or unknown members are refused. An empty, overlong, or control-character
reason answers 400 request.reason-invalid, and an out-of-bounds cutoff or limit
400 request.retention-invalid. State and recovery conflicts answer 409.
Review completions
Section titled “Review completions”POST /v1/review-completions takes Casework’s completion events for a review producer connection.
It authenticates the sender by the Authorization: Bearer completion token and the
Registry-Recipient-Binding header together, comparing the token in constant time, and refuses
any other sender with 401 authentication.refused before reading the body. Without a configured
completion block on the connection, it refuses every event.
An authenticated event is application/json of at most 2048 bytes, with type, eventId,
requestId, resultId, and completedAt. It repeats its eventId as the Idempotency-Key
header, and its completedAt is no older than 365 days and no more than five minutes ahead of the
Coordinator’s clock. Anything else answers 400 request.invalid or
415 request.unsupported-media-type.
Every authentic, well-formed event answers 204, whether or not a run waits on its request. The
event only brings forward the next result read of a run already waiting on that request: the run
reads the result itself through the connection’s producer credential. An event starts no run.
503 service.unavailable asks Casework to deliver the event again.
Problem documents and codes
Section titled “Problem documents and codes”Every refusal, including a request refused before its route runs and a refused completion event, is
an RFC 9457 application/problem+json document with type, title, status, detail, code,
and traceId. The type is the code with its dot replaced by a slash under
https://id.registrystack.org/problems/registry-coordinator/. The title and detail are fixed
sentences that belong to the code, and no member repeats a submitted value. A refusal carries
Cache-Control: no-store and a traceparent header whose trace matches traceId; a 401 also
carries WWW-Authenticate: Bearer, and a 503 carries Retry-After.
Every problem response carries one code from this closed catalogue, answered with the HTTP status
listed beside it. request.not-found answers a path the contract does not declare, and
request.unprocessable a body the HTTP framework rejects before an operation reads it, so no
operation lists either. coordinatorctl reports its own diagnostics with coordinator.* codes,
which this catalogue does not hold.
| Code | Status | When |
|---|---|---|
admission.held | 409 | Starts stay held after a restore until an operator releases the admission hold. |
admission.recovery-only | 409 | This instance runs in recovery-only mode and admits no runs. Send starts to the ordinary service. |
audit.response-unavailable | 503 | The operation may have completed, but its audited result could not be released. Inspect the run before repeating the operation. |
authentication.refused | 401 | The bearer credential is missing, invalid, or expired. |
binding.conflict | 409 | The run's original connection settings differ from this service's runtime configuration. |
binding.live-conflict | 409 | Connection settings that runs in progress use differ from this service's runtime configuration. |
definition.refused | 409 | The workflow definition, one of its mappings or one of its functions refused the request. |
deployment.activation-changed | 409 | An apply changed the active packages after this service started. Restart it with the runtime file that apply used. |
deployment.refused | 409 | The deployment's activation, runtime configuration or state key custody check failed. |
evaluation.uncertain | 409 | An evaluation may have completed at its provider, so the run is never evaluated again. |
event.invalid | 400 | The event envelope, source, type, schema, or trigger mapping does not match the authored contract. |
event.source-unavailable | 503 | The configured event source key cannot be read. Restore that binding before redelivery. |
event.too-large | 413 | The signed event exceeds the event value profile: 131072 canonical bytes, 32 levels of nesting, 4096 values, 256 items per array, 256 members per object, and 16384 bytes per string. Project fewer or smaller fields into the event. |
event.unauthenticated | 401 | The delivery signature, signed metadata, or delivery time cannot be verified. |
execution.held | 409 | Execution is under restore hold until an operator releases it. |
workflow.not-served | 404 | This deployment does not serve the named workflow. |
workflow.revision-mismatch | 409 | The expected workflow revision is not active. Discover the active contract before a new start; preserve the original revision for unknown-admission recovery. |
idempotency.key-invalid | 400 | Supply an Idempotency-Key header of 1 to 256 bytes. |
idempotency.key-reused | 409 | This idempotency key already names a different workflow revision or input. |
input.invalid | 400 | The start input does not satisfy the workflow's declared input schema and value bounds. |
operation.not-authorized | 403 | The caller's policy does not allow this operation. |
receipt.expired | 409 | The original command's receipt can no longer be recovered. This is not evidence that the command was not sent. |
reconcile.raced | 409 | The run's progress changed while it was being reconciled. Inspect it again. |
reconcile.refused | 409 | Reconcile applies only to a held mutation with a prepared command, its original binding and no active lease. |
reconcile.unsupported | 409 | The held operation has no original-result lookup. |
request.body-too-large | 413 | The request body exceeds the accepted size: 65536 bytes for a JSON request, or 1048576 bytes for a signed event delivery. |
request.invalid | 400 | The request could not be read as a Coordinator request. |
request.limit-out-of-range | 400 | The list limit must be between 1 and 100. |
request.method-not-allowed | 405 | The route exists but not for this method. |
request.not-found | 404 | The requested route does not exist. |
request.reason-invalid | 400 | The reason or recovery reference must be a bounded reference without personal data or control characters. |
request.retention-invalid | 400 | The retention cutoff must be in the past and the limit between 1 and 100. |
request.unprocessable | 422 | The request body could not be processed. |
request.unsupported-media-type | 415 | The request body is not JSON. |
restore.hold-required | 409 | This recovery action requires execution to be under restore hold. |
restore.lease-incompatible | 409 | Held work no longer matches its protected definition or prepared command. |
restore.review-required | 409 | Execution history must be complete and the prior deployment fenced before recovery is reviewed. |
restore.unresolved | 409 | Held work remains unresolved, so the restore hold cannot be released. |
retry.refused | 409 | Retry-same applies only to held work before its original deadline. |
run.definition-incompatible | 409 | This service's Coordinator version cannot interpret the run's pinned definition. |
run.not-visible | 404 | No run with this identifier is visible to the caller. |
run.payload-erased | 409 | Retention erased this run's payloads, so it can no longer be retried or reconciled. |
runtime.failure | 500 | Coordinator could not complete the request. |
service.unavailable | 503 | A dependency of the Coordinator, such as its database, audit stream or token verifier, is unavailable. Try again after the service recovers. |
Routes
Section titled “Routes”| Method | Path | Authentication | Purpose |
|---|---|---|---|
GET | /health | None | 200 with {"live":true} while the process answers. |
GET | /ready | None | 200 with {"ready":true}, or 503 with {"ready":false} while the service cannot accept work. |
GET | /v1/workflows/{workflow} | Bearer, start on the named workflow | Discover the active revision, input schema, and declared outcomes. |
POST | /v1/runs | Bearer, start on the named workflow | Start one run with the expected workflowRevision and an Idempotency-Key. |
GET | /v1/runs | Bearer, status | List at most 100 of the most recently admitted runs the caller may see, newest first. |
GET | /v1/runs/{run} | Bearer, status | Read one run's progress and its reviewed terminal output. |
GET | /v1/runs/{run}/inspect | Bearer, inspect | Read bounded steps, accepted or unknown effects, durable wait budgets, and available recovery actions. |
POST | /v1/runs/{run}/cancel | Bearer, cancel | Stop future dispatch where possible, without rolling back an accepted effect. |
POST | /v1/runs/{run}/reconcile | Bearer, reconcile | Settle an uncertain call from the receiving product's original-key receipt. |
POST | /v1/runs/{run}/retry-same | Bearer, retry-same | Schedule held work again with its original command, key, binding, and authority. |
GET | /v1/doctor | Bearer, operator | Report activation, audit readiness, holds, and run counts. |
POST | /v1/restore-hold | Bearer, operator | Place the restore hold, which stops dispatch and admissions. |
POST | /v1/release-restore-hold | Bearer, operator | Release the restore hold once no held work is unresolved. |
POST | /v1/release-admission-hold | Bearer, operator | Reopen admissions after a restore, with audited attestations. |
POST | /v1/complete-execution-recovery | Bearer, operator | Attest complete execution history and prior fencing while the restore hold remains. |
POST | /v1/retention | Bearer, operator | Erase eligible terminal payloads that ended before a cutoff. |
POST | /v1/review-completions | A Casework connection's completion token and recipient binding | Bring forward the next result read of a run waiting on that review request. |
POST | /v1/events/{workflow}/{trigger} | Configured event source signature | Admit one run per exact authenticated event and trigger; exact redelivery returns the same run. |
GET /metrics is not part of this surface. When the deployment configures a metrics listener, the
metrics are served on that separate socket only, and the service listener has no metrics route.
coordinatorctl openapi prints the OpenAPI document offline.
- Registry Coordinator API operations for every route, parameter, and schema.
- Author a Coordinator workflow for the workflows a deployment serves.
- Deploy Registry Coordinator for the runtime, its database, its caller policies, and the operator commands.