Skip to main content
A workflow in ModuleX changes along three independent timelines, and it helps to keep them apart:

Edit version

The live canvas state. Every accepted edit bumps a monotonic edit_version integer and records an RFC-6902 patch of the change.

Deployments

Immutable snapshots of the canvas taken when you deploy. One deployment is marked live; runs from a saved workflow execute the live snapshot, not the draft canvas.

Run history

The durable record of executions. Each run row references the deployment it ran (deployment_id) plus its input and output summary. See running workflows.
The draft canvas is what you edit; a deployment is a frozen copy you promote; a run is one execution against a snapshot. Editing the canvas never changes a past deployment or a past run. “Restoring a prior version” means activating an older deployment so future runs use it.
All routes and SDK methods on this page require an owner or admin role and the auth headers Authorization: Bearer mx_live_… plus X-Organization-ID. The retired member role cannot edit or deploy. See roles & permissions and authentication.

The edit version (live canvas)

Each workflow carries an edit_version integer, a last_edited_by user id, and a last_edited_at timestamp. The version advances every time the canvas is saved, whether the edit came from you, a collaborator, the AI Composer, or a REST PATCH/PUT. This is the version a collaborative session uses to detect and reconcile concurrent edits.

Two version planes

The realtime collaboration server and the saved workflow keep two version counters that advance at different rates by design. Understanding the split prevents surprises when you read version numbers off the wire. The room batches accepted operations and saves them roughly every two seconds. One save can carry many operations, so after N edits and one save the room version is base + N while edit_version is base + 1. Both numbers are correct; they measure different things.
Do not overwrite your tracked client version from the saved event. saved.version is the saved edit_version (Plane B) and can be numerically behind the ack/broadcast version (Plane A) you have been following. Treat saved as a persistence confirmation, not a version source. Full event semantics live in presence, locks & versioning and Socket.io collaboration events.

Edit conflicts

When a client submits an operation whose version is too far behind the room (more than the server’s tolerance window), the server rejects it and emits a conflict event instead of applying it:
resolution is always the literal string rebase. The server does not perform a rebase for you — the value is advisory. The client’s recovery is to leave and re-join the workflow to resynchronize from the authoritative state. A separate, silent conflict can occur at save time if an external writer (Composer, a REST PATCH, or an activation) advanced edit_version past the room’s last-saved base; the room re-queues the patches and retries on the next tick without emitting anything to you. Conflicts are covered end to end in realtime co-editing & external sync.

Canvas edit history

Every successful save also records one edit-history entry. Each entry stores the JSON Patch array (RFC-6902) that took the workflow from the previous version to this one; each edit_version is unique per workflow.
uuid
Unique identifier for the history entry.
uuid
The workflow this edit belongs to. Edit history is removed when the workflow is deleted.
uuid
The user who made the edit.
integer
The sequential version this patch produced. Unique per workflow.
array
The RFC-6902 JSON Patch array applied to reach this version, for example [{op, path, value}]. Paths address the schema, such as /workflow/nodes/2/name.
datetime
When the edit was persisted.
This edit history powers the audit trail and the in-app undo/redo, but it is an internal store.
There is no public REST endpoint or SDK method to read, replay, or revert the edit history. It is written by the collaboration server and the Composer; it is not exposed for retrieval. Undo/redo in the builder is a client-side snapshot stack (see below), and rolling back to an earlier point is done through deployments, not by replaying patches. Do not build automation that assumes a history-read API exists. This gap is tracked in known limitations.

Undo and redo in the canvas

Undo/redo in the builder is a client-only feature, not a server operation. The canvas keeps a bounded stack of shallow snapshots (up to 50). Undo and redo compute the difference between two snapshots — added/deleted nodes, connected/disconnected edges, moved nodes — and push that diff to the collaboration server as a single batched operation so other collaborators converge. Undo does not call any /workflows route directly and does not touch deployments. Closing the canvas clears the stack.

Deployments (versioned snapshots)

A deployment is an immutable copy of the workflow taken at deploy time. Deploying captures the current canvas (workflow_schema), name, description, version, default input, and execution config into a new WorkflowDeployment row, then marks it live. The draft canvas keeps evolving afterward; the deployment does not. Deployments are the unit of versioning and rollback. They matter because a run from a saved workflow executes the live deployment, not the draft canvas:
  • POST /workflows/run with a workflow_id loads the schema from that workflow’s live deployment.
  • If the workflow has no live deployment, the run is rejected with 400 and the message Workflow has no active deployment. Deploy the workflow first using POST /workflows/{workflow_id}/deploy.
  • An ad-hoc run that sends an inline workflow schema (what the builder’s Run button does) bypasses deployments entirely and runs the canvas as-is.
See running workflows and run via API for the execution side, and schedules — a schedule also requires a live deployment.

Deployment record

uuid
Unique deployment identifier. Use this id to get, activate, or delete the deployment.
uuid
The parent workflow. Omitted from list-row responses; present on the detail response.
string
Workflow name captured at deploy time.
string
Snapshot version string. On each deploy the major version auto-bumps, for example 1.0.02.0.03.0.0. Defaults to 1.0.0.
string | null
Optional note you pass at deploy time, for example Bug fix for empty-query edge case.
string | null
Optional URL of a visual snapshot of the canvas, generated from the React Flow graph.
uuid | null
The user who deployed. Nullable — the deployment persists even if that user is later removed.
boolean
Whether this deployment is the one the workflow currently runs. Exactly one deployment is live at a time (or none, after deactivation).
datetime
When the deployment was created.
The detail response (GET …/{deployment_id}) additionally includes description, the full workflow_schema, the snapshotted input, and the snapshotted config. List rows omit workflow_id, description, and the schema to stay light.

Deploy a workflow

POST /workflows/{workflow_id}/deploy snapshots the current canvas and immediately marks the new deployment live.
string
required
The UUID of the workflow to deploy.
string
Optional note describing this deployment.
string
Optional URL of a visual snapshot of the canvas.
The response is the deployment record with is_live: true. Errors: 400 Invalid workflow_id format, 404 Workflow not found, 500 on an internal failure.

List deployments

GET /workflows/{workflow_id}/deployments returns the deployment history, newest first.
integer
default:"20"
Page size, 1–100.
integer
default:"0"
Number of rows to skip. Pagination is offset-based; see pagination.
The response is { deployments: [...], total, limit, offset }. The is_live flag marks the active deployment.

Restore a prior version (activate a deployment)

Restoring an earlier version means activating an older deployment so future runs use its snapshot. Activation does not touch the draft canvas — your editable workflow stays as it is; only live_deployment_id moves. PUT /workflows/{workflow_id}/deployments/{deployment_id}/activate
1

Find the deployment to restore

List the deployments and pick the id of the version you want live (use version, created_at, and deployment_note to identify it).
2

Activate it

Call activate with that deployment id. The previously live deployment is recorded in the response so you can swap back if needed.
3

Verify

Subsequent saved-workflow runs now execute the restored snapshot. The draft canvas is unchanged.
The response is { success: true, message, deployment_id, previous_live_deployment_id? }. If the deployment is already live, you get { success: true, message: "Deployment is already live", deployment_id }. Errors: 400 invalid UUID, 404 workflow or deployment not found.
A clean rollback workflow: keep deploying as you ship (each deploy auto-bumps the major version and becomes live), and if a release misbehaves, activate the previous deployment id from the list response’s previous_live_deployment_id to revert in one call.

Get a single deployment

GET /workflows/{workflow_id}/deployments/{deployment_id} returns the full record, including the stored workflow_schema, input, and config — useful for diffing a deployment against the current canvas or against another deployment.

Deactivate the live deployment

DELETE /workflows/{workflow_id}/deployments/live clears live_deployment_id so no deployment is active. After this, a saved-workflow run returns the 400 “no active deployment” error until you deploy or activate again. The path segment live is literal.
The response is { success: true, message, previous_live_deployment_id? }, or { success: true, message: "No live deployment was active" } when nothing was live.

Delete a deployment

DELETE /workflows/{workflow_id}/deployments/{deployment_id} permanently removes a snapshot. If you delete the live deployment, ModuleX auto-promotes the previous one by created_at; if none remains, live_deployment_id is cleared.
The response is { success: true, message, deleted_deployment_id, was_live, new_live_deployment_id? }. The new_live_deployment_id field is populated only when was_live is true.

SDK and REST parity

Every deployment operation exists in both SDKs and as a REST route. There is no SDK shortcut that reads or reverts the edit history, because no such endpoint exists. See the full SDK ⇄ API parity matrix and the deploy & versions page.

Credits and the billing gate

Versioning operations do not consume credits and are not subject to the billing-admission gate. Deploying, listing, activating, deactivating, and deleting deployments are plain CRUD-style routes: on auth or validation failure they return the standard FastAPI envelope {detail} (for example a 400 or 404), not the flat DenialEnvelope. The billing gate applies when you run a workflow, not when you version it. A run can return a 402, 403, or 429 DenialEnvelope with the shape {code, layer, key, current, limit, reason}. That behavior is documented on usage gating & limits and errors & status codes; resuming an interrupted run reuses the run reservation and is not charged again.

Versioning, history & runs at a glance

The room version (Plane A) advances per operation and appears on ack and broadcasts. The saved edit_version (Plane B) advances per save and appears on the saved event and on GET /workflows/{id}. A deployment’s version is a separate string (1.0.0, 2.0.0, …) that auto-bumps on each deploy. Run history exposes none of these as a counter — it records deployment_id per run instead.
Activate an older deployment with deployments.activate (or the activate REST route). This changes only which snapshot future runs use; it does not rewrite your draft canvas. There is no API to roll the canvas itself back to an earlier edit_version — edit history is not exposed for replay.
Run history is separate from versioning. Use GET /workflow-runs (and GET /workflow-runs/{run_pk} for one run) to list durable runs, each of which carries the deployment_id it executed and an output_summary. See running workflows.
Live edits flow over Socket.io. If your client version drifted too far behind the room, the server emits a conflict (with resolution: "rebase") and you re-join to resync. External changes from the Composer or a REST PATCH are pushed over workflow:external-sync. See realtime co-editing & external sync and presence, locks & versioning.

Next steps

Deploy & versions

The deployment lifecycle from the builder, end to end.

Presence, locks & versioning

How the collaboration version planes, locks, and conflicts work on the wire.

Running workflows

Run a workflow and read its durable run history.

Realtime co-editing & external sync

How canvas edits and external changes converge across collaborators.