Skip to main content
An operating process records a repeatable workflow — its purpose, stages, handoffs, and completion contract — as an event-sourced aggregate. Reads replay the workspace ledger; writes are commands with an idempotency key and an expected aggregate version, so concurrent writers conflict loudly instead of silently overwriting each other. The process itself is a thin header; the full workflow definition lives in an immutable process spec revision attached to it.

Wire shape

Process fields (camelCase on the wire):
Both process and revision are strict, and every field in them is required. There are no optional fields. A nullable field must still be present as an explicit null, an array field must be present as [], and an unrecognized key is a 400 rather than being ignored. This is the single most common reason a hand-written proposal is rejected.
Every field above is required on process. Nullable ones (domainRef, currentRevisionRef, accountableResponsibilityLinkRef) must be sent as null. A ref is exactly { "id": "<uuid>", "workspaceId": "<uuid>" } — no other keys — and every ref’s workspaceId must equal process.workspaceId. schemaVersion must match 1.<minor>.<patch>. Timestamps are ISO 8601.

Revision fields

revision (the spec revision) is likewise strict with every field required: Each entry in stages[] requires all of: stageKey (lowercase identifier matching ^[a-z][a-z0-9_-]*$), name, purpose, entryConditions (array of JSON objects), exitConditions (array of JSON objects), responsibleLinkRefs, requiredCapabilityRefs, optionalSkillRefs (arrays of refs), allowedCommandTypes (array of strings), authorityClassobserve prepare gated_side_effect autonomous_side_effect, expectedDuration (JSON object or null), and exceptionRoutes (array of JSON objects). Each entry in handoffs[] requires all of: handoffKey (same identifier pattern), fromStageKey, toStageKey, acceptingLinkRef (ref or null), sla (JSON object or null), and escalation (JSON object or null). As with process, every ref anywhere inside revision must carry the same workspaceId as the revision itself.

A proposal that validates

Replace contentDigest with the real sha256: digest of your revision content before proposing anything you intend to keep.

Lifecycle

Declared states: discovered, proposed, confirmed, shadowing, active, changing, deprecated, retired. Transitions exposed by the REST API: changing, deprecated, and retired remain valid state values, but REST does not expose transitions into them. Treat active as the terminal state reachable through this API and propose a new revision when the workflow changes.

Operations

All routes accept Authorization: Bearer oxk_... or a web-app session, and every mutation requires an Idempotency-Key header — a missing one is 400 missing_idempotency_key.
Mutations return 201 (or 200 when the idempotency key replays) with { "data": { "processId", "transition", "ledger", "aggregateVersion" }, "meta": { ... } }. A stale expected_aggregate_version returns 409 operating_process_conflict.

Where the IDs come from

The easiest way to get a well-formed proposal is not to hand-write one: run a discovery run and call its propose operation, which materializes an observed process card into a confirmation-ready proposal.

MCP equivalents

No MCP tool writes operating processes today — this resource is REST-first. The wizard’s map command drives the discovery → propose flow over these same endpoints.