Branches, versions, and merging
Branches are versioned snapshots of an agent's config (prompts, voices, tools, workflows) that let you test changes without touching live production. Main is the default branch and receives 100% of traffic unless a split is configured. Drafts are work-in-progress edits on a branch that are not committed to a version until published. Traffic splitting distributes a share of live conversations deterministically across branches (by conversation id) for A/B testing. Merging folds a source branch's changes into its parent (usually main), creating a new version on the target.
Host https://api.elevenlabs.io, header xi-api-key: $API_KEY. The engineer supplies $API_KEY and $AGENT_ID.
Branch operations fail most often on stale state: a wrong branch id, a traffic allocation that does not total 100, or an operation blocked by branch protection. Read current branch state first, then route to the exact operation.
HARD GATE: resolve the id before any write
Never write to a branch id you have not just confirmed. If the user names a branch ("switch to angelo/test-switch", "go to my test branch"), that name is not a ready-to-use id. Resolve it by listing branches and matching on name first, in the same task, before the first write. Do not reuse an id from earlier context, memory, or a guess. If no exact name match exists, say so and ask the user to confirm rather than picking the closest-looking one.
1. Read current branch state first
curl -s "https://api.elevenlabs.io/v1/convai/agents/$AGENT_ID/branches" \
-H "xi-api-key: $API_KEY"
This is the source of truth. For each branch note the exact id (agtbrch_...), current_live_percentage, protection_status (admin_perms_required vs writer_perms_required), parent_branch_id, main_branch_id, and draft_exists. Get one branch's true HEAD version from .most_recent_versions[0].id.
Also get the agent to confirm which branch is currently active and the agent id:
curl -s "https://api.elevenlabs.io/v1/convai/agents/$AGENT_ID" -H "xi-api-key: $API_KEY"
2. Route to the exact operation
- Create a copy to experiment on ->
POST /v1/convai/agents/$AGENT_ID/branches with {name, description, parent_version_id}. Use the parent branch's HEAD version id (from .most_recent_versions[0].id). New branches start at 0% live traffic, so creation alone sends no live traffic.
- Edit config on a different branch -> scope every read and write to that branch with
?branch_id=<id>. PATCH /v1/convai/agents/$AGENT_ID?branch_id=<id> commits to that branch's HEAD, never main.
- Send a share of live traffic to a branch -> set the traffic split. This is the operation most likely to fail; see the constraints below.
- Fold a branch's changes back into its parent -> merge. Preview first, then merge; the merge is destructive on the target.
3. Constraints that cause the failures
- Traffic split must total exactly 100% across all active branches. Setting a split declares the whole allocation, not one branch. If you raise a new branch to 10%, lower another (usually main) by the same 10% in the same call. Sum every branch's
current_live_percentage from the branch list, apply the delta, and confirm the new set sums to 100 before sending. A partial set that does not total 100 is the top validation failure.
- Only include active (non-archived) branches in the split. An archived branch or a stale id triggers not_found or validation. Rebuild the allocation strictly from the current branch list.
- Protection status blocks writes. Main typically has
protection_status: admin_perms_required. If the user is not an admin, merging into main and editing main fail with a permission error. Check protection_status before promising a merge; if it is admin-gated and the user lacks the role, route them to the traffic-split A/B path (ramp traffic instead of merging) or to support.
- Merge target follows parentage. A branch merges into its
parent_branch_id. Verify the branch has the parent the user expects. You cannot merge a branch into an unrelated branch.
- An uncommitted draft blocks the operation. If
draft_exists: true, unsaved edits can cause a wrong-state error on switch or merge. Have the user save/commit or explicitly discard the draft first. Do not silently overwrite it.
- Ramp gradually, do not jump to 100. Move a proven branch 10% -> 50% -> 100% across separate traffic-split calls, watching analytics between steps, rather than one 0 -> 100 jump.
- Traffic split is not the same as explicit branch selection.
current_live_percentage only governs how un-pinned live traffic is auto-routed. A branch at 0% can still receive conversations when it is selected explicitly (a branch_id passed via the API, or an explicit branch pin in a client). Never tell a user that a 0% branch gets no conversations, and account for explicitly-targeted branches when reasoning about where recent conversations came from.
4. Merge
Merge folds a branch into its parent. Always run a merge preview first to show the diff before merging. The merge endpoint is:
POST /v1/convai/agents/{agent_id}/branches/{source_branch_id}/merge
Merging is destructive on the target and creates a new version on it. If main is admin_perms_required and the user lacks the role, the merge fails with a permission error; do not retry. Offer the traffic-split A/B path instead, or escalate to support.
5. Recovery
- A bare validation failure is almost always the traffic split not summing to 100 or an array shape issue. Re-read the branch list, recompute the full allocation over active branches to total exactly 100, resend. Do not retry the identical payload.
- not_found means the branch id is stale, archived, or a name was passed. Re-read the branch list, copy the exact current id, retry. For a traffic split, drop any branch not present in the fresh list.
- A wrong-state error means you acted before the branch was ready or an uncommitted draft is blocking. Save/commit or discard the draft, then retry.
- A permission error means the target branch is protection-gated and the user lacks the role. Do not retry; offer the traffic-split path or escalate.
1---2name: architect-branches-versions-merge3description: Use when working with agent branches, versions, drafts, traffic splits, or merges. Fires on "make a copy to test on", "create a branch", "switch to my test branch", "send 10% of traffic to the new version", "roll it out gradually", "merge my branch back to main", or when a branch operation errored.4---56# Branches, versions, and merging78Branches are versioned snapshots of an agent's config (prompts, voices, tools, workflows) that let you test changes without touching live production. Main is the default branch and receives 100% of traffic unless a split is configured. Drafts are work-in-progress edits on a branch that are not committed to a version until published. Traffic splitting distributes a share of live conversations deterministically across branches (by conversation id) for A/B testing. Merging folds a source branch's changes into its parent (usually main), creating a new version on the target.910Host `https://api.elevenlabs.io`, header `xi-api-key: $API_KEY`. The engineer supplies `$API_KEY` and `$AGENT_ID`.1112Branch operations fail most often on stale state: a wrong branch id, a traffic allocation that does not total 100, or an operation blocked by branch protection. Read current branch state first, then route to the exact operation.1314## HARD GATE: resolve the id before any write1516Never write to a branch id you have not just confirmed. If the user names a branch ("switch to angelo/test-switch", "go to my test branch"), that name is not a ready-to-use id. Resolve it by listing branches and matching on `name` first, in the same task, before the first write. Do not reuse an id from earlier context, memory, or a guess. If no exact name match exists, say so and ask the user to confirm rather than picking the closest-looking one.1718## 1. Read current branch state first1920```bash21curl -s "https://api.elevenlabs.io/v1/convai/agents/$AGENT_ID/branches" \22 -H "xi-api-key: $API_KEY"23```2425This is the source of truth. For each branch note the exact `id` (agtbrch_...), `current_live_percentage`, `protection_status` (admin_perms_required vs writer_perms_required), `parent_branch_id`, `main_branch_id`, and `draft_exists`. Get one branch's true HEAD version from `.most_recent_versions[0].id`.2627Also get the agent to confirm which branch is currently active and the agent id:2829```bash30curl -s "https://api.elevenlabs.io/v1/convai/agents/$AGENT_ID" -H "xi-api-key: $API_KEY"31```3233## 2. Route to the exact operation3435- **Create a copy to experiment on** -> `POST /v1/convai/agents/$AGENT_ID/branches` with `{name, description, parent_version_id}`. Use the parent branch's HEAD version id (from `.most_recent_versions[0].id`). New branches start at 0% live traffic, so creation alone sends no live traffic.36- **Edit config on a different branch** -> scope every read and write to that branch with `?branch_id=<id>`. `PATCH /v1/convai/agents/$AGENT_ID?branch_id=<id>` commits to that branch's HEAD, never main.37- **Send a share of live traffic to a branch** -> set the traffic split. This is the operation most likely to fail; see the constraints below.38- **Fold a branch's changes back into its parent** -> merge. Preview first, then merge; the merge is destructive on the target.3940## 3. Constraints that cause the failures41421. **Traffic split must total exactly 100% across all active branches.** Setting a split declares the whole allocation, not one branch. If you raise a new branch to 10%, lower another (usually main) by the same 10% in the same call. Sum every branch's `current_live_percentage` from the branch list, apply the delta, and confirm the new set sums to 100 before sending. A partial set that does not total 100 is the top validation failure.432. **Only include active (non-archived) branches** in the split. An archived branch or a stale id triggers not_found or validation. Rebuild the allocation strictly from the current branch list.443. **Protection status blocks writes.** Main typically has `protection_status: admin_perms_required`. If the user is not an admin, merging into main and editing main fail with a permission error. Check `protection_status` before promising a merge; if it is admin-gated and the user lacks the role, route them to the traffic-split A/B path (ramp traffic instead of merging) or to support.454. **Merge target follows parentage.** A branch merges into its `parent_branch_id`. Verify the branch has the parent the user expects. You cannot merge a branch into an unrelated branch.465. **An uncommitted draft blocks the operation.** If `draft_exists: true`, unsaved edits can cause a wrong-state error on switch or merge. Have the user save/commit or explicitly discard the draft first. Do not silently overwrite it.476. **Ramp gradually, do not jump to 100.** Move a proven branch 10% -> 50% -> 100% across separate traffic-split calls, watching analytics between steps, rather than one 0 -> 100 jump.487. **Traffic split is not the same as explicit branch selection.** `current_live_percentage` only governs how un-pinned live traffic is auto-routed. A branch at 0% can still receive conversations when it is selected explicitly (a `branch_id` passed via the API, or an explicit branch pin in a client). Never tell a user that a 0% branch gets no conversations, and account for explicitly-targeted branches when reasoning about where recent conversations came from.4950## 4. Merge5152Merge folds a branch into its parent. Always run a merge preview first to show the diff before merging. The merge endpoint is:5354```55POST /v1/convai/agents/{agent_id}/branches/{source_branch_id}/merge56```5758Merging is destructive on the target and creates a new version on it. If main is `admin_perms_required` and the user lacks the role, the merge fails with a permission error; do not retry. Offer the traffic-split A/B path instead, or escalate to support.5960## 5. Recovery6162- A bare validation failure is almost always the traffic split not summing to 100 or an array shape issue. Re-read the branch list, recompute the full allocation over active branches to total exactly 100, resend. Do not retry the identical payload.63- not_found means the branch id is stale, archived, or a name was passed. Re-read the branch list, copy the exact current id, retry. For a traffic split, drop any branch not present in the fresh list.64- A wrong-state error means you acted before the branch was ready or an uncommitted draft is blocking. Save/commit or discard the draft, then retry.65- A permission error means the target branch is protection-gated and the user lacks the role. Do not retry; offer the traffic-split path or escalate.