Skip to main content
Zeptar’s versioning system lets you change an agent’s configuration without disturbing what’s serving production traffic. Every change you save becomes a draft, every publish becomes an immutable version, and you can fork a branch off any version to experiment in isolation.
Per-branch traffic splitting and A/B experiments are not yet available — every Zeptar agent currently serves 100% of traffic from its Main branch. The branching, drafting, and merging surfaces described below are all live today.

Overview

The versioning system provides:
  • Immutable snapshots of your agent’s configuration at any point in time.
  • Isolated branches for working on changes without affecting Main.
  • Per-user drafts so two teammates can edit the same branch without colliding.
  • Merging to promote a branch’s current head onto Main as a new version.
Versioning is always on for Zeptar agents — there’s no opt-in toggle. The moment you create an agent you get a Main branch with its first version pinned to the initial configuration.

Core concepts

Versions

A version is an immutable snapshot of an agent’s configuration. Each version has:
  • a unique id with the prefix agtvrsn_
  • a version_number that increases monotonically within its branch (starting at 1)
  • a config payload — in this release that’s { system_prompt }; future specs will add voice, LLM, tools, first message, and other per-version fields
  • a parent_version_id linking back to the previous head of the branch (or null for the first version on Main)
  • audit fields (created_by, created_at)
Versions are created when you publish a draft on a branch or when you merge a branch into another branch. Once written, a version is never edited or deleted — it’s the audit trail and the surface that running conversations resolve against.

Branches

A branch is a named line of versions, analogous to a git branch.
  • Every agent has a default branch called Main (id prefix agtbrch_). Main cannot be renamed or archived.
  • You can create additional branches from any version on any branch. The new branch starts at that parent version and accumulates its own history independently.
  • Branch names are 1–140 characters and accept letters, numbers, spaces, underscores, hyphens, dots, slashes, and ()[]{}. The literal name main (any casing) is reserved.
  • Each branch carries an optional description — a free-form note about what the branch is for ("Trying GPT-4o-mini", "Demo for Acme").
  • A name conflict with another active branch on the same agent returns 400 branch_name_in_use. Archived branches don’t count toward the conflict.

Drafts

Unsaved edits live as drafts. Drafts are scoped to the requesting user — two teammates can edit the same branch concurrently and each sees only their own draft.
  • A draft is a { system_prompt } shape stored against (agent_id, branch_id, user_id).
  • Editing the system prompt in the dashboard auto-saves to your draft.
  • Publishing the draft creates a new version on the branch and clears the draft row in the same transaction.
  • Discarding the draft removes it without creating a version.
  • Switching branches in the dashboard reveals that branch’s draft for your user.

Working with branches

The dashboard’s Branches tab on any agent page is the primary UI:
  • Create branch opens a dialog with a target-branch select (defaults to Main) and optional description.
  • Inline rename — click any non-default branch name in the table to edit. Main shows a tooltip explaining it can’t be renamed.
  • Version history — each row’s history icon opens a side drawer listing every version on the branch. Click a version to see a side-by-side diff against the branch’s current head.
  • Merge — the row’s overflow menu opens a merge confirmation dialog showing the diff between the source branch and the target you pick (defaults to Main).
  • Archive / unarchive — the overflow menu archives a branch (hidden from listings by default; toggle “Show archived” in the toolbar to see them).
Everything in the table is also available via the API.

Create a branch

Returns the full BranchSummary (including the new branch’s head_version_id, which is the same as the parent version you forked from).

Edit on the branch

Switch to the branch from the breadcrumb dropdown on the agent detail page. Edits to the system prompt are saved to your per-user draft on that branch automatically. When you’re ready to publish:
Creates a new version on the branch, advances head_version_id, deletes your draft. The publish runs under a FOR UPDATE row lock on the branch row so concurrent publishes serialize cleanly instead of racing the unique (branch_id, version_number) index.

List and inspect

BranchSummary carries a derived is_archived: boolean alongside the archived_at timestamp, and a has_user_draft: boolean flag indicating whether the caller has an unpublished draft on the branch.

Merging branches

When you’re ready to promote a branch’s current head:
Merging:
  • Creates a new version on the target branch with the source branch’s current config snapshot.
  • Sets the new version’s parent_version_id to the target’s previous head (observed under the row lock, not pre-tx).
  • Optionally archives the source branch.
  • Acquires FOR UPDATE row locks on both source and target in fixed id order to avoid deadlock with a concurrent reverse-pair merge.
Merge is admin-only at the organization level — non-admins get 403 not_admin.
Today’s merge is a promote, not a three-way merge: the target’s intermediate changes since the source branch was forked are overwritten by the source’s head config. There’s no conflict detection. Document the assumption with your team before running parallel branches that both touch the same fields.

Archiving branches

Archived branches are hidden from the default listing but remain queryable, and their history is preserved. Unarchive by sending { "is_archived": false } — the server validates that no other active branch already uses the same name (since the unique-on-name partial index only counts active rows). The Main branch cannot be archived — attempts return 400 cannot_modify_main.

Best practices

1

Use descriptive branch names + descriptions

Branch names show up in the breadcrumb dropdown, the branches table, and merge dialogs. A name like experiment/shorter-greeting communicates intent at a glance; test doesn’t. Use the description field to capture what hypothesis you’re testing, what success looks like, and which version you forked from.
2

Publish frequently, merge promptly

Drafts are per-user — they don’t ship anywhere until you publish. Publish a version when a change is testable, even mid-experiment. Once an experiment is conclusive, merge promptly so the branch doesn’t drift from Main for weeks (long-running branches are harder to merge because the target’s intermediate history gets overwritten).
3

Use Version History to diff before merging

The version history drawer’s diff view (any version vs the branch’s current head) is the cheapest way to verify a change is what you think it is. Always sanity-check before clicking Confirm Merge.
4

Archive instead of delete

There’s no delete — by design. Archiving keeps the version history queryable so you can revisit a past experiment or restore the branch if you change your mind.

What’s next

  • Per-branch traffic splitting — split production traffic across multiple branches by percentage, with deterministic routing by conversation id. Tracked under the deployments spec.
  • Experiments — opinionated A/B testing on top of branches + deployments, with success-metric monitoring and auto-rollout. Lands after traffic splitting.
  • Versioned voice, LLM, tools, knowledge base — today’s per-version config only carries the system prompt. Future specs will fatten it.
  • Conflict-aware merge — three-way merge with a conflict UI so concurrent edits on Main and a feature branch can be reconciled instead of silently overwritten.
See the Branches API reference for the full endpoint surface, or the Drafts and Versions overviews for the lower-level details.