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.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 anedit_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.
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 aconflict 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; eachedit_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.
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/runwith aworkflow_idloads the schema from that workflow’s live deployment.- If the workflow has no live deployment, the run is rejected with
400and the messageWorkflow has no active deployment. Deploy the workflow first using POST /workflows/{workflow_id}/deploy. - An ad-hoc run that sends an inline
workflowschema (what the builder’s Run button does) bypasses deployments entirely and runs the canvas as-is.
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.0 → 2.0.0 → 3.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.
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.
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.
{ 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; onlylive_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.
{ 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.
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.
{ 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.
{ 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
Which version number am I looking at?
Which version number am I looking at?
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.How do I revert a workflow?
How do I revert a workflow?
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.Where do I see past executions?
Where do I see past executions?
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.Why did a teammate's change not appear, or appear as a conflict?
Why did a teammate's change not appear, or appear as a conflict?
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.