Change
Included from Basic tier
A change is one execution of an action against a node (a project, environment
or asset): the run record, its step tree, its approval/pause/cancel audit
trail, and the git commit it ran from. JSON:API type: change.
The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.
Endpoints
| Route | Serves |
|---|---|
GET /api/changes | List, created_at desc then id desc. Default limit 100, max 10,000; a page trimmed at the limit answers 206 with meta.partial_response: true. |
GET /api/projects/{project}/…/changes | The same list scoped to a node subtree (the node-path router). |
GET /api/changes/{id} | Show. Default include=steps. Changes of a soft-deleted subtree are hidden from lists but still served by id. |
POST /api/changes (and the node-path variant) | Create — or retry, when attributes.retry is true and the body carries a change relationship naming the original. The body may carry an inputs object, validated against the action's declarations before the run exists; a named 422 points at inputs on a violation, and inputs are refused for actions with no declarative action file. 201 with the show document, steps included. |
POST /api/changes/plan (and the node-path …/changes/plan) | Plan without creating: the create body compiled and evaluated with no run row; 200 with a plan document — the step tree with tier-1 will_run/will_skip verdicts and tier-2 symbolic spans. Advisory: the run re-evaluates at step entry. |
DELETE /api/changes/{id} | Cancel: 204, or 422 Change cannot be cancelled because it is already finalised. |
POST /api/changes/{id}/pause / /resume | Pause/resume; 200 with the updated change, 422 named errors (already finalised, already paused, not paused). |
GET /api/changes/{id}/log_lines | The change's log surface. |
GET /api/changes/{id}/artefacts (alias /artifacts) | Artefacts published by the change's steps. |
GET /api/changes/{id}/converged_properties / /converged_settings | The converged documents the change ran with (see opschain docs resources/properties and opschain docs resources/settings). |
List filtering is filter[<attribute>_eq]=<value> over the serialized
attributes (any attribute below), plus two ransackable-scope predicates the
GUI's "Search by step" page sends: filter[step_action_or_name_in][]=<term>
(repeatable — keeps changes where ANY step of the change's whole tree has
that action or step name, exact but case-insensitive; the upstream
Change.step_action_or_name_in scope) and filter[created_at_lt]=<ts> (the
page's keyset bound, strict < over creation time). Any other predicate is a
400 unsupported filter. List meta carries authorisations
(update/delete/execute on the request path), has_any and
partial_response.
The activity feed (GET /api/activities)
The GUI's Activity screens and the CLI's changes list read changes and
workflow runs through the merged activity feed, which carries its own filter
vocabulary (each key the upstream Ransack predicate over the activity views'
columns; a key outside it is a 400 Unpermitted parameters):
| Filter | Meaning |
|---|---|
filter[action_present]=1 | Keeps rows whose action is not null. |
filter[g][][m]=or with filter[g][][parent_path_eq] / filter[g][][parent_path_start] | The grouped node-subtree scope: the node itself OR anything under it (_start carries the trailing /). |
filter[sorts][]=created_at desc | Overrides the default updated_at desc order. |
filter[activity_type_eq] | change or workflow_run — restricts the union to one arm. |
filter[id_eq] / filter[id_in][] | Exact id lookup / id set (text comparison over the canonical uuid). |
filter[free_text_cont_any] | Case-insensitive contains over each arm's free-text fields (id, project code, action, creator, status, metadata text; plus git remote/rev/sha on changes, workflow name/code and project name on runs). LIKE metacharacters in the term match literally. |
filter[created_at_gt/lt/gteq/lteq] | Creation-time bounds (also the GUI's keyset pagination). |
filter[updated_at_gt/lt], filter[started_at_gt/lt], filter[finished_at_gt/lt] | Root-step timestamp windows; a null timestamp matches no bound. |
filter[created_by_in][] | Creator usernames, case-insensitive, every provider's account of each name. |
filter[status_code_in][] | Serialized status codes (the vocabulary below). |
filter[scheduled_eq] / filter[paused_eq] | Boolean flags (paused is paused_at IS NOT NULL). |
filter[git_remote_name_cont] / filter[git_rev_cont] | Case-insensitive contains over the change's commit details; never matches a workflow run. |
filter[metadata_text_cont] | Case-insensitive contains over the row's metadata rendered as text. |
filter[workflow_code_eq] / filter[version_in][] | Workflow-run-only keys (never match a change): the workflow's code, the run's version set. |
filter[action_eq] | Exact, case-sensitive action match. |
filter[scheduled_activity_id_eq] | The owning scheduled activity (uuid, case-insensitive; an unparseable value matches nothing). |
filter[project_code_eq] / filter[project_code_in][] | The owning project's code (exact). |
A blank value is ignored rather than matched against (filter[action_eq]=
is a no-op), as upstream Ransack drops blank conditions. Sparse fieldsets
(fields[activity]=…) and limit (default 100, max 1000; a trimmed page
answers 206) apply as elsewhere.
Attributes
Timestamps are ISO8601 strings with six fractional digits. Many change
attributes are delegated from the root step (started_at, finished_at,
updated_at, state_timing, the approval/cancel audit attributes), and the
action/step_name pair comes from the change's action step (the root step
on a pre-approval schema).
| Attribute | Type | Meaning / origin / null semantics |
|---|---|---|
created_by | string | Username of the creator (the authenticated actor at POST). Never null. |
step_name | string | The action step's step name. |
action | string | The action the change runs (the action step's action). Set at create; immutable. |
build_without_cache | boolean | Request attribute frozen at create. DEPRECATED architecture-wide (owner 2026-10-18: no step-image builds) — accepted as a documented no-op for upstream payload parity. |
auto_continue_wait_steps | boolean | Request attribute frozen at create; wait steps continue without a user gate. |
initial_step_tree | object/null | The step tree known at creation; null until/unless discovery supplies one. |
metadata | object | Caller-supplied metadata, stored at create, plus the server-owned opschain namespace the create lane stamps: opschain.requester_kind (user | agent), opschain.rationale (the create request's stated reason), opschain.recommendation_id (the recommendation the change implements), and on an agent-originated change opschain.agent_id (the fleet member id), opschain.instance_id (the acting instance — the change's initiator), opschain.agent_code, opschain.agent_project, opschain.tier_at_creation and opschain.correlation_id — the provenance the approvals inbox reads back as the approval context. A plain human create that states neither a rationale nor a recommendation keeps its metadata byte-for-byte; the provenance keys are server-written and overwrite any caller-supplied twin. |
rationale | string | Create attribute (POST /api/changes): why the change is raised, stored as opschain.rationale and shown to approvers. Optional for a person; mandatory for an agent principal — an agent-originated create without one answers 422 (Rationale can't be blank). At most 4000 characters. |
recommendation_id | string | Create attribute: the recommendation this change implements, stored as opschain.recommendation_id and resolved into the approval context. |
scheduled | boolean | True when a scheduled activity created the change. |
approved_by | array | Audited approve transitions of the root step ({username, message, date}, date at second precision), from the audit:steps:approve events. [] when none. |
rejected_by | array | As approved_by, for audit:steps:reject. [] when none. |
cancelled_by | array/null | Audited cancel transitions (audit:steps:cancel); null when the change was never cancelled. |
state_timing | object | The root step's per-state timing ranges. |
state_timing_summary | object | The change's stored timing summary column. |
notify | object | The create request's notification routing, stored verbatim (notification dispatch is an unported subsystem — recorded gap). |
skip_steps | array of strings | The create/retry request's skip mask (exact, glob or path-prefix matches against step full_paths). |
starting_step | string/null | The create request's starting step, or null. |
cluster_name | string | The stored cluster name; "" when unset. |
pause_state | string | "none", "pausing" (paused with an active leaf-or-wait step still running down) or "paused". |
status_code | string | The serialized run status — see the vocabulary below. Computed by the upstream SQL function public.change_serialized_status_code over the root step. |
paused | boolean | True while paused_at is set. |
paused_by | array | Ordered pause/resume audit trail ({username, action, message, date}) from the audit:changes:pause/audit:changes:resume events. [] when never paused. |
created_at | string | Row creation time. |
started_at | string/null | Root step start; null until the run starts. |
finished_at | string/null | Root step finish; null until finalised. |
updated_at | string | Root step's last update. |
paused_at | string/null | When the current pause began; null while not paused. |
asset_name | string/null | Target asset's display name; null when the target is not under an asset. |
environment_name | string/null | Target environment's display name; null for project-targeted changes. |
project_name | string | Target project's display name. |
git_remote_name | string/null | The commit's git remote name. |
git_rev | string/null | The requested git revision. |
commit_sha | string/null | The resolved commit SHA. |
requires_approval_from | array/null | The stored approval requirements, each merged with its first matching approve (approved_by, always present once requirements exist — null until someone matching approves) and, when matched, rejected_by response. null when the change requires no approval. Username matching is case-insensitive; live LDAP group resolution is a recorded gap. |
correlation_id | string | The chain handle; defaults to the change's own id when nothing caused it. An agent-originated change joins the chain of the agent's SDK-managed correlation scope (the invocation that triggered the agent), so the chain reads incident → the agent's reads → recommendation → this change. |
caused_by_event_id | string/null | The event that triggered this change (trigger firings), else null. |
initiated_by_type / initiated_by_id | string/null | The polymorphic initiator (e.g. a trigger, or agent + the agent instance's id for a change an agent raised inside a correlation scope — the member id and agent code are in metadata.opschain); null for user-created changes. |
chain_depth | integer | Hops from the chain's origin (0 for an origin run). |
maintenance_stamp | string/null | Present only on changes created under a maintenance override; absent otherwise. |
inputs | object | The change's recorded typed inputs, resolved at creation against the action's inputs: declarations — supplied values and evaluated defaults (run.*, the target node.*) alike. Secret-typed values serialize as ****** everywhere. Absent when the change carries no inputs. |
error_handled | boolean | true once a failure-path step (when: error()/always()) executed on this change's failed scope — the change still finalises error; compensation never masks it. Absent otherwise. |
Status vocabulary
Raw step states (mirroring the
upstream StateMachine::AvailableStates):
pending, queued, running, waiting, success, skipped, error,
cancelled, aborted, system_error
Display pseudo-statuses layered on top (display.go): a running change whose
every blocking leaf-or-wait step is waiting reports waiting, or
waiting_for_approval when a blocking step requires approval; a rejected
approval root reports rejected; a retried root awaiting its re-run reports
pending. Wait steps count as blockers even though they carry children — a
waiting wait step acquires its children on entry to waiting, so "leaf" alone
never identifies it. Upstream change roots that still need commit resolution
can additionally report initializing; runs created by this server resolve
the commit at creation and never enter it.
pause_state vocabulary: none, pausing, paused.
What affects approval, pause, cancel and retry
- Approval:
requires_approval_from(resolved from settings at create) gates the root/wait steps; the gates themselves are step endpoints (POST /api/steps/{id}/approve|continue|reject— seeopschain docs resources/step).approved_by/rejected_byrecord the outcomes. - Pause:
POST …/pausewith{"data":{"attributes":{"reason":""}}};paused,paused_at,paused_byandpause_statereflect it. Authorised by execute on the change's action path. Availability is advertised on the document itself:links.pauseappears while the change is pausable (not finalised, not paused) andlinks.resumewhile it is paused — the GUI gates its pause/resume verbs on these conditional links (the upstream serializer's shape). The v2 change view carries the same signal as the typedpausablefield. - Cancel:
DELETE /api/changes/{id}(execute or delete on the action path); recordscancelled_byand drives the steps tocancelled/aborted. - Retry:
POST /api/changeswithattributes.retry: true, achangerelationship, and optionalskip_steps/metadata/override_maintenance. Successful/skipped steps keep their state; the rest re-run (retried steps readpendinguntil re-driven). - Maintenance: a create targeting a node under an active maintenance
window is refused unless
override_maintenance: trueand the caller holds the window's override right; the override stampsmaintenance_stamp.
Relationships and includes
| Relationship | Target type | Notes |
|---|---|---|
properties | properties | The action step's properties document. |
parents | node types | The target node's ancestry, root project first. |
root_step | step | The run's root step. |
steps | step | Every step, ordered (parent, step_sequence). The show default include. |
template_version | template_version | Only on templated (asset) changes. |
workflow_run / workflow | workflow_run / workflow | Only when a workflow change step spawned the change. |
override_properties / override_settings | properties / settings | Only when the create carried overrides. |
Any relationship is includable via include= (dotted paths supported, e.g.
include=steps.properties). Links: self (node-path form), parent (the
target node), project, log_lines, git_remote. Show-only meta:
wait_queue and blocking_queue (both [] unless the composition supplies
them — see opschain docs fleet-queue for the dispatch-side queue view).
Related pages
opschain docs resources/step— the step family this change is made of.opschain docs resources/workflow_run— the workflow-run sibling.opschain docs resources/event— the audit events behind the*_byattributes.opschain docs cli/runs-create,opschain docs cli/runs-show.