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

# Flow versions and publishing lifecycle

> Manage drafts, snapshots, and publishes for Orbit Flows. Stage changes, review diffs, roll back a regression, and read the audit trail.

# Flow versions and publishing lifecycle

Every Orbit Flow has two active states at once: the **published snapshot** that live traffic executes, and a **working draft** where you stage the next change. Saves never overwrite the live graph on their own — only an explicit publish promotes the draft to live. That separation lets you edit, validate, diff, and review before a change touches a single contact.

This guide covers the lifecycle from first draft through rollback: where each version lives, how to stage a change safely, how to compare it to the published snapshot, how to restore a previous version, and how long drafts and audit entries are retained.

## Where versioning lives

Open any saved flow in the dashboard and you see its current draft. The same flow exposes three related surfaces:

* **Working draft** — the editable graph. It starts identical to the published snapshot and diverges as you edit. Drafts never execute live.
* **Published snapshot** — the immutable graph that the runtime uses for new executions. It is created when you publish and kept as a version you can inspect or restore.
* **Version history** — every save and every publish writes a numbered snapshot. The Versions panel lists them with author, timestamp, node count, and a diff summary.

The dashboard status badge tells you which state is active: `draft`, `published`, or `published with pending draft`.

## Stage a draft change

You can build the draft in the canvas or edit the JSON definition through the API and CLI. The safest path is the same in both cases: edit, validate, snapshot, review, then publish.

### Edit in the Flow Builder

1. Open the flow and change the graph — add a node, rewire a branch, update a prompt, or change a trigger source.
2. Choose **Save**. Saving writes a new version snapshot but leaves the published snapshot live.
3. Choose **Test → Validate Flow** to run structural checks: every node needs a route from a trigger, A/B weights must sum to 100, and disconnected nodes are flagged.
4. Choose **Test → Run test** to execute the draft against a sample payload and watch the canvas highlight the path it took.

Only choose **Deploy** when the validated draft is the one you want live. Deploy flips the published snapshot to the current draft and starts using it for new executions immediately.

### Edit through the API

The API uses the same `definition` shape as the builder. A typical cycle:

```bash cURL theme={null}
# 1. Fetch the current flow definition.
curl -H "X-API-Key: dv_live_sk_..." \
  https://api.orbit.devotel.io/api/v1/flows/flw_abc123

# 2. Edit the JSON locally, then PUT the full definition back.
curl -X PUT -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d @flow-updated.json \
  https://api.orbit.devotel.io/api/v1/flows/flw_abc123

# 3. Validate before publishing.
curl -X GET "https://api.orbit.devotel.io/api/v1/flows/flw_abc123/validate" \
  -H "X-API-Key: dv_live_sk_..."

# 4. Publish when ready.
curl -X POST "https://api.orbit.devotel.io/api/v1/flows/flw_abc123/publish" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{}'
```

```typescript Node.js theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

// Fetch the live definition.
const { data: flow } = await orbit.request("GET", "/flows/flw_abc123");
const definition = flow.definition;

// Stage a change: widen a keyword match.
definition.nodes.find((n) => n.id === "check_intent").data.expression =
  "body.includes('HELP') || body.includes('SUPPORT')";

// Save the draft.
await orbit.request("PUT", "/flows/flw_abc123", { definition });

// Validate, then publish.
const validation = await orbit.request("GET", "/flows/flw_abc123/validate");
if (validation.data.valid) {
  await orbit.request("POST", "/flows/flw_abc123/publish");
}
```

`PUT` is always a full-definition replacement. Because the API does not merge node-by-node, the safe pattern is GET, edit, validate, then publish.

### Create a pending version snapshot

Both the dashboard **Save** button and the API `PUT` write a new version snapshot. You can also capture an explicit snapshot without publishing:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/flows/flw_abc123/versions" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{}'
```

This is useful when you want a named checkpoint — for example, before a risky refactor — without promoting it to live. The snapshot appears in the Versions panel and can be restored later.

## Review diffs against the published snapshot

Open the **Versions** panel to see every saved and published version. Each row shows the version number, author, timestamp, and a short diff summary. Pick any two versions to compare:

* **Preview** loads a version onto the canvas read-only so you can inspect the graph.
* **Diff** shows node additions, removals, and config changes between the selected version and the current published snapshot.

Review the diff before you publish. Look especially at trigger sources, send-node `to`/`body` values, condition expressions, and any AI prompt text — these are the changes that alter live behavior.

### Diff via the API

The versions list is available programmatically:

```bash theme={null}
curl -H "X-API-Key: dv_live_sk_..." \
  https://api.orbit.devotel.io/api/v1/flows/flw_abc123/versions
```

The response carries each snapshot with its version number, `status` at the time it was saved (`draft` or `published`), author, timestamp, and node/edge counts. Use the version numbers to request a side-by-side diff or to restore a known-good graph.

## Roll back the last-deployed version

A publish is not a one-way door. If a change causes a regression, restore the previous published snapshot:

1. Open the flow and choose **Versions**.
2. Find the last version whose status is `published` before the bad deploy.
3. Choose **Restore**. The version's graph replaces the current draft, but the flow stays published.
4. Choose **Deploy** to promote the restored draft back to live.

Restoring never flips status on its own: if you restore onto a published flow, the published snapshot keeps running until you deploy the restored draft. That prevents an accidental rollback from going live before you review it.

### Roll back via the API

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/flows/flw_abc123/versions/3/restore" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{}'
```

The call returns the flow with its draft definition replaced. Republish when you are ready to make that snapshot live again.

## Retention and audit entries

Versions and drafts are retained with separate ceilings:

* **Version snapshots** — the last 100 snapshots per flow are kept, newest first. Older snapshots fall off automatically.
* **Working drafts** — unpublished draft edits are retained for 90 days after the last save. A draft that is not touched for 90 days is eligible for cleanup; the published snapshot and version history are not affected.
* **Audit entries** — every `flow.created`, `flow.updated`, `flow.published`, `flow.unpublished`, and `flow.version_restored` event is written to the workspace audit log. Audit entries do not expire; export them through **Settings → Audit log** or the `audit.log.created` webhook.

If you need to prove who published a change or when, filter the audit log by `resource=flow` and the flow id. The detail payload includes before/after summaries for updates and the version number for publishes and restores.

## Example: canary rollout of a new handoff prompt

You want to test a warmer AI handoff prompt in a support flow without affecting every contact.

1. Open the published flow and change the **AI Response** node's system prompt.
2. Save the draft and run **Test** with a sample support message to confirm the new prompt routes correctly.
3. Add an **A/B Test** node before the AI Response: 10% to the new prompt, 90% to the current one.
4. Validate the graph and publish.
5. Watch the flow analytics and A/B results. If the new prompt performs better, edit the A/B node to 100% new prompt and republish. If it performs worse, restore the previous published version and remove the A/B node.

The published snapshot always records which variant a contact saw, so you can measure conversion against the variant assignment.

## Example: git-synced review via CLI

Your team wants flow definitions in source control.

1. Export the flow from the dashboard or fetch it with `GET /flows/:id`.
2. Save the `definition` JSON in your repository as `flows/support-handoff.json`.
3. On a branch, edit the JSON locally and open a pull request.
4. In CI, run validation with `GET /flows/:id/validate` against a test copy, or upload the definition to a sandbox flow and validate there.
5. After merge, apply the approved definition with `PUT /flows/:id` and publish with `POST /flows/:id/publish`.

Because every PUT creates a new version snapshot, the repository commit maps cleanly to a flow version number. If a deploy fails, the audit log shows the publish event and you can restore the previous version.

## Example: bounce-back from a regression

A published change is routing support messages to the wrong queue.

1. Open the flow and go to **Versions**.
2. Find version 4, the last published version before the bad deploy, and choose **Restore**.
3. The canvas now shows the old routing. Run **Test** with a sample message to confirm it routes to the right queue.
4. Choose **Deploy** to republish the restored graph.
5. Open **Settings → Audit log**, filter by the flow id, and confirm the `flow.version_restored` entry.

Live executions that started under the bad version keep running with the graph they entered; new executions use the restored graph. If an in-flight execution needs to be stopped, use **Take offline** to pause new entries while you fix the draft.

## See also

* [Flow Builder](/flows/builder) — visual editor, node config, and toolbar actions
* [Flows overview](/flows/overview) — triggers, node taxonomy, execution semantics
* [Flow execution model](/concepts/flow-execution-model) — runtime binding, async waits, and version pinning
* [Audit Log](/guides/audit-log) — reading, filtering, and exporting the audit ledger
* [Flows API reference](/api-reference/endpoints/flows) — full endpoint shapes for publish, restore, and versions


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.