> Source: https://builder.ema.ai/v2/core-concepts/versioning
> Title: Versioning

# Versioning

Every AI Employee (AIE) you build is backed by a [workflow](/builder/v2/core-concepts/workflows), and every workflow keeps a full version history. You edit a **draft** freely, then **publish** when it is ready. Publishing freezes the current definition into an immutable, self-contained version and makes it the single **active** version that new runs use. Earlier versions stay in the history so you can compare, promote, or revert to any of them.

This page explains the model: how drafts and published versions differ, why a published version never changes, what the version history records, what you see while editing versus publishing, and — importantly — what happens to runs that are already in progress when you publish.

## Drafts versus published versions

A workflow has two distinct working states, kept separate so you can iterate without affecting what end users see.

-   A **draft** is the working copy you edit in the AI Employee builder. Saving a change that alters the workflow's DAG writes a new version row, marked unpublished (its `published_at` is empty) and inactive. You can save as many drafts as you like; none of them is reachable by end users.
-   A **published version** is a draft that has passed validation and been promoted to live. Publishing stamps the version with a `published_at` timestamp and marks it the single **active** version. New runs resolve to the active version.

Both states are stored as rows in the same `workflow_versions` table. The only structural difference is whether `published_at` is set. A database constraint enforces the rule that a draft can never be the active version — only a published version can be active.

> [INFO]
> **Saving is not publishing.** Saving a draft preserves your work and adds a checkpoint to the history, but it changes nothing for end users. Only publishing changes the version that live runs use.

## How versions are numbered

Each workflow carries a monotonically increasing `latest_version` counter. Saving a changed draft allocates the next number and writes a new draft row with it; publishing allocates a number too if the current draft hasn't already been saved. Version numbers only ever go up — they are never reused, even after a revert.

A version can also carry an optional **name** and **description**: a human-friendly alias such as `production` or `holiday-promo`, set explicitly by a builder. Names are unique per workflow. Unnamed versions are identified by their number.

## Publish immutability

A published version is a complete, self-contained snapshot. Once published, it never changes:

-   The workflow's DAG — every node, edge, and condition — is stored on the version row exactly as it was at publish time.
-   **Agent configuration is stored inline on each node.** Editing an agent template later, or changing a tool's configuration, never alters an already-published version. The version keeps the configuration it captured.
-   Publishing also captures a snapshot of the live AIE configuration (model selection, data-protection settings, feedback options, welcome message, and similar) and the AIE's exposed output fields onto the version row.
-   The attached knowledge bases are snapshotted too, so a later revert can restore both the workflow and the exact knowledge-base state that version ran against.

Because each published version is fully self-contained, nothing it depends on can drift out from under it. This is what makes promote and revert safe: activating an old version reproduces precisely the behavior that version had when it was published.

### What publishing validates

Publishing is not a silent save — it runs a series of pre-flight checks and refuses to publish if any fail. The draft must be a valid DAG (see [DAG validation](/builder/v2/core-concepts/workflows#dag-validation)), and in addition:

Check

What it verifies

`NO_LLM_PROVIDER`

At least one LLM completion model is enabled for your tenant.

`HITL_INVALID_ASSIGNEE`

Every human-in-the-loop node assigns to a real user (or to the triggering user).

`MCP_SERVER_NOT_FOUND` / `MCP_SERVER_NOT_APPROVED`

Every referenced MCP server exists and is approved for use.

Agent-version checks

No agent node pins a deprecated, deleted, or otherwise invalid agent type version.

Tool config

Each Tool's configuration matches the integration catalog's schema.

If a check fails, publishing stops and reports the specific blocker; the active version is unchanged. Fix the reported issue and publish again. A successful publish can still return non-blocking **warnings** (for example, an unreachable branch) alongside the published version.

## The publish transaction

When all checks pass, publishing completes the live cutover atomically:

1.  **Resolve the version to promote.** If your latest saved draft already matches the current DAG, that row is reused; otherwise a fresh draft row is auto-saved first so the published version always reflects exactly what you see.
2.  **Capture snapshots.** The live AIE configuration and exposed output fields are serialized onto the version row, and a knowledge-base snapshot is created.
3.  **Stamp published.** The version's `published_at` timestamp is set.
4.  **Activate.** The version is flipped to active, any previously active version is deactivated, and the workflow's status becomes `published` — all in one transaction.

The previously active version stays in the history, unchanged, available to promote or revert to later.

## In-flight runs and a new publish

This is the behavior that matters most when you publish a change to a workflow that is already serving traffic.

**A run is pinned to the version that was active when it started.** When a run begins, the platform resolves the active version and records its identifier on the run record. From that point on, the run executes entirely against that pinned version's DAG — it does not re-resolve the active version mid-flight.

So when you publish a new version:

-   **In-progress runs are unaffected.** A run that is already executing keeps running against the version it started on, even if you publish a newer version while it is mid-flight. It finishes on its original definition.
-   **Paused runs resume on their original version.** A run that is waiting on a [human-in-the-loop](/builder/v2/core-concepts/human-in-the-loop) response is pinned the same way. When the person responds, the run reloads and resumes against the version it started on — not the version you just published.
-   **Only new runs use the new version.** Runs started after the publish completes resolve to the newly active version.

This guarantees that a run never executes a mix of two definitions: a run started before your publish completes exactly as it would have, and a run started after uses your new version cleanly. The run history records the version number each run executed, so you can always see which definition produced a given result.

> [TIP]
> **Plan around long-running and paused runs.** Because runs complete on the version they started on, a behavior change you publish will not reach runs that are already in progress — including runs paused for hours or days awaiting a human response. If a change must apply to in-flight work, you may need to wait for those runs to drain.

## Version history

The full history of a workflow lives in `workflow_versions`, one row per saved draft and per publish. Each row records:

-   The version `number` and optional `name` / `description`.
-   The complete `dag_definition` snapshot.
-   Whether it is published (`published_at`) and whether it is currently `active`.
-   Who published or saved it, and the configuration, exposed-fields, and knowledge-base snapshots captured at publish time.

You can work with the history through the API:

Method and path

Purpose

`POST /api/v1/workflow/workflows/{id}/publish`

Publish the current draft; returns the published version.

`GET /api/v1/workflow/workflows/{id}/versions`

List a workflow's versions, newest first.

`GET /api/v1/workflow/workflows/{id}/versions/{version}`

Fetch one version's full snapshot.

`GET /api/v1/ai-employees/{id}/versions/{version}/document-diff`

Compare the knowledge-base document sets of two published versions.

`PATCH /api/v1/ai-employees/{id}/versions/{version}`

Rename a version or set its description (no activation).

`POST /api/v1/ai-employees/{id}/versions/{version}/promote`

Make an earlier published version active again.

`POST /api/v1/ai-employees/{id}/versions/{version}/revert`

Make an earlier version active and reconcile its knowledge bases.

`DELETE /api/v1/ai-employees/{id}/versions/{version}`

Delete a version (the active version cannot be deleted).

These routes are available to builder-tier roles — system admin, builder admin, and builder.

### Promote versus revert

Both promote and revert make an earlier published version active again, but they differ in how they treat knowledge-base data:

-   **Promote** is a forward activation. It flips the active version and restores that version's captured AIE configuration and exposed fields into the live tables. It does **not** touch knowledge-base data, because the target version's knowledge bases are already in the state captured at its own publish time.
-   **Revert** is a rollback. It does everything promote does and additionally reconciles the knowledge bases back to the snapshot captured when the target version was published, so the data the AIE searches matches the reverted definition.

Both refuse to activate a draft — only a published version can be promoted or reverted to. Neither alters the version rows themselves; they only change which version is active.

## What a builder sees: editing versus publishing

The builder always knows whether its edits are reaching end users. The platform surfaces two version numbers on a workflow — the `latest_version` (your newest saved draft) and the `active_version` (the live one) — and compares them:

State

Condition

What it means

**Draft**

No active version

The workflow has never been published. End users cannot reach it yet.

**Published**

Active version equals latest version

What you see is exactly what is live. No pending changes.

**Unpublished changes**

Active version is older than latest version

You have saved draft edits that have not been published. Live runs still use the older active version until you publish.

While **editing**, you change the draft; saving adds checkpoints to the history but leaves the active version alone. While **publishing**, you cut a new live version and shift the active pointer to it. The "unpublished changes" indicator clears once the active version catches up to your latest draft.

## Where to go next

-   [Workflows](/builder/v2/core-concepts/workflows) — the DAG model, runs, and steps that versions snapshot.
-   [Human in the Loop](/builder/v2/core-concepts/human-in-the-loop) — why paused runs resume on their original version.
-   [Knowledge Bases](/builder/v2/core-concepts/knowledge-bases) — the data that publish snapshots and revert reconciles.
-   [API reference](/builder/v2/api-reference) — the full version, publish, and run API.
