> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useorgx.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Operating process

> The operating-process resource and its spec revisions: propose, confirm, and activate repeatable workflows with expected-version control.

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):

| Field                              | Type                               | Meaning                                              |
| ---------------------------------- | ---------------------------------- | ---------------------------------------------------- |
| `schemaVersion`                    | string                             | Contract version.                                    |
| `id`                               | UUID                               | Process ID. Supplied by the caller at proposal time. |
| `workspaceId`                      | UUID                               | Tenant scope; all nested refs must match it.         |
| `name`, `purpose`                  | string                             | What the process is and why it exists.               |
| `valueRecipientRefs`               | array of refs                      | Who receives the value.                              |
| `domainRef`                        | ref, nullable                      | Business domain.                                     |
| `lifecycleState`                   | enum                               | See lifecycle below.                                 |
| `currentRevisionRef`               | ref, nullable                      | The active spec revision.                            |
| `accountableResponsibilityLinkRef` | ref, nullable                      | Accountable owner link.                              |
| `provenanceMode`                   | `declared` `observed` `reconciled` | How the process was learned.                         |
| `confidence`                       | number 0–1                         | Evidence confidence.                                 |
| `tags`                             | string\[]                          | Labels.                                              |
| `createdAt`, `updatedAt`           | ISO datetime                       | Timestamps.                                          |

<Warning>
  **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.
</Warning>

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:

| Field                       | Type                                                                                                                 |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion`             | string, `1.<minor>.<patch>`                                                                                          |
| `id`                        | UUID — you generate it                                                                                               |
| `processId`                 | UUID — must equal `process.id`, else `409 operating_process_scope_mismatch`                                          |
| `workspaceId`               | UUID — must equal the authed workspace                                                                               |
| `revisionNumber`            | positive integer (a first proposal is `1`)                                                                           |
| `contentDigest`             | `sha256:` + 64 lowercase hex characters                                                                              |
| `authoredBy`                | actor: `{ type, id }`, `type` ∈ `human` `agent` `service` `external_agent` `system`                                  |
| `ratifiedBy`                | array of actors — `[]` if none                                                                                       |
| `effectiveFrom`             | ISO datetime                                                                                                         |
| `effectiveTo`               | ISO datetime or `null`                                                                                               |
| `triggerSpec`               | JSON object — `{}` if unspecified                                                                                    |
| `intendedTransformation`    | strict object: `inputState` (JSON object), `outputState` (JSON object), `valueStatement` (non-empty string)          |
| `stages`                    | array, **at least one** stage (shape below)                                                                          |
| `handoffs`                  | array of handoffs — `[]` if none                                                                                     |
| `completionContract`        | JSON object                                                                                                          |
| `exceptionContracts`        | array of JSON objects                                                                                                |
| `systemRefs`                | array of refs                                                                                                        |
| `capabilityRefs`            | array of refs                                                                                                        |
| `skillRefs`                 | array of refs                                                                                                        |
| `policyRefs`                | array of refs                                                                                                        |
| `authorizationRequirements` | array of JSON objects                                                                                                |
| `budgetRef`                 | ref or `null`                                                                                                        |
| `measurementPlan`           | JSON object                                                                                                          |
| `adoptionPlan`              | JSON object                                                                                                          |
| `migrationState`            | JSON object or `null`                                                                                                |
| `provenance`                | strict object: `declaredClaimRefs` (refs), `observedVariantRefs` (refs), `reconciliationDecisionRef` (ref or `null`) |
| `limitations`               | array of non-empty strings                                                                                           |

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), `authorityClass` ∈ `observe`
`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

```bash theme={"dark"}
curl https://useorgx.com/api/v1/operating-processes \
  -X POST \
  -H "Authorization: Bearer $ORGX_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: propose-intake-process-001' \
  -d '{
    "workspace_id": "3d7e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
    "process": {
      "schemaVersion": "1.0.0",
      "id": "5e8f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
      "workspaceId": "3d7e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
      "name": "Inbound lead intake",
      "purpose": "Qualify every inbound lead within one business day.",
      "valueRecipientRefs": [],
      "domainRef": null,
      "lifecycleState": "proposed",
      "currentRevisionRef": null,
      "accountableResponsibilityLinkRef": null,
      "provenanceMode": "declared",
      "confidence": 0.6,
      "tags": [],
      "createdAt": "2026-08-19T12:00:00Z",
      "updatedAt": "2026-08-19T12:00:00Z"
    },
    "revision": {
      "schemaVersion": "1.0.0",
      "id": "7a9b0c1d-2e3f-4a5b-6c7d-8e9f0a1b2c3d",
      "processId": "5e8f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
      "workspaceId": "3d7e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
      "revisionNumber": 1,
      "contentDigest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
      "authoredBy": { "type": "human", "id": "user_2abc" },
      "ratifiedBy": [],
      "effectiveFrom": "2026-08-19T12:00:00Z",
      "effectiveTo": null,
      "triggerSpec": {},
      "intendedTransformation": {
        "inputState": {},
        "outputState": {},
        "valueStatement": "An unqualified lead becomes a routed, qualified lead."
      },
      "stages": [
        {
          "stageKey": "triage",
          "name": "Triage",
          "purpose": "Decide whether the lead is in ICP.",
          "entryConditions": [],
          "exitConditions": [],
          "responsibleLinkRefs": [],
          "requiredCapabilityRefs": [],
          "optionalSkillRefs": [],
          "allowedCommandTypes": [],
          "authorityClass": "prepare",
          "expectedDuration": null,
          "exceptionRoutes": []
        }
      ],
      "handoffs": [],
      "completionContract": {},
      "exceptionContracts": [],
      "systemRefs": [],
      "capabilityRefs": [],
      "skillRefs": [],
      "policyRefs": [],
      "authorizationRequirements": [],
      "budgetRef": null,
      "measurementPlan": {},
      "adoptionPlan": {},
      "migrationState": null,
      "provenance": {
        "declaredClaimRefs": [],
        "observedVariantRefs": [],
        "reconciliationDecisionRef": null
      },
      "limitations": []
    }
  }'
```

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:

| Transition | From                            | To          | Operation                                               |
| ---------- | ------------------------------- | ----------- | ------------------------------------------------------- |
| Propose    | (new), `discovered`, `proposed` | `proposed`  | `POST /api/v1/operating-processes`                      |
| Confirm    | `proposed`                      | `confirmed` | `POST /api/v1/operating-processes/{processId}/confirm`  |
| Activate   | `confirmed`, `shadowing`        | `active`    | `POST /api/v1/operating-processes/{processId}/activate` |

`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`.

| Operation       | Method + path                                           | Body / params                                                                                             |
| --------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Propose         | `POST /api/v1/operating-processes`                      | `workspace_id`, `process` (full shape above), `revision` (full spec revision), `correlation_id?`          |
| List            | `GET /api/v1/operating-processes`                       | `workspace_id`, `limit` (default 50, max 200; `meta.truncated` signals a capped replay)                   |
| Get             | `GET /api/v1/operating-processes/{processId}`           | `workspace_id`. Returns `{ process, revisions, observedEpisodeIds, variantIds, aggregateVersion }`        |
| Confirm         | `POST /api/v1/operating-processes/{processId}/confirm`  | `workspace_id`, `expected_aggregate_version`, `correlation_id?`                                           |
| Activate        | `POST /api/v1/operating-processes/{processId}/activate` | `workspace_id`, `expected_aggregate_version`, `correlation_id?`                                           |
| Map view        | `GET /api/v1/operating-map`                             | Derived map projection over processes and discovery sessions.                                             |
| Update / delete | Not exposed                                             | The process header and revisions are immutable through REST; propose a new revision via a fresh proposal. |

```bash theme={"dark"}
curl https://useorgx.com/api/v1/operating-processes/5e8f2a3b-4c5d-6e7f-8a9b-0c1d2e3f4a5b/confirm \
  -X POST \
  -H "Authorization: Bearer $ORGX_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: confirm-intake-process-001' \
  -d '{
    "workspace_id": "3d7e4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a",
    "expected_aggregate_version": 1
  }'
```

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

| Field                        | Obtain it from                                                                                                               |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `processId`                  | You generate `process.id` (UUID) when proposing — or it arrives from a [discovery-run proposal](/docs/api/entities/discovery-run) |
| `expected_aggregate_version` | `GET /api/v1/operating-processes/{processId}` response `data.aggregateVersion` (a fresh proposal is `1`)                     |
| `revision.id`                | You generate it (UUID) alongside the process at proposal time                                                                |
| `workspace_id`               | `GET /api/v1/me` → `data.default_workspace_id` or `data.workspaces[].id`                                                     |
| `contentDigest`              | You compute it: `sha256:` + hex digest of the revision content                                                               |

The easiest way to get a well-formed proposal is not to hand-write one:
run a [discovery run](/docs/api/entities/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.
