# Agent: Branches

**Page:** api/agent/branches

[Download Raw Markdown](./api/agent/branches.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



## Agent: Branches

The Branches API manages the git branches that make up an agent workspace. Each branch is an isolated git worktree with its own filesystem directory, lifecycle status, and disk usage. Use these endpoints to discover branches, inspect their state, create or delete them, sync them with a remote, and open or check pull requests.

All endpoints are scoped to a workspace. The workspace ID is supplied as a path parameter for every operation.

---

## Branch discovery

### `GET /api/v1/workspaces/{workspaceID}/branches`

Returns every branch registered to the current project. Each branch is an isolated git worktree with its own directory, status, and disk usage. The list is bounded by the project's branch count (typically fewer than 50) and is not paginated; callers receive the full array in a single response. Response objects are sanitised — `start_command`, `last_stale_notify`, and `last_disk_notify` are stripped.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `workspaceID` | path | string | Yes | The workspace that owns the branches. |

#### Response



```json
[
  {
    "id": "br_01HXYZABCDEF",
    "path": "/var/hoody/workspaces/ws_main/branches/br_01HXYZABCDEF",
    "branch": "feature/add-oauth",
    "name": "Add OAuth provider",
    "status": "ready",
    "base_branch": "main",
    "base_commit": "f3a9c12d4e5b6789",
    "created_at": 1715000000000,
    "updated_at": 1715090000000
  },
  {
    "id": "br_01HXYZGHIJKL",
    "path": "/var/hoody/workspaces/ws_main/branches/br_01HXYZGHIJKL",
    "branch": "fix/null-pointer",
    "name": "Fix null pointer in parser",
    "status": "resetting",
    "base_branch": "main",
    "base_commit": "a1b2c3d4e5f60718",
    "created_at": 1715100000000,
    "updated_at": 1715100500000
  }
]
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid project context"
    }
  ],
  "data": null
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `INVALID_PROJECT_CONTEXT` | Project context is invalid | The request reached the branches router but the attached project instance could not be resolved (missing or malformed project id). | Ensure the request is routed through a valid workspace/project scope before calling this endpoint. |


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `PROJECT_NOT_FOUND` | Project record not found | The project record referenced by the current instance no longer exists on disk (deleted between instance creation and this call). | Re-open the project or select a different one; do not retry with the same stale id. |



#### SDK

```ts
await client.agent.branches.listBranches({
  workspaceID: "ws_main"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/remote`

Returns the git remotes configured for the project, including provider detection, repository coordinates, and whether the stored credentials are valid.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `workspaceID` | path | string | Yes | The workspace whose remotes should be inspected. |

#### Response



```json
{
  "remotes": [
    {
      "name": "origin",
      "url": "git@github.com:acme/widgets.git",
      "provider": "github",
      "owner": "acme",
      "repo": "widgets",
      "authenticated": true
    },
    {
      "name": "upstream",
      "url": "https://github.com/official/widgets.git",
      "provider": "github",
      "owner": "official",
      "repo": "widgets",
      "authenticated": false
    }
  ],
  "defaultRemote": "origin"
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getRemoteInfo({
  workspaceID: "ws_main"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/remote-refs`

Fetches the set of branches and tags visible on a configured git remote via `git ls-remote`. The `remote` query parameter selects which remote to inspect; when omitted, the router uses the project's default (`origin` when present). The response is a point-in-time snapshot of remote refs and is not paginated — very large mirrors may return a sizeable payload. Rate-limited per remote to avoid upstream abuse.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `remote` | query | string | No | The name of the remote to inspect. Defaults to the project's default remote. |
| `workspaceID` | path | string | Yes | The workspace whose remote refs should be listed. |

#### Response



```json
{
  "branches": [
    "refs/heads/main",
    "refs/heads/release/2024-q1",
    "refs/heads/feature/agent-branches"
  ],
  "tags": [
    "refs/tags/v1.0.0",
    "refs/tags/v1.1.0"
  ]
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Unsafe remote name"
    }
  ],
  "data": null
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `INVALID_REMOTE_NAME` | Remote name is unsafe or malformed | The supplied `remote` query parameter failed `assertSafeRemoteName` validation (contains disallowed characters, path separators, or shell metacharacters). | Pass a plain remote identifier such as `origin` or `upstream`; avoid spaces, slashes, and quoting. |
| `REMOTE_RATE_LIMITED` | Remote ref listing was rate-limited | The per-remote rate limiter rejected this request because recent listings against the same remote exceeded the allowed frequency. | Back off and retry after the limiter's cool-down window; avoid polling `remote-refs` in tight loops. |


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Remote 'upstream' not configured"
  }
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `REMOTE_NOT_CONFIGURED` | Remote is not configured on this project | The requested remote name does not match any remote registered in the project's git configuration. | Call `GET /branches/remote` first to discover configured remotes, or add the remote before listing its refs. |



#### SDK

```ts
await client.agent.branches.listRemoteRefs({
  workspaceID: "ws_main",
  remote: "origin"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/disk-usage`

Returns the on-disk size of one or all branches in the workspace. When the `id` query parameter is omitted, the response contains an entry for every branch.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | query | string | No | The branch id to measure. Omit to measure every branch in the workspace. |
| `workspaceID` | path | string | Yes | The workspace whose branches should be measured. |

#### Response



```json
[
  {
    "id": "br_01HXYZABCDEF",
    "directory": "/var/hoody/workspaces/ws_main/branches/br_01HXYZABCDEF",
    "bytes": 482310984
  },
  {
    "id": "br_01HXYZGHIJKL",
    "directory": "/var/hoody/workspaces/ws_main/branches/br_01HXYZGHIJKL",
    "bytes": 15728329
  }
]
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getBranchDiskUsage({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

## Branch status and diff

### `GET /api/v1/workspaces/{workspaceID}/branches/{id}/status`

Returns the live git status of a single branch — its name, how far ahead or behind its upstream it is, the count of staged/unstaged/untracked files, and a `clean` flag.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Response



```json
{
  "branch": "feature/add-oauth",
  "ahead": 3,
  "behind": 0,
  "staged": 4,
  "unstaged": 1,
  "untracked": 0,
  "clean": false
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getBranchStatus({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/{id}/diff`

Returns the diff between the branch and its base (or another reference). The `format` parameter controls whether the response is a summary or full hunk data; the `file` parameter scopes the diff to a single path.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `base` | query | string | No | The base ref to diff against. Defaults to the branch's base branch. |
| `file` | query | string | No | Restrict the diff to a single file path. |
| `format` | query | string | No | Output format. One of `summary`, `full`. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Response



```json
{
  "base_commit": "f3a9c12d4e5b6789",
  "head_commit": "9b8c7d6e5f4a3210",
  "files": [
    {
      "path": "src/oauth/provider.ts",
      "status": "modified",
      "insertions": 42,
      "deletions": 7,
      "hunks": [
        {
          "old_start": 12,
          "old_lines": 5,
          "new_start": 12,
          "new_lines": 8,
          "lines": [
            { "type": "context", "content": "import { Client } from './client';" },
            { "type": "remove", "content": "const TOKEN_TTL = 3600;" },
            { "type": "add", "content": "const TOKEN_TTL_MS = 60 * 60 * 1000;" },
            { "type": "context", "content": "" }
          ]
        }
      ]
    }
  ],
  "truncated": false
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getBranchDiff({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF",
  format: "full"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/{id}/remote-status`

Returns whether the branch has an upstream tracking ref and, if so, how many commits it is ahead or behind.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Response



```json
{
  "hasUpstream": true,
  "ahead": 3,
  "behind": 1,
  "remoteBranch": "origin/feature/add-oauth"
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getRemoteStatus({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `GET /api/v1/workspaces/{workspaceID}/branches/{id}/pr`

Returns the current state of a pull/merge request for the branch — whether one exists, its URL and number, its provider state, CI status, and review status.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Response



```json
{
  "exists": true,
  "url": "https://github.com/acme/widgets/pull/42",
  "number": 42,
  "state": "open",
  "ci": {
    "status": "success",
    "url": "https://github.com/acme/widgets/actions/runs/123456"
  },
  "reviewStatus": "approved"
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.getPRStatus({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

## Branch lifecycle

### `POST /api/v1/workspaces/{workspaceID}/branches`

Creates a new branch as an isolated git worktree. The new branch can be given a name, a base branch to fork from, and an optional start command that runs after the worktree is provisioned.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `workspaceID` | path | string | Yes | The workspace in which to create the branch. |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | No | The human-readable display name for the branch. |
| `startCommand` | string | No | A command to run inside the new worktree once it is provisioned. |
| `baseBranch` | string | No | The branch to fork from. Defaults to the project's default branch. |

#### Response



```json
{
  "id": "br_01HMNOPQRSTU",
  "path": "/var/hoody/workspaces/ws_main/branches/br_01HMNOPQRSTU",
  "branch": "feature/billing-portal",
  "name": "Billing portal MVP",
  "status": "creating",
  "base_branch": "main",
  "base_commit": "f3a9c12d4e5b6789",
  "created_at": 1715200000000,
  "updated_at": 1715200000000
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.createBranch({
  workspaceID: "ws_main",
  data: {
    name: "Billing portal MVP",
    baseBranch: "main",
    startCommand: "pnpm install && pnpm dev"
  }
});
```

---

### `PATCH /api/v1/workspaces/{workspaceID}/branches/{id}`

Renames the branch's human-readable display name. The underlying git branch ref is not changed.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | Yes | The new display name for the branch. |

#### Response



```json
{
  "id": "br_01HXYZABCDEF",
  "path": "/var/hoody/workspaces/ws_main/branches/br_01HXYZABCDEF",
  "branch": "feature/add-oauth",
  "name": "Add OAuth provider (rev 2)",
  "status": "ready",
  "base_branch": "main",
  "base_commit": "f3a9c12d4e5b6789",
  "created_at": 1715000000000,
  "updated_at": 1715210000000
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.renameBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF",
  data: { name: "Add OAuth provider (rev 2)" }
});
```

---

### `DELETE /api/v1/workspaces/{workspaceID}/branches/{id}`

Deletes a branch and its underlying git worktree.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint takes no parameters beyond the path above.

#### Response



```json
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.deleteBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/reset`

Resets a branch back to its base commit. While the reset is in progress the branch's `status` is set to `resetting`; once complete it returns to `ready`.


Resetting discards any uncommitted or unpushed changes on the branch.


#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint takes no parameters beyond the path above.

#### Response



```json
{
  "id": "br_01HXYZABCDEF",
  "path": "/var/hoody/workspaces/ws_main/branches/br_01HXYZABCDEF",
  "branch": "feature/add-oauth",
  "name": "Add OAuth provider",
  "status": "resetting",
  "base_branch": "main",
  "base_commit": "f3a9c12d4e5b6789",
  "created_at": 1715000000000,
  "updated_at": 1715300000000
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.resetBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/retry`

Retries a branch whose provisioning or operation previously failed. Only branches in the `failed` state are eligible.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint takes no parameters beyond the path above.

#### Response



```json
{
  "id": "br_01HXYZGHIJKL",
  "path": "/var/hoody/workspaces/ws_main/branches/br_01HXYZGHIJKL",
  "branch": "fix/null-pointer",
  "name": "Fix null pointer in parser",
  "status": "creating",
  "base_branch": "main",
  "base_commit": "a1b2c3d4e5f60718",
  "created_at": 1715100000000,
  "updated_at": 1715310000000
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.retryBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZGHIJKL"
});
```

---

## Branch sync and pull requests

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/pull`

Pulls the latest commits from the branch's upstream (or a specified remote) into the local worktree. Returns any conflict paths if the pull cannot be fast-forwarded.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint accepts an empty body.

#### Response



```json
{
  "success": true,
  "conflicts": [],
  "message": "Already up to date."
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.pullBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/push`

Pushes the branch to its remote. When successful the response includes the pushed `ref` and the remote name. This endpoint is rate-limited; callers that exceed the limit receive a 429 response with a `retryAfterMs` hint.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint accepts an empty body.

#### Response



```json
{
  "success": true,
  "ref": "refs/heads/feature/add-oauth",
  "remote": "origin",
  "url": "https://github.com/acme/widgets",
  "message": "Branch pushed successfully."
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```


```json
{
  "error": "Rate limit exceeded",
  "retryAfterMs": 30000
}
```



#### SDK

```ts
await client.agent.branches.pushBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```

---

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/merge`

Merges the branch into its base. The `strategy` field selects squash, rebase, or a true merge commit. Set `dry_run` to validate the merge without applying it, and `deleteBranch` to remove the source branch on success.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id to merge. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `strategy` | string | No | Merge strategy. One of `squash`, `rebase`, `merge`. |
| `message` | string | No | Custom commit message. |
| `dry_run` | boolean | No | When `true`, validates the merge without applying it. |
| `deleteBranch` | boolean | No | When `true`, deletes the source branch on a successful merge. |

#### Response



```json
{
  "success": true,
  "sha": "9b8c7d6e5f4a3210",
  "head_commit": "9b8c7d6e5f4a3210",
  "already_merged": false,
  "target": "main",
  "conflicts": [],
  "message": "Merge successful."
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.mergeBranch({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF",
  data: {
    strategy: "squash",
    message: "feat: add OAuth provider",
    deleteBranch: true
  }
});
```

---

### `POST /api/v1/workspaces/{workspaceID}/branches/{id}/pr`

Opens a pull (or merge) request from the branch against its target. The input body is empty by contract — the PR is created from the branch's current state.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `id` | path | string | Yes | The branch id. |
| `workspaceID` | path | string | Yes | The workspace that owns the branch. |

This endpoint accepts an empty body.

#### Response



```json
{
  "success": true,
  "url": "https://github.com/acme/widgets/pull/43",
  "number": 43,
  "provider": "github",
  "message": "Pull request opened."
}
```


```json
{
  "success": false,
  "errors": [
    {
      "message": "Invalid request"
    }
  ],
  "data": null
}
```


```json
{
  "name": "NotFoundError",
  "data": {
    "message": "Project not found"
  }
}
```



#### SDK

```ts
await client.agent.branches.createPR({
  workspaceID: "ws_main",
  id: "br_01HXYZABCDEF"
});
```