# Branches and events (/api-v2/branches)



Branches are explicit and append-only. Every event append uses optimistic concurrency: you state the `expected_version` and `expected_head_event_id` you are writing against, and a mismatch returns `409 branch_version_conflict` rather than silently rewriting history. Branches are prefixed `br_` and events `evt_`. See [branches](/concepts/branches) and the [sessions and branching guide](/guides/sessions-and-branching).

All requests require a bearer API key. See [authentication](/api-reference/authentication).

## Fork a branch [#fork-a-branch]

`POST /v2/sessions/{session_id}/branches`

Creates a new branch off an existing one, optionally pinned to a specific event so the fork inherits exactly the history up to that point.

<ParamField path="session_id" type="string">
  The session the branch belongs to.
</ParamField>

<ParamField body="fork_from_branch_id" type="string">
  The `br_...` branch to fork from. Must belong to this session.
</ParamField>

<ParamField body="fork_from_event_id" type="string">
  Optional `evt_...` event on the source branch to fork at. When set, it must belong to the fork-source branch, and the new branch's head is pinned there. When omitted, the new branch inherits the source branch's current head.
</ParamField>

```bash
curl https://api.zumik.ai/v2/sessions/ses_01jy7n7w2z9pcu7a5q2gh8askm/branches \
  -H "Authorization: Bearer $ZUMIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fork_from_branch_id": "br_01jy7n7w30ardv8b6r3jk9btln",
    "fork_from_event_id": "evt_01jy7naa12b3c4d5e6f7g8h9jk"
  }'
```

```json
{
  "id": "br_01jy7nbb45c6d7e8f9g0h1j2km",
  "object": "session_branch",
  "session_id": "ses_01jy7n7w2z9pcu7a5q2gh8askm",
  "parent_branch_id": "br_01jy7n7w30ardv8b6r3jk9btln",
  "forked_from_event_id": "evt_01jy7naa12b3c4d5e6f7g8h9jk",
  "head_event_id": "evt_01jy7naa12b3c4d5e6f7g8h9jk",
  "version": 3
}
```

<ResponseField name="id" type="string">
  Opaque branch id, prefixed `br_`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `session_branch`.
</ResponseField>

<ResponseField name="session_id" type="string">
  The owning session.
</ResponseField>

<ResponseField name="parent_branch_id" type="string">
  The branch this one was forked from, or `null` for a root branch.
</ResponseField>

<ResponseField name="forked_from_event_id" type="string">
  The event the fork was pinned at, or `null`.
</ResponseField>

<ResponseField name="head_event_id" type="string">
  The current head event, or `null` on an empty branch.
</ResponseField>

<ResponseField name="version" type="integer">
  The branch's monotonic version. Inherited from the parent at fork time.
</ResponseField>

## Retrieve a branch [#retrieve-a-branch]

`GET /v2/sessions/{session_id}/branches/{branch_id}`

<ParamField path="session_id" type="string">
  The session the branch belongs to.
</ParamField>

<ParamField path="branch_id" type="string">
  The branch to fetch.
</ParamField>

```bash
curl https://api.zumik.ai/v2/sessions/ses_01jy7n7w2z9pcu7a5q2gh8askm/branches/br_01jy7n7w30ardv8b6r3jk9btln \
  -H "Authorization: Bearer $ZUMIK_API_KEY"
```

Returns the same branch object as fork. Read it first to learn the current `version` and `head_event_id` before appending.

## Append an event [#append-an-event]

`POST /v2/sessions/{session_id}/branches/{branch_id}/events`

Appends one immutable event. The append succeeds only if your stated `expected_version` and `expected_head_event_id` match the branch's current state; otherwise it fails with `409 branch_version_conflict` and the branch is left untouched. On success the branch version increments by one and its head advances to the new event.

<ParamField path="session_id" type="string">
  The session the branch belongs to.
</ParamField>

<ParamField path="branch_id" type="string">
  The branch to append to.
</ParamField>

<ParamField body="expected_version" type="integer">
  The branch version you are writing against. Use the `version` from a recent retrieve. A root branch starts at `0`.
</ParamField>

<ParamField body="expected_head_event_id" type="string">
  The `evt_...` you expect to be the current head, or `null` when the branch is empty. Must match the branch's actual head.
</ParamField>

<ParamField body="event" type="object">
  The event to append.

  <Expandable title="event">
    <ParamField body="event_type" type="string">
      One of `user_message`, `assistant_message`, `tool_result`, `retrieval_result`, `checkpoint`, `note`.
    </ParamField>

    <ParamField body="payload_ref" type="string">
      Optional `art_...` artifact holding the event payload. Must exist in this project.
    </ParamField>
  </Expandable>
</ParamField>

```bash
curl https://api.zumik.ai/v2/sessions/ses_01jy7n7w2z9pcu7a5q2gh8askm/branches/br_01jy7n7w30ardv8b6r3jk9btln/events \
  -H "Authorization: Bearer $ZUMIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expected_version": 0,
    "expected_head_event_id": null,
    "event": {
      "event_type": "user_message",
      "payload_ref": "art_01jy7n3q8v6kzr4w2m9bd5xpfh"
    }
  }'
```

```json
{
  "id": "evt_01jy7naa12b3c4d5e6f7g8h9jk",
  "object": "session_event",
  "session_id": "ses_01jy7n7w2z9pcu7a5q2gh8askm",
  "branch_id": "br_01jy7n7w30ardv8b6r3jk9btln",
  "sequence": 1,
  "event_type": "user_message",
  "parent_event_id": null,
  "payload_ref": "art_01jy7n3q8v6kzr4w2m9bd5xpfh",
  "created_at": "2026-06-15T16:08:33Z"
}
```

<ResponseField name="id" type="string">
  Opaque event id, prefixed `evt_`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `session_event`.
</ResponseField>

<ResponseField name="session_id" type="string">
  The owning session.
</ResponseField>

<ResponseField name="branch_id" type="string">
  The branch the event was appended to.
</ResponseField>

<ResponseField name="sequence" type="integer">
  The event's position on the branch (the new branch version).
</ResponseField>

<ResponseField name="event_type" type="string">
  The event type, echoed back.
</ResponseField>

<ResponseField name="parent_event_id" type="string">
  The prior head event, or `null` for the first event.
</ResponseField>

<ResponseField name="payload_ref" type="string">
  The referenced artifact, or `null`.
</ResponseField>

<ResponseField name="created_at" type="string">
  RFC 3339 creation timestamp.
</ResponseField>

### Handling a conflict [#handling-a-conflict]

When another writer advanced the branch first, the append fails:

```json
{
  "error": {
    "message": "Branch 'br_01jy7n7w30ardv8b6r3jk9btln' is at version 1 with head Some(\"evt_...\"), not the expected version/head.",
    "type": "invalid_request_error",
    "code": "branch_version_conflict"
  }
}
```

Re-read the branch to learn the new `version` and `head_event_id`, rebase your intended event onto that head, and retry.

## Errors [#errors]

| Status | Code                      | When                                                                                                                   |
| ------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 400    | `invalid_request_error`   | The fork source branch or event does not belong to the session, or `event.payload_ref` does not exist in this project. |
| 401    | `invalid_api_key`         | Missing or invalid API key.                                                                                            |
| 404    | `invalid_request_error`   | The session or branch does not exist in this project.                                                                  |
| 409    | `branch_version_conflict` | `expected_version` or `expected_head_event_id` did not match the branch's current state.                               |

See the full table on [errors](/api-reference/errors).
