Scripting: JSON output, exit and error codes, environment variables, the api command and scripting guides # API command - Raw authenticated requests > Call any Bitbucket Cloud 2.0 API endpoint through the CLI's authenticated stack, the escape hatch for endpoints not yet wrapped by a typed command. Mirrors gh api. `bb api` calls Bitbucket Cloud 2.0 endpoints using your configured credentials, OAuth refresh, and the CLI’s retry settings. Use it for endpoints without a typed command. See [Global flags](/reference/global-flags/) for output and context options. `--no-truncate` and `--locale` have no effect because this command prints no tables or dates. ## `bb api` ```bash bb api [options] [method] ``` ### Arguments | Argument | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `method` | Optional HTTP verb (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`), case-insensitive. When omitted, defaults to `GET`, or `POST` when fields/body are present. Both `bb api GET /user` and `bb api /user` work. | | `endpoint` | The API path, relative to `https://api.bitbucket.org/2.0` (a leading `/` is optional). A redundant leading `/2.0` is stripped, so paths copied straight out of the Bitbucket REST docs (`/2.0/user`) work unchanged. `{workspace}` and `{repo}` placeholders are substituted from `--workspace`/`--repo` or the current repository. | ### Options | Option | Description | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-X, --method ` | HTTP method. Overrides a positional verb. | | `-f, --raw-field ` | Add a string parameter. Query param on `GET`/`HEAD`, JSON body field otherwise. Repeatable. | | `-F, --field ` | Add a typed parameter: `true`/`false`/`null` and numbers are converted; `@file` reads a file and `@-` reads stdin. Repeatable. | | `--input ` | Read the raw request body from a file, or `-` for stdin. Sent as `application/json` (override with `-H 'Content-Type: ...'`). Mutually exclusive with `-f`/`-F`. | | `-H, --header ` | Add a request header. Repeatable. `Authorization` is managed automatically and cannot be set here. | | `-i, --include` | Print the HTTP status line and response headers before the body, on success and failure (text mode only, suppressed under `--json`). | | `--paginate` | Follow the cursor (`next`) across pages and merge every page into a single `{ "values": [...] }` result (`GET`/`HEAD` only). | | `--dry-run` | Print the write request instead of sending it ([details](/reference/global-flags/#--dry-run)) | ### Argument errors Put bare `--json` after the endpoint. Its optional field-list argument can otherwise consume the HTTP verb or endpoint. At most two positional arguments are accepted. With two, the first must be an HTTP verb. Missing endpoints and invalid verbs fail before sending a request. ```bash bb api # missing endpoint bb api GET # missing endpoint after method bb api GTE /user # invalid method bb api GET /user /extra # too many arguments ``` ### Examples ```bash # Current user bb api /user # Explicit method (equivalent to the above) bb api GET /user # List every pull request, following pagination, using the current repository bb api /repositories/{workspace}/{repo}/pullrequests --paginate # Trigger a pipeline (a JSON body from a file; the target object is nested) bb api POST /repositories/my-ws/my-repo/pipelines/ --input pipeline.json # -F applies gh-style magic typing: true/false/null and numbers become JSON literals bb api PUT /repositories/my-ws/my-repo -F is_private=true # Send a JSON body from a file bb api PUT /repositories/my-ws/my-repo/pullrequests/42 --input body.json # Pipe a body in from stdin cat body.json | bb api POST /repositories/my-ws/my-repo/pullrequests/42/comments --input - # Force a GET with query parameters (fields normally infer POST) bb api GET /repositories/my-ws/my-repo/pullrequests -f state=MERGED # Filter the response with jq (no --json needed — bb api output is already JSON) bb api /repositories/my-ws --jq '.values[].name' # Inspect the status line and response headers bb api -i /user ``` ### How fields are sent * **`GET` / `HEAD`**, `-f`/`-F` are appended as a URL query string (`?key=value&...`). * **Every other method**, `-f`/`-F` are assembled into a JSON request body. * Repeating the same key turns it into an array. * Field keys are literal. `-f target.commit=abc1234` does not create a nested `target` object. Use `--input` for nested JSON. ### Placeholders `{workspace}` and `{repo}` are resolved only when they actually appear in the path, so `bb api /user` works outside a checkout. ```bash # Inside a Bitbucket checkout, these resolve automatically: bb api /repositories/{workspace}/{repo}/commits # Or override explicitly: bb api -w my-ws -r my-repo /repositories/{workspace}/{repo}/commits ``` ### Output The response body is printed to stdout. JSON responses are pretty-printed on a terminal and compact when piped, and respect the global `--json [fields]` projection, `--lean`, and `--jq` filtering. Non-JSON responses (e.g. raw diffs) are printed verbatim, and `--json`/`--jq` are inert on them. An empty response body (e.g. a `HEAD` or `204`) prints nothing in text mode, and `{}` under `--json`, so a downstream `jq` never sees zero bytes. ```bash bb api /user ``` ```json { "type": "user", "username": "my-user", "display_name": "My User", "account_id": "557058:..." } ``` `bb api` is the only command where `--jq` works without `--json`; everywhere else a bare `--jq` fails with `✗ --jq requires --json`. On a paginated endpoint, `--json ` unwraps the `values` array and drops the envelope (`pagelen`, `size`, `next`), returning a bare array of projected objects. Projection runs before `--jq`, so once you pass `--json ` the expression sees that flat array, use `.[]`, not `.values[]`. ```bash # Envelope intact bb api /repositories/my-ws --jq '.values[].name' # Projected and unwrapped bb api /repositories/my-ws --json name --jq '.[].name' ``` ### Notes * **Authentication is automatic.** The same credentials as the rest of the CLI are attached to every request; you cannot (and need not) set `Authorization` yourself. * **Absolute endpoints require HTTPS on the Bitbucket API host.** Use `https://api.bitbucket.org/...` without embedded URL credentials. Malformed URLs, protocol-relative paths such as `//host/path`, backslashes and raw control characters are rejected. Pagination cursors follow the same rules. Relative endpoints such as `/user` and `/2.0/user` are still accepted. * **`--paginate` degrades instead of erroring.** On a non-`GET`/`HEAD` request it prints `--paginate only applies to GET/HEAD requests; ignoring it.` on stderr and sends the request unpaginated. If the first response has no `values` array, a single resource rather than a collection, it prints `--paginate: response has no "values" array; returning the first page only.` and returns page 1. Both warnings are suppressed under `--json`. Combined with `-i`, the status line and headers printed are the first page’s. * **Errors surface the API response.** On a non-2xx status the command exits non-zero and prints the upstream exchange to **stderr**, after the `✗` line: by default just the error body; with `-i/--include`, the status line and response headers first. A body that merely repeats the message (a plain-text `Bad Request`) is not echoed twice. With `--json`, a single compact error object is emitted on stderr carrying `statusCode`, `response` (the body), `headers`, and `statusText` when the transport provides one. A `401` or `403` also carries a `hint` with the next step to take. The generic `404` hint is deliberately suppressed here, you supplied the URL yourself, so advice about `--workspace`/`--repo` would not apply. Unlike success output, error bodies are **not** field-projected or `--jq`-filtered. ```bash # The failing request's status line, headers and body all land on stderr bb api GET /repositories/my-ws/no-such-repository -i # Structured error (status + headers + body) for scripts bb api GET /repositories/my-ws/no-such-repository --json 2> err.json jq '.statusCode, .response, .headers' err.json ``` * **Retries and redaction are inherited** from the shared API client, rate limits (`429`) and the transient gateway errors `502`/`503`/`504` are retried with backoff, and sensitive values are redacted from `BB_DEBUG` traces. ### Related * [Scripting & Automation](/guides/scripting/), combine `bb api` with `--jq` to build pipelines over endpoints without a typed command. * [JSON Output](/reference/json-output/), how `--json` projection and `--jq` filtering work across the CLI. * [Authentication](/getting-started/authentication/), the auth methods that `bb api` reuses. # AI Agent Integration - Claude Code, Cursor, Windsurf & More > Set up the Bitbucket CLI with Claude Code, opencode, Cursor, Windsurf, and other AI coding assistants. Skill files, quick-start guides, and real-world workflow examples. Drop a skill or rules file into your project and your coding assistant drives Bitbucket through `bb`. The templates below cover Claude Code, opencode, Cursor, and Windsurf. ## Quick start Install `bb` and authenticate yourself first. A [standalone binary](/getting-started/installation/#standalone-binary) needs no runtime; the npm package requires Bun 1.1.30 or newer, and Windows `--jq` requires Bun 1.4.2 or newer. ```bash bb auth login bb auth status bb doctor ``` `bb doctor --json` gives an agent a pass/warn/fail row per check and exits 1 when something is broken. Ask your assistant to run `bb --help`, then `bb pr list --all --json`. For repeated use, put these instructions in its project rules or skill file: ```markdown ## Bitbucket CLI Use `bb` for Bitbucket Cloud operations. - Run `bb --help` and `bb --help` before using unfamiliar flags. - Use `--json` after the command for structured output. Use `--all` on list commands that support it when the task needs every result. Add `--lean` to drop link noise, or `--jq --raw-output` to get plain strings. - Workspace and repository come from the Bitbucket git remote. Pass -w and -r explicitly when operating outside that checkout. - Read PRs with `bb pr view `, `bb pr diff `, and `bb pr comments list --all --unresolved`. - `bb pr checks ` reads every build status. `bb pr checks --watch` waits until none is in progress and exits non-zero if any failed; `bb pipeline watch` does the same for a pipeline run. - Use `bb api ` for operations without a typed command. - Follow the user's approval rules before posting comments, approving, pushing, merging, or deleting resources. - Add `--dry-run` to a write command to show the exact request (method, URL, body) without sending it. - Never read, print, or copy credentials. Ask the user to authenticate. Without a terminal, `bb auth login` fails with code 1001 instead of opening a browser. ``` * Claude Code Save the shared instructions above in `.claude/skills/bb-cli/SKILL.md`, with this frontmatter at the top: ```yaml --- name: bb-cli description: Use the bb CLI to inspect and manage Bitbucket Cloud repositories and pull requests. --- ``` Invoke `/bb-cli`, or ask a question that matches its description. For a personal skill, use `~/.claude/skills/bb-cli/SKILL.md`. See [Claude Code skills](https://code.claude.com/docs/en/skills). * opencode Add the shared instructions to the project’s `AGENTS.md`. Preserve any instructions already in that file. See [OpenCode rules](https://opencode.ai/docs/rules/). * Cursor Add the shared instructions to `AGENTS.md`, or create `.cursor/rules/bb-cli.mdc` with this frontmatter followed by the instructions: ```yaml --- description: Use bb for Bitbucket Cloud operations alwaysApply: true --- ``` See [Cursor rules](https://cursor.com/docs/rules). * Windsurf Add the shared instructions to the project’s root `AGENTS.md`. See [Windsurf rules](https://docs.windsurf.com/windsurf/cascade/memories). ## Example workflows ### Create and submit a PR After the user authorizes a push and PR creation: ```bash git push -u origin feat/input-validation bb pr create -t "Add input validation" -b "Validates email and password fields." --draft ``` ### Review multiple PRs at once ```bash bb pr list --all --json bb pr view 42 --json bb pr diff 42 bb pr comments list 42 --all --unresolved --json ``` The list and diff do not prove merge readiness. Inspect approvals and required checks, and follow the repository’s merge policy. ### Full review-and-merge cycle Review the changes and run the relevant tests before requesting approval to merge. For unattended merge logic, use the [CI checks recipe](/recipes/auto-merge-on-ci-green/). ```bash bb pr checkout 42 bb pr diff 42 # After the user authorizes approval and merge: bb pr approve 42 bb pr merge 42 --strategy squash --close-source-branch ``` ### Work an open review thread Read unresolved threads, fix the code, then post and resolve only when authorized: ```bash bb pr comments list 45 --all --unresolved bb pr comments reply 45 109820 "Capped retries at 3 attempts in a1b2c3d." bb pr comments resolve 45 109820 ``` For multi-line markdown, pass the text on stdin with `-F -` and a quoted heredoc, so backticks and `$` reach Bitbucket as written. The same flag works on `bb pr create`, `bb pr edit` and `bb pr comments add`: ```bash bb pr comments reply 45 109820 -F - <<'EOF' Capped retries at 3 attempts in `a1b2c3d`. EOF ``` ### Draft PR workflow ```bash bb pr create -t "WIP: New dashboard" --draft bb pr ready 48 bb pr reviewers add 48 '{alice-uuid}' ``` ## Security Caution Never commit API tokens to version control. Use environment variables or `bb auth login`. Normal `bb` commands use stored credentials internally, so the assistant does not need a token in its prompt. This is not a security boundary: an agent with file or shell access can read the config (plaintext unless `credentialStorage` is `keychain`) or run `bb auth token`. Use your assistant’s tool permissions to control that access. Rule and skill files provide instructions, not credential isolation. For CI/CD or shared environments, use environment variables: ```bash export BB_USERNAME="you@example.com" export BB_API_TOKEN="your-api-token" bb auth login # BB_USERNAME contains your Atlassian account email ``` ### Required API token scopes Choose scopes for the operations your agent needs, using the [command-to-scope reference](/reference/token-scopes/). Read-only access still requires the appropriate read scope for each resource. ### OAuth agents The CLI requests `account repository repository:admin pullrequest pullrequest:write`. The OAuth consumer’s configured scopes determine the resulting grant; this request does not narrow a consumer configured with more scopes. For commands outside repository and PR work, use an API token with the required scopes or configure a custom consumer with those scopes. See [Authentication](/getting-started/authentication/#using-a-custom-oauth-consumer) and [Atlassian’s OAuth scope rules](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/). ## Troubleshooting | Issue | Solution | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | “Not authenticated” | Run `bb auth login`; for tokens, set `BB_USERNAME` to your account email and `BB_API_TOKEN` before login | | “Browser login needs an interactive terminal” | The agent has no terminal, so OAuth can’t finish. Set `BB_USERNAME` and `BB_API_TOKEN`, or run `bb auth login` yourself | | Claude doesn’t use `bb` | Restart Claude Code after creating the skill file. Check `/bb-cli` shows in the slash menu | | “Could not determine repo” | Run from a git repo with a Bitbucket remote, or pass `-w`/`-r` flags explicitly | | Skill not auto-activating | Invoke manually with `/bb-cli`, or check the `description` field in SKILL.md | | Cursor rule not loading | Ensure the file is at `.cursor/rules/bb-cli.mdc` with valid frontmatter. Restart Cursor | | opencode ignoring AGENTS.md | Ensure the file is in the project root (not a subdirectory). Restart opencode | | `bb` command not found | Install with `npm install -g @pilatos/bitbucket-cli`. The npm package requires Bun 1.1.30+; standalone binaries need no runtime | | AI using wrong flags | Tell the AI to run `bb --help` first | | Windsurf not picking up rules | Ensure the shared instructions are in the root `AGENTS.md` | *** ## `llms.txt` for IDE agents The docs site publishes the [`llms.txt`](https://llmstxt.org/) convention so agents that fetch documentation on demand (Cursor, Cline, Continue, Aider, and similar) can pull a clean markdown view of the full reference instead of scraping HTML: | URL | Contents | | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | [`/llms.txt`](https://bitbucket-cli.paulvanderlei.com/llms.txt) | Index of every page with a one-line description, plus the sets below | | [`/.md`](https://bitbucket-cli.paulvanderlei.com/commands/pr.md) | Any single page as markdown: add `.md` to its path, e.g. `/commands/pr.md` | | [`/_llms-txt/pull-requests.txt`](https://bitbucket-cli.paulvanderlei.com/_llms-txt/pull-requests.txt) | Topic set: every `pr` page plus the reviewer and auto-merge recipes | | [`/_llms-txt/pipelines-and-ci.txt`](https://bitbucket-cli.paulvanderlei.com/_llms-txt/pipelines-and-ci.txt) | Topic set: pipelines, deployments, commit statuses and CI/CD setup | | [`/_llms-txt/scripting.txt`](https://bitbucket-cli.paulvanderlei.com/_llms-txt/scripting.txt) | Topic set: JSON output, error codes, environment variables and scripting | | [`/llms-full.txt`](https://bitbucket-cli.paulvanderlei.com/llms-full.txt) | Every doc page concatenated as markdown | | [`/llms-small.txt`](https://bitbucket-cli.paulvanderlei.com/llms-small.txt) | Abridged subset (excludes Help / FAQ / Changelog) for tight context windows | Start an agent at `/llms.txt` so it fetches only the pages it needs. All of these are generated at build time, so they always match the live docs. Give your assistant one of these URLs when it needs the reference. Automatic discovery depends on the assistant; publishing the files does not guarantee that it will fetch them. *** ## Related guides * [Scripting & Automation](/guides/scripting/), JSON output, exit codes, and shell scripting patterns * [CI/CD Integration](/guides/cicd/), Use `bb` in GitHub Actions, GitLab CI, Jenkins, and more * [JSON Output Reference](/reference/json-output/), Detailed JSON schemas for all commands * [Error Codes](/reference/error-codes/), the numeric `code` values in `--json` error output * [Command Reference](/commands/pr/), Complete PR command documentation # Understanding Repository Context > How the CLI determines which workspace and repository to use Most commands need a workspace and a repository. If you don’t pass `-w`/`-r`, the CLI works them out from your git remote, then `BB_WORKSPACE`, then your config file, in that order. ## Context resolution order 1. **`-w/--workspace` and `-r/--repo`** on the command line 2. **A Bitbucket git remote** of the current repository (see [Which remote is used](#which-remote-is-used)) 3. **The `BB_WORKSPACE` environment variable** 4. **`defaultWorkspace`** in the config file Sources 3 and 4 only supply a workspace. A repository still has to come from `-r` or the git remote. ### Typical local layout Run `bb` from the clone or one of its subdirectories. It reads that repository’s git remotes to resolve the workspace and repository. ### Examples #### Scenario 1: inside a git repository ```bash # /projects/myrepo has a Bitbucket remote cd /projects/myrepo bb pr list # workspace and repository come from the remote ``` The CLI reads your `.git/config` and extracts the workspace and repository from the remote URL. #### Scenario 2: using command-line flags ```bash bb pr list -w myworkspace -r myrepo ``` Flags always win. With both flags supplied, no git repository is required. #### Scenario 3: using a default workspace ```bash # Environment variable, useful in CI export BB_WORKSPACE=myworkspace # Or persist it in the config file bb config set defaultWorkspace myworkspace bb repo list # workspace-only command, no repository needed ``` `BB_WORKSPACE` wins over `defaultWorkspace`, so a pipeline can override a developer’s persisted default without rewriting the config file. #### Scenario 4: mixed sources ```bash # Inside a clone of gitworkspace/gitrepo cd /projects/myrepo # Override just the workspace bb pr list -w anotherworkspace # anotherworkspace/gitrepo # Override just the repository bb pr list -r anotherrepo # gitworkspace/anotherrepo ``` ## Workspace-only vs repository-scoped commands The two classes fail differently when context is missing. | Class | Commands | Error when unresolved | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | | Workspace-only | `bb repo list`, `bb repo create`, `bb repo clone`, `bb project *`, `bb snippet *`, `bb workspace view`, `bb search code`, `bb webhook * --scope workspace` | `6002 CONTEXT_WORKSPACE_NOT_FOUND` | | Repository-scoped | `bb pr *`, `bb pipeline *`, `bb commit *`, `bb status *`, `bb browse`, `bb repo view`, `bb repo delete`, `bb repo default-reviewers *`, `bb webhook *`, `bb branch-restriction *`, `bb deployment *` | `6001 CONTEXT_REPO_NOT_FOUND` | `bb ssh-key *` and `bb gpg-key *` act on your own account and ignore `-w`/`-r`. `bb api` falls in either class depending on the endpoint: it resolves a workspace only if the endpoint contains `{workspace}`, and a repository only if it contains `{repo}`. ```bash # Placeholders are filled from the same resolution chain bb api /repositories/{workspace}/{repo}/pullrequests ``` If a placeholder can’t be resolved you get `Endpoint uses {repo} but no repository could be resolved. Pass --repo or run inside a Bitbucket repo.` ## Repository argument formats Commands that accept a repository argument support two formats. ### Full format: `workspace/repo` ```bash bb repo view myworkspace/myrepo bb repo delete myworkspace/myrepo --yes ``` ### Short format: `repo` only ```bash bb repo view myrepo ``` The workspace is resolved in the usual order: 1. `-w/--workspace`, if you passed it 2. Otherwise the workspace from the current git remote 3. Otherwise `BB_WORKSPACE` 4. Otherwise `defaultWorkspace` from the config file 5. Otherwise error The git remote wins over your config. Running `bb repo view myrepo` inside a clone of `otherws/otherrepo` resolves to `otherws/myrepo`, even if `defaultWorkspace` is set to something else. ## Supported remote URL formats ### SSH format ```text git@bitbucket.org:workspace/repo.git git@bitbucket.org:workspace/repo ssh://git@bitbucket.org/workspace/repo.git ssh://git@altssh.bitbucket.org:443/workspace/repo.git ``` ### HTTPS format ```text https://bitbucket.org/workspace/repo.git https://bitbucket.org/workspace/repo https://bitbucket.org/workspace/repo/ https://username@bitbucket.org/workspace/repo.git ``` Repository names may contain dots (`my.repo`, `docs.example.com`). Only a trailing `.git` is stripped. ### SSH host aliases A remote like `git@bitbucket-work:workspace/repo.git` works when `bitbucket-work` is a `Host` entry in your SSH config whose `HostName` is `bitbucket.org`. The CLI asks `ssh -G` for the real hostname, the same lookup git does when it connects. ### URL rewrites `url..insteadOf` rules in your git config are applied before the URL is parsed, so a remote such as `bb:workspace/repo` works if it rewrites to a Bitbucket URL. ## Which remote is used 1. `origin`, if it points to Bitbucket 2. Otherwise the Bitbucket remote, if all Bitbucket remotes point to the same repository 3. Otherwise `upstream`, if it is one of them 4. Otherwise the CLI stops with an error and asks for `-w`/`-r` A fork clone with `origin` (your fork) and `upstream` (the parent) both on Bitbucket resolves to your fork. ## Error messages ### Repository could not be resolved Four distinct reasons, all error code `6001 CONTEXT_REPO_NOT_FOUND`. Under `--json` each sets a different `context.reason`. The working directory is not a git repository, `reason: not_a_git_repo`: ```text ✗ Not in a git repository. Use --workspace and --repo options, or run this command from within a Bitbucket repository. ``` The repository has no remotes, `reason: no_remote`: ```text ✗ Git repository has no remote configured. Add a Bitbucket remote with `git remote add origin `, or use --workspace and --repo options, or run this command from within a Bitbucket repository. ``` No remote points to Bitbucket, or `origin` does but its path isn’t `workspace/repo`, `reason: remote_not_bitbucket`. With a single remote the message shows its URL (a password in it is shown as `***`), with several it lists their names: ```text ✗ Remote 'git@github.com:acme/tool.git' is not a Bitbucket URL. Use --workspace and --repo options, or run this command from within a Bitbucket repository. ✗ None of the git remotes (origin, gitlab) is a Bitbucket URL. Use --workspace and --repo options, or run this command from within a Bitbucket repository. ``` Several remotes point to different Bitbucket repositories and neither `origin` nor `upstream` is one of them, `reason: ambiguous_remote`: ```text ✗ Git remotes fork, mirror point to different Bitbucket repositories and neither origin nor upstream is one of them. Use --workspace and --repo options to pick one. ``` Fix any of them with explicit flags: `bb pr list -w myworkspace -r myrepo`. ### No workspace specified ```text ✗ No workspace specified. Use --workspace option or set a default workspace with `bb config set defaultWorkspace `. ``` Error code `6002 CONTEXT_WORKSPACE_NOT_FOUND`, thrown by workspace-only commands. Pass `-w`, export `BB_WORKSPACE`, or set `defaultWorkspace`. ### “Repository not found” The workspace and repository resolved, but the pair doesn’t exist or your token can’t see it. Check the spelling, your access, and that the repository lives in that workspace. See [Error Codes](/reference/error-codes/) for the full list. ## Which approach to use | Situation | Use | | --------------------------- | ------------------------------------------- | | Interactive work in a clone | Nothing, the git remote resolves it | | Scripts and automation | Explicit `-w`/`-r` on every command | | CI pipelines | `BB_WORKSPACE` plus `-r`, or explicit flags | | You live in one workspace | `bb config set defaultWorkspace ` | Don’t set `defaultWorkspace` if you work across several workspaces. A stale default silently sends commands to the wrong place when you’re outside a clone. ## Checking what got resolved ```bash bb repo view # the resolved workspace/repository bb workspace view # the resolved workspace ``` Both accept `--json`. See [JSON Output](/reference/json-output/) for their response shapes. For the full list of global flags including `-w`/`-r`, see [Global Flags](/reference/global-flags/). # Scripting & Automation - Bitbucket CLI for CI/CD Workflows > Learn how to use Bitbucket CLI in automation scripts and CI/CD pipelines. JSON output, exit codes, error handling, and practical examples for DevOps workflows. ## JSON output mode Add `--json` for machine-readable output: ```bash bb pr list --json bb repo list --json bb pr view 42 --json ``` Caution `--json` must come **after** the subcommand. Its field-list argument is optional, so `bb --json pr list` swallows `pr` as the field list, leaves `list` to be parsed as a top-level command, and exits 1 with error code 5002. Write `bb pr list --json`. If you need the flag before the subcommand, use the equals form: `bb --json=id,title pr list`. Paginated list commands return 25 rows by default. Pass `--all` to fetch every page, or `--limit ` for a specific cap. `--all` wins if you pass both. `--limit` must be a positive integer; anything else fails with `--limit must be a positive integer` (error code 5002). There is no `--page` flag; pagination is followed internally. ```bash bb pr list --json --all bb pr list --json --limit 100 ``` Output of any size reaches a pipe in full, so `bb pr list --json --all | jq` works the same as redirecting to a file, even when the reader is slow. These flags belong to specific commands, not every list operation. Check `bb --help` or the [list-command flag reference](/reference/global-flags/#list-command-flags). Some commands, such as `bb pr checks`, return complete collections without these flags. ### Field selection and `--jq` Two flags inspired by the [`gh` CLI](https://cli.github.com/manual/gh_help_formatting) slice and filter output without an external `jq` binary: ```bash # Project to a comma-separated field list bb pr list --json id,title,state # Filter through the embedded jq (requires --json) bb pr list --json --jq '.pullRequests[].title' # Combine both bb pr list --json id,title,state \ --jq '.[] | select(.state == "OPEN") | .title' ``` When `--json fields` is passed against a list-style command, the wrapper envelope is dropped and the field list is projected per-item. The result is a flat array. See [JSON Output reference](/reference/json-output/) for details. `--jq` matches the syntax of the standalone [jq](https://jqlang.github.io/jq/) binary, so existing expressions work either way. These two are equivalent: ```bash bb pr list --json --jq '.pullRequests[].title' bb pr list --json | jq '.pullRequests[].title' ``` ### jq patterns ```bash # Open PR count (--state defaults to OPEN) bb pr list --json --all --jq '.count' # Merged PR count bb pr list -s MERGED --json --all --jq '.count' # Project specific fields bb pr list --json --all --jq '.pullRequests[] | {id, title, author: .author.display_name}' # Filter by author bb pr list --json --all --jq '.pullRequests[] | select((.author.nickname // .author.display_name) == "alice")' # PR web URLs bb pr list --json --all --jq '.pullRequests[].links.html.href' # Web diff URL for one PR bb pr diff 42 --web --json --jq '.url' # PRs updated in the last 7 days bb pr list --json --all | jq --arg date "$(date -d '7 days ago' -Iseconds 2>/dev/null || \ date -v-7d -Iseconds)" \ '.pullRequests[] | select(.updated_on > $date)' # PRs targeting main bb pr list --json --all | jq '.pullRequests[] | select(.destination.branch.name == "main")' # Group PRs by author bb pr list --json --all | jq '.pullRequests | group_by(.author.nickname // .author.display_name) | map({author: (.[0].author.nickname // .[0].author.display_name), count: length})' ``` *** ## Piped table output When stdout is not a terminal, list tables come out as tab-separated values: one line per row, no header, nothing padded or cut off. Quick one-liners work without `--json`: ```bash bb pr list | cut -f1,2 # "#42Fix login redirect" bb repo list | sort -t$'\t' -k1 ``` Dates in piped tables are ISO 8601 timestamps (`2026-07-28T11:14:00.000Z`), and hints such as “Showing N results” go to stderr. Use `--json` when a script depends on the exact fields; the TSV columns follow the table and may change. ## Raw API access (escape hatch) When no typed command covers what you need, [`bb api`](/commands/api/) calls any Bitbucket Cloud 2.0 endpoint through the same authenticated stack. Its output is already JSON, so `--jq` works without `--json`. ```bash # Any endpoint, authenticated bb api /user --jq '.username' # Fields become a query string on GET, a JSON body otherwise bb api /repositories/myworkspace/myrepo/pullrequests -X GET -f state=MERGED # Follow pagination and pull a single field across every page bb api /repositories/myworkspace/myrepo/pullrequests --paginate \ --jq '.values[].title' # Send a JSON body from a file bb api POST /repositories/{workspace}/{repo}/hooks --input body.json ``` `{workspace}` and `{repo}` placeholders resolve from `-w`/`-r` or the current checkout. See the [API command reference](/commands/api/) for every flag. *** ## Exit codes | Code | Meaning | | ---- | ------------------------------------------------------------------------------- | | 0 | Success | | 1 | Command failure, such as authentication, API, validation, network, or jq errors | | 130 | An interactive prompt interrupted with Ctrl+C | `bb auth status` exits 1 when you are not logged in, and [`bb doctor`](/commands/doctor/) exits 1 when any of its checks fails. Set `BB_DETAILED_EXIT_CODES=1` to split failures into `2` usage, `3` not found, `4` authentication and `5` confirmation required; see the [full map](/reference/error-codes/#exit-codes-and-error-codes). For anything finer, read the `code` field of the JSON error envelope, which is one of the [error codes](/reference/error-codes/). *** ## Error handling In `--json` mode, success JSON goes to stdout and the error envelope goes to stderr. HTTP debug tracing also writes to stderr, so it never corrupts the JSON on stdout. Redirect the two streams separately: ```bash #!/bin/bash set -euo pipefail if ! bb pr view 999 -w myworkspace -r myrepo --json > out.json 2> err.json; then code=$(jq -r '.code' err.json) case "$code" in 1001|1002|1003) echo "auth problem";; 2002) echo "PR not found";; *) echo "other failure ($code): $(jq -r '.message' err.json)";; esac exit 1 fi ``` The error envelope carries: | Field | Notes | | ------------ | ------------------------------------------------------------------- | | `name` | Error class, `BBError`, `AuthError`, `APIError`, … | | `code` | Numeric [error code](/reference/error-codes/) | | `message` | Human-readable failure description | | `context` | Extra detail (ids, keys, args); omitted when the error carries none | | `statusCode` | HTTP status, `APIError` only | | `response` | Raw API response body, `APIError` only, when non-empty | | `hint` | Remediation advice; optional, see below | `hint` appears on API errors with status 401, 403, or 404 (skipped on 404 when the message already names the missing resource) and on argument-parsing errors. Treat it as optional in scripts. Argument-parsing failures use the same envelope whenever `--json` or `--jq` appears before any `--`. An unknown subcommand or option, or too many arguments, returns code 5002; a missing argument, option value or subcommand returns 5001. Both carry `context.parseError`, `context.commandPath` and a `hint`: ```bash bb pr bogus --json 2> err.json jq -c '{code, parseError: .context.parseError, commandPath: .context.commandPath, hint}' err.json # {"code":5002,"parseError":"unknownCommand","commandPath":"pr","hint":"Run `bb pr --help` for usage."} ``` An unknown top-level command (`bb bogus --json`) also returns 5002, with `context: { command, args }` naming the token that didn’t match. Without `--json` or `--jq`, parse errors stay plain text. *** ## Environment variables For non-interactive scripts, use environment variables: ```bash export BB_USERNAME=you@example.com export BB_API_TOKEN=ATBB_token bb auth login # Uses env vars bb pr list -w workspace -r repo ``` See [Environment Variables Reference](/reference/environment-variables/) for details. *** ## Built-in resilience ### Automatic retry API requests that fail with HTTP 429, 502, 503, or 504 are retried up to 3 times with exponential backoff. Add your own retry loop only if you need more than 3 attempts. ### OAuth token refresh OAuth access tokens expire after 2 hours. The CLI refreshes them before expiry and retries authentication after a 401. If refresh fails, for example because the refresh token was revoked, the command raises `AUTH_EXPIRED` and you must run `bb auth login` again. *** ## Scripting best practices ### Always pass `-w` and `-r` Workspace and repository detection reads the git remote of the current checkout. CI runners frequently check out with a non-Bitbucket remote, or no remote at all, so context detection fails there. ```bash # Good - explicit and reliable bb pr list -w myworkspace -r myrepo --json # Bad - fails when the checkout has no Bitbucket remote bb pr list --json ``` ### Never rely on prompts `bb` only prompts when stdin and stdout are both terminals, so pipelines and CI jobs never block on a question (`bb completion install` aside, which always asks for your shell). Scripts run inside a pseudo-terminal (`expect`, `script`, some IDE task runners) do look interactive, though. Set `BB_PROMPT_DISABLED=1` or pass `--no-input` there, and pass `--yes` and `--title` explicitly, so a missing flag fails fast instead of waiting for an answer. ### Add delays in loops When making many sequential API calls, add short delays to avoid hitting rate limits: ```bash #!/bin/bash set -euo pipefail WORKSPACE="myworkspace" REPO="myrepo" pr_ids=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json --all | jq -r '.pullRequests[].id') for pr_id in $pr_ids; do bb pr view "$pr_id" -w "$WORKSPACE" -r "$REPO" --json sleep 1 done ``` *** ## Example scripts ### Batch pull request approval Approve all open pull requests (PRs) from a specific author: ```bash #!/bin/bash set -euo pipefail WORKSPACE="myworkspace" REPO="myrepo" AUTHOR="trusted-bot" echo "Finding PRs from $AUTHOR..." pr_ids=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json --all | \ jq -r --arg author "$AUTHOR" '.pullRequests[] | select((.author.nickname // .author.display_name) == $author) | .id') for pr_id in $pr_ids; do echo "Approving PR #$pr_id..." bb pr approve "$pr_id" -w "$WORKSPACE" -r "$REPO" sleep 1 done echo "Done!" ``` ### PR status report Generate a markdown report of open PRs: ```bash #!/bin/bash set -euo pipefail WORKSPACE="myworkspace" REPO="myrepo" echo "# Open Pull Requests" echo "" echo "Generated: $(date)" echo "" bb pr list -w "$WORKSPACE" -r "$REPO" --json --all | jq -r '.pullRequests[] | "## PR #\(.id): \(.title)\n" + "- **Author:** \(.author.display_name)\n" + "- **Branch:** \(.source.branch.name) → \(.destination.branch.name)\n" + "- **Created:** \(.created_on)\n" + "- **Link:** \(.links.html.href)\n"' ``` ### Auto-close stale PRs Decline PRs not updated in 30 days: ```bash #!/bin/bash set -euo pipefail WORKSPACE="myworkspace" REPO="myrepo" DAYS_OLD=30 cutoff=$(date -d "$DAYS_OLD days ago" -Iseconds 2>/dev/null || \ date -v-${DAYS_OLD}d -Iseconds) # macOS fallback echo "Finding PRs older than $DAYS_OLD days..." stale_prs=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json --all | \ jq -r --arg cutoff "$cutoff" '.pullRequests[] | select(.updated_on < $cutoff) | .id') if [ -z "$stale_prs" ]; then echo "No stale PRs found" exit 0 fi for pr_id in $stale_prs; do echo "Declining stale PR #$pr_id..." bb pr decline "$pr_id" -w "$WORKSPACE" -r "$REPO" sleep 1 done ``` # Configuration File > Complete reference for the bb configuration file All persistent settings live in one JSON file, written by `bb auth login`, `bb config set`, and `bb alias set`. Logout and automatic OAuth refresh also update it. This page documents the file itself; for the commands that read and write it, see the [Config command reference](/commands/config/). ## File location * macOS / Linux ```plaintext ~/.config/bb/config.json ``` * Windows ```plaintext %APPDATA%\bb\config.json ``` Typically: `C:\Users\\AppData\Roaming\bb\config.json` *** ## Configuration schema | Key | Type | Description | Set by | | --------------------------------- | ------------------------ | ---------------------------------------------------------------------------------- | --------------------------------- | | `accounts` | object | Saved accounts by name, see [Account fields](#account-fields) | `bb auth login` | | `activeAccount` | string | Account used when neither `--account` nor `BB_ACCOUNT` is given | `bb auth login`, `bb auth switch` | | `credentialStorage` | `"file"` \| `"keychain"` | Where account secrets live (default: `file`) | `bb config set` | | `defaultWorkspace` | string | Default workspace | `bb config set defaultWorkspace` | | `skipVersionCheck` | boolean | Disable update notifications | `bb config set` | | `versionCheckInterval` | number | Days between update checks (default: 1) | `bb config set` | | `prCreateIncludeDefaultReviewers` | boolean | Auto-add the repository’s default reviewers on `bb pr create` (default: false) | `bb config set` | | `gitProtocol` | `"ssh"` \| `"https"` | Protocol for `bb repo clone` and fork fetches in `bb pr checkout` (default: `ssh`) | `bb config set` | | `aliases` | object | User-defined command aliases | `bb alias set` | | `lastVersionCheck` | string | Timestamp of last update check | Automatic | ### Account fields | Key | Type | Description | Set by | | ------------------- | ---------------------- | -------------------------------------------------------------- | ------------------------------- | | `authMethod` | `"basic"` \| `"oauth"` | Authentication method of the account | `bb auth login` | | `username` | string | Your Atlassian account email (API token auth) | `bb auth login -u` | | `apiToken` | string | Your API token (API token auth); secret | `bb auth login --with-token` | | `oauthAccessToken` | string | OAuth access token; secret | `bb auth login` (OAuth) | | `oauthRefreshToken` | string | OAuth refresh token; secret | `bb auth login` (OAuth) | | `oauthExpiresAt` | number | OAuth token expiry (Unix timestamp) | `bb auth login` (OAuth) | | `oauthClientId` | string | Custom OAuth consumer client ID | `bb auth login --client-id` | | `oauthClientSecret` | string | Custom OAuth consumer client secret; secret | `bb auth login --client-secret` | | `storage` | `"keychain"` | Present when the secret fields live in the OS keychain instead | `bb config set` | Older versions of bb kept one account’s fields at the top level of the file. bb still reads those as the `default` account and moves them under `accounts` the first time it writes credentials, so upgrading needs no action. ### Example configuration (two accounts) ```json { "accounts": { "default": { "authMethod": "basic", "username": "you@example.com", "apiToken": "ATBB_xxxxxxxxxxxxxxxxxxxx" }, "personal": { "authMethod": "oauth", "oauthAccessToken": "xxxxxxxxxxxxxxxx", "oauthRefreshToken": "xxxxxxxxxxxxxxxx", "oauthExpiresAt": 1711036800 } }, "activeAccount": "default", "defaultWorkspace": "myworkspace", "versionCheckInterval": 7 } ``` ### Example configuration (keychain storage) ```json { "accounts": { "default": { "authMethod": "oauth", "oauthExpiresAt": 1711036800, "storage": "keychain" } }, "activeAccount": "default", "credentialStorage": "keychain" } ``` The tokens sit in the OS keychain under the service `bitbucket-cli`, one entry per account name. See [Configuration storage](/getting-started/authentication/#configuration-storage). *** ## Managing configuration ### View all settings ```bash bb config list ``` Output: ```text Config file: /Users/you/.config/bb/config.json KEY VALUE ---------------- ----------- username you@example.com defaultWorkspace myworkspace apiToken ******** skipVersionCheck false Settable keys: defaultWorkspace, credentialStorage, skipVersionCheck, versionCheckInterval, prCreateIncludeDefaultReviewers, gitProtocol. Run 'bb config set --help' for details. ``` The API token is masked. Use `bb auth token` to see the real value. ### Get a specific value ```bash bb config get defaultWorkspace # Output: myworkspace ``` Values are stored as native JSON types, so for the typed keys `--json` returns a real boolean or number rather than a quoted string: ```bash bb config get skipVersionCheck --json # {"key":"skipVersionCheck","value":true} ``` ### Set a value ```bash bb config set defaultWorkspace myworkspace bb config set skipVersionCheck true bb config set versionCheckInterval 7 ``` #### Settable keys `bb config set` accepts these six keys; `bb config list` prints the current list as a footer. Everything else is written by `bb auth login` or by the CLI itself. | Key | Type | Example | | --------------------------------- | -------------------- | ---------------------------------------------------- | | `defaultWorkspace` | string | `bb config set defaultWorkspace myworkspace` | | `credentialStorage` | `file` or `keychain` | `bb config set credentialStorage keychain` | | `skipVersionCheck` | boolean | `bb config set skipVersionCheck true` | | `versionCheckInterval` | integer (days, ≥ 1) | `bb config set versionCheckInterval 7` | | `prCreateIncludeDefaultReviewers` | boolean | `bb config set prCreateIncludeDefaultReviewers true` | | `gitProtocol` | `ssh` or `https` | `bb config set gitProtocol https` | `skipVersionCheck` and `prCreateIncludeDefaultReviewers` accept only `true` or `false` (case-insensitive, surrounding whitespace ignored). `versionCheckInterval` accepts only positive integers (`>= 1`). `gitProtocol` accepts only `ssh` or `https` (case-insensitive). #### Readable keys `bb config get` has its own allowlist, and it is not the same one: ```text username, defaultWorkspace, credentialStorage, skipVersionCheck, versionCheckInterval, prCreateIncludeDefaultReviewers, gitProtocol ``` `username` is readable but not settable; change it with `bb auth login -u`. `bb config get apiToken` refuses: ```text Cannot display 'apiToken' - it is part of your authentication credentials, not config. Use 'bb auth token' to retrieve credentials, or run 'bb config list' to see readable keys. ``` Any other key throws `Unknown config key ''` with the valid list and a suggestion, e.g. `(Did you mean defaultWorkspace?)`. #### `prCreateIncludeDefaultReviewers` and the `--default-reviewers` flag | Config | Flag | Result | | --------------- | ------------------------ | ----------------------------------- | | unset / `false` | (none) | Default reviewers are **not** added | | `true` | (none) | Default reviewers are added | | unset / `false` | `--default-reviewers` | Default reviewers are added | | `true` | `--no-default-reviewers` | Default reviewers are **not** added | ### Protected keys These keys cannot be set with `bb config set`: | Key | Reason | How to set | | ------------------- | ---------------------- | -------------------------------- | | `username` | Tied to authentication | Use `bb auth login -u` | | `apiToken` | Security-sensitive | Use `bb auth login --with-token` | | `authMethod` | Managed by login flow | Use `bb auth login` | | `oauthAccessToken` | Managed by OAuth flow | Use `bb auth login` | | `oauthRefreshToken` | Managed by OAuth flow | Use `bb auth login` | | `oauthExpiresAt` | Managed by OAuth flow | Use `bb auth login` | *** ## Update notifications After every command the CLI checks npm for a newer version, at most once per `versionCheckInterval` days (default 1). The notice goes to stderr, and only when stderr is a TTY, so `--json` and piped output stay clean. It is skipped entirely in CI and when `skipVersionCheck` is true. ### Example notification ```text ────────────────────────────────────────────────── A new version is available: (you have ) Run 'bun install -g @pilatos/bitbucket-cli' to update Or disable with 'bb config set skipVersionCheck true' ────────────────────────────────────────────────── ``` There is no warning glyph. The separator rules are 50 box-drawing dashes, or 50 ASCII hyphens under `--no-unicode`. The update line follows where `bb` is installed: `pnpm add -g` for a pnpm global install, `bun install -g` for a Bun global install or `bunx`, a release download link for the standalone binary, and `npm install -g` for any other `node_modules` install. Outside `node_modules`, such as a source checkout, it suggests `bun install -g`. ### Disable notifications ```bash bb config set skipVersionCheck true ``` ### Change check frequency `versionCheckInterval` is in days. Once per week: ```bash bb config set versionCheckInterval 7 ``` *** ## Configuration precedence Command-line flags beat the git remote, which beats `BB_WORKSPACE`, which beats the config file: ```bash bb pr list -w otherworkspace -r otherrepo # flags win cd /path/to/cloned-repo && bb pr list # git remote BB_WORKSPACE=myworkspace bb repo list # env var bb config set defaultWorkspace myworkspace # config file ``` The full order, including which commands skip the git-remote step, is in [Environment Variables](/reference/environment-variables/#resolution-order). [Understanding Repository Context](/guides/repository-context/) has worked examples. Some runtime behavior is tuned only with environment variables, not config keys; `BB_HTTP_TIMEOUT` for the API request timeout, for example. *** ## File permissions The config file holds your tokens (unless `credentialStorage` is `keychain`), so the CLI creates the directory `0700` and the file `0600`, and writes atomically (temp file, then rename). | Platform | Directory | File | | ----------- | --------------------------------- | --------------------------------- | | macOS/Linux | `0700` (owner only) | `0600` (owner read/write only) | | Windows | Inherits user profile permissions | Inherits user profile permissions | On macOS and Linux the CLI also *verifies* both on every read. If any group or other bit is set, every command fails with error 4001 until you fix it: ```text Config file has insecure permissions (644); expected 600. Run: chmod 600 /Users/you/.config/bb/config.json Config directory has insecure permissions (755); expected 700. Run: chmod 700 /Users/you/.config/bb ``` Windows skips the check, and a path that does not exist yet is not checked, so a fresh install never hits this. Security Never share your config file or commit it to version control. The API token grants access to your Bitbucket account. *** ## Reset configuration ### Clear authentication only ```bash bb auth logout ``` This removes the current account’s credentials (OAuth tokens or API token) but keeps other accounts, `defaultWorkspace` and other settings. For OAuth, it also attempts to revoke the token on Bitbucket’s side. ### Full reset With keychain storage, run `bb auth logout` for each account first (or `bb config set credentialStorage file`), otherwise the keychain entries stay behind. Then delete the config file entirely: * macOS / Linux ```bash rm ~/.config/bb/config.json ``` * Windows ```powershell Remove-Item $env:APPDATA\bb\config.json ``` Then re-authenticate: ```bash bb auth login ``` *** ## Troubleshooting ### “Config file is not valid JSON” Fix the file by hand, or delete it and log in again: ```bash rm ~/.config/bb/config.json bb auth login ``` ### “Config file has insecure permissions” ```bash chmod 700 ~/.config/bb chmod 600 ~/.config/bb/config.json ``` ### “Failed to read config file” The path exists but is unreadable. Check ownership and permissions: ```bash ls -la ~/.config/bb/ ``` ### “Failed to write config file” / “Failed to create config directory” The parent directory is missing or not writable: ```bash mkdir -p ~/.config/bb ls -ld ~/.config/bb ``` ### Config not being used Verify the CLI is reading from the expected location: ```bash bb config list # Check the "Config file:" line ``` Command-line flags, git context and `BB_WORKSPACE` all take precedence over config file settings. # Environment Variables > Configure Bitbucket CLI using environment variables Every environment variable the CLI reads. `bb --help` lists the main ones in its footer. ## Available variables | Variable | Description | Example | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | | `BB_USERNAME` | Your Atlassian account email (fallback for API-token `bb auth login`) | `you@example.com` | | `BB_API_TOKEN` | Your Bitbucket API token (fallback for `bb auth login`; forces API token auth when set) | `ATBB...` | | `BB_WORKSPACE` | Default workspace. Overrides `config.defaultWorkspace`. `--workspace` always wins; the git remote wins too, but only on repository-scoped commands; see [Resolution order](#resolution-order). | `myworkspace` | | `BB_ACCOUNT` | Saved account to use for this invocation, instead of the active one. `--account` wins. See [Multiple accounts](/getting-started/authentication/#multiple-accounts). | `work` | | `BB_LOCALE` | BCP-47 locale tag for date/time formatting. `--locale` takes precedence; falls back to `LC_TIME`/`LC_ALL`/`LANG`, then `en-US`. | `de-DE` | | `BB_NO_UNICODE` | When set to any non-empty value, use ASCII fallbacks for separators, arrows, and status icons. Same effect as the global `--no-unicode` flag, but it cannot be overridden per command: `--unicode` is accepted and does nothing while this is set. Unset the variable instead. | `1` | | `BB_PROMPT_DISABLED` | When set to any non-empty value, never prompt, even in an interactive terminal (`bb completion install` still asks for your shell). Same effect as the global `--no-input` flag. See [`--no-input`](/reference/global-flags/#--no-input). | `1` | | `BB_DETAILED_EXIT_CODES` | When set to any non-empty value, exit with a status per failure class: `2` usage, `3` not found, `4` authentication, `5` confirmation required (otherwise every failure exits `1`). See [Exit codes](/reference/error-codes/#exit-codes-and-error-codes). | `1` | | `NO_COLOR` | Disable color output globally. Triggers on any value, including the empty string. | `1` | | `FORCE_COLOR` | Force-enable color output globally (any value except `0`) | `1` | | `BB_PAGER` | Pager for `bb pr diff`, `bb pr view` and `bb pipeline logs` on a terminal. Overrides `PAGER`; defaults to `less -FRX`. Empty or `cat` turns paging off. Split on whitespace, no shell quoting. See [Pager](#pager). | `less -R` | | `PAGER` | Fallback pager when `BB_PAGER` is unset. | `less` | | `LESS` | Options for `less`. Set to `FRX` for the pager when you have not set it, so `PAGER=less` still shows colors. | `-FRX` | | `BB_DEBUG` | HTTP debug tracing on stderr. `http` logs method, URL, status (or network error code) and elapsed time for every API call; `verbose` also logs the request and response bodies, secrets redacted. `0`, `false`, `off`, `no` or `none` disables it, even when `DEBUG=true` is set; any other non-empty value (e.g. `1`) means `http`. Any level also prints the browser-open and version-check diagnostics on stderr. See [HTTP debug tracing](#http-debug-tracing). | `http` | | `DEBUG` | Alias for `BB_DEBUG=verbose`. Must equal the literal string `true`, and is ignored when `BB_DEBUG` is set. | `true` | | `BB_HTTP_TIMEOUT` | Per-request HTTP timeout in **milliseconds** for Bitbucket API calls. Prevents the CLI from hanging forever when a server accepts a connection but never responds. Defaults to `30000` (30s). Set to `0` to disable the timeout entirely. Invalid or negative values fall back to the default. A timed-out request is reported as a network error. `bb repo downloads upload` adds transfer time for the upload size on top. | `60000` | | `BB_API_BASE_URL` | Base URL for Bitbucket API calls. Defaults to `https://api.bitbucket.org/2.0`. Point it at a gateway, a mirror, or a local mock server (trailing slashes are stripped). | `http://localhost:8080/2.0` | BB\_HTTP\_TIMEOUT is in milliseconds `BB_HTTP_TIMEOUT=60` means 60 milliseconds, so almost every request fails. For a 60-second ceiling use `BB_HTTP_TIMEOUT=60000`. In CI, set something short like `BB_HTTP_TIMEOUT=15000` so a stalled call fails fast instead of hanging the pipeline. `BB_HTTP_TIMEOUT=0` disables the timeout; it affects API requests, not `git clone`. A stalled request can then wait indefinitely. ### Pager On a terminal, `bb pr diff`, `bb pr view` and `bb pipeline logs` open their output in a pager. The default `less -FRX` quits right away when the output fits on one screen (`-F`), keeps colors (`-R`) and leaves the text on screen after you quit (`-X`). Ctrl+C goes to the pager while it is open. There is no pager with `--json` or when stdout is piped. If the pager can’t be started (for example no `less` on Windows) or exits with an error, the output is printed directly. A command that fails prints what it has so far without the pager. ```bash BB_PAGER= bb pr diff 42 # no pager for this run export BB_PAGER="less -RS" # don't wrap long diff lines ``` ### HTTP debug tracing `BB_DEBUG=http` prints one line when a request is sent and one when it finishes. Each line starts with `[HTTP]` and a six-character correlation id, so lines from overlapping requests can be matched up: ```text $ BB_DEBUG=http bb pr list [HTTP] 3f9a1c GET https://api.bitbucket.org/2.0/repositories/my-ws/my-repo/pullrequests?[redacted] [HTTP] 3f9a1c 200 GET https://api.bitbucket.org/2.0/repositories/my-ws/my-repo/pullrequests?[redacted] 184ms ``` A retried request (rate limit, `502`/`503`/`504`, transient network error) or an OAuth token refresh keeps its id, and the resent line ends in `(attempt N)`. The elapsed time on an outcome line covers one attempt on the wire. Time spent before sending is shown separately so slow runs can be explained: * `(waited Nms)` on a request line: how long it was held back, by rate-limit pacing for a first attempt, or since the previous attempt finished (retry backoff, token refresh) for a retry. * `(auth Nms)` on an outcome line: credential lookup, including a proactive OAuth token refresh, between the request line and the send. A request that gets no response logs the network error code instead of a status, e.g. `ECONNRESET` or `ECONNABORTED` for a timeout. A failure before the request is sent (e.g. missing credentials) logs `[HTTP] Error: ` right after its request line. `BB_DEBUG=verbose` adds a `Request Body:` line after a request line that has a body, and a `Response Body:` (or `Error Response Body:`) line after each outcome. Query strings are always replaced with `?[redacted]`, and values under keys such as `access_token`, `password` and `authorization` are replaced with `[REDACTED]` in JSON bodies. Headers are never logged. Trace lines go to stderr, including with `--json`. JSON output stays on stdout, so it can be piped into another tool while tracing is enabled. ### POSIX locale variables If neither `--locale` nor `BB_LOCALE` is set, the CLI reads the standard POSIX locale variables in the order `LC_TIME`, `LC_ALL`, then `LANG`, and falls back to `en-US` if none is usable. `LC_TIME` is deliberately checked **before** `LC_ALL`, which is the reverse of the usual POSIX override order, so setting both means `LC_TIME` wins. Values are normalised before use: a `.UTF-8` codeset suffix and an `@euro`-style modifier are stripped, `_` becomes `-` (`de_DE.UTF-8` → `de-DE`), and the `C` and `POSIX` locales map to `en-US`. Unset them, or pass `--locale`, to pin formatting explicitly. ### CI detection The update check is skipped when any of these is set to any value: `CI`, `CONTINUOUS_INTEGRATION`, `BUILD_ID`, `BUILD_NUMBER`, `DRONE`, `GITHUB_ACTIONS`, `GITLAB_CI`, `CIRCLECI`, `TRAVIS`, `JENKINS_URL`, `HUDSON_URL` Set `CI=1` to get the same behavior locally. ### Internal / system variables Set by the runtime, your shell, or your operating system rather than by you. Documented so they aren’t surprising during troubleshooting. | Variable | Set by | Description | | --------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NODE_ENV` | Test runners | When set to `test`, the CLI suppresses `process.exitCode = 1` on errors so a single failing test cannot cascade into later tests. Don’t set this in production. | | `COMP_LINE` | tabtab / your shell | Set automatically while shell completion is being computed (`bb completion`). Its presence triggers the completion path; you should not set it manually. | | `BB_COMPLETION_SHELL` | The completion scripts | Set by the scripts from `bb completion ` to `bash`, `zsh`, `fish` or `powershell`, so candidates come back in that shell’s format. Scripts written by older versions don’t set it, and `$SHELL` decides instead. Don’t set it manually. | | `APPDATA` | Windows | Used to locate the config file at `%APPDATA%\bb\config.json` on Windows. The CLI falls back to `%USERPROFILE%\AppData\Roaming\bb\config.json` if it isn’t set. | ## Resolution order Workspace and repository, highest priority first: 1. **Command-line flags** (`--workspace`, `--repo`) 2. **Git repository context** (detected from the remote URL) 3. **`BB_WORKSPACE`** (workspace only) 4. **Configuration file** (`defaultWorkspace`, workspace only) Step 2 does not apply to workspace-only commands; `bb workspace view`, every `bb project` and `bb snippet` command, `bb repo list`, `bb repo create`, `bb repo clone`, and `bb api` filling a `{workspace}` placeholder go straight from the flag to `BB_WORKSPACE` to `defaultWorkspace`. Color, highest priority first. The default with none of them set is colors on: 1. `--color`; matched by a raw scan of argv, so `bb pr diff 42 --color never` still turns global color on 2. `FORCE_COLOR` (any value except `0`) 3. `--no-color` 4. `NO_COLOR` (any value, including the empty string) Credentials are **not** a chain. `bb pr list` and every other API call read the stored `username` (your Atlassian account email) and API token from the config file only; if nothing is stored there the command fails with error 1001 even when `BB_USERNAME` and `BB_API_TOKEN` are exported. Those two variables are read by `bb auth login` and nowhere else; run it once and it writes the config file. Inside `bb auth login`: * `--username` / `-u` beats `BB_USERNAME`. * `--with-token` (token on stdin) beats `--password` / `-p`, which beats `BB_API_TOKEN`. Combining `--with-token` with `--password` is an error. * Setting `BB_API_TOKEN` at all selects API token auth instead of OAuth. ## Authentication with environment variables `bb auth login` picks up both variables, so it runs without prompts: ```bash export BB_USERNAME=you@example.com export BB_API_TOKEN=ATBB_your_token_here bb auth login bb pr list -w myworkspace -r myrepo ``` Or inline, without exporting: ```bash BB_USERNAME=you@example.com BB_API_TOKEN=ATBB_token bb auth login ``` To keep the token out of shell history and `ps` output, pipe it instead: ```bash printf '%s' "$BB_API_TOKEN" | bb auth login -u you@example.com --with-token ``` ## Shell configuration Set `BB_USERNAME=you@example.com` in your shell profile if you want an email fallback. Load `BB_API_TOKEN` from a secret store when logging in; avoid putting it in your shell profile. These variables select credentials for login, not for subsequent API calls. ## Docker Pass credentials from a secret store and run `bb auth login` inside the container before API commands. The npm package still needs Bun on `PATH`. Config credentials are stored on disk; account for that when mounting a home directory or building an image. ## CI/CD examples See [CI/CD Integration](/guides/cicd/) for runner setup and authentication. Use an Atlassian account email for `BB_USERNAME`, and store `BB_API_TOKEN` in the runner’s secret store. ## Keeping tokens safe * Add `.env*` to `.gitignore` and pass tokens through your CI platform’s secrets store. Never commit them. * Grant the token only the scopes the script uses. See [Token Scopes](/reference/token-scopes/) for the scope each command needs. # Error Codes > Complete reference of error codes and their meanings Every failure carries a numeric `code` in the `--json` error payload (`{"code": 2002, …}`). The process exit status is normally `1` (Ctrl+C at a prompt uses `130`); set `BB_DETAILED_EXIT_CODES=1` for a coarse status per failure class (see [Exit codes](#exit-codes-and-error-codes)). Branch on `code` for anything finer. Ctrl-F the number below. ## Error code ranges | Range | Category | Description | | ----- | -------------------------- | -------------------------------------------------------------- | | 1xxx | Authentication | Login, credentials, and token issues | | 2xxx | API | Bitbucket API request failures | | 3xxx | Git | Local git operations | | 4xxx | Configuration | Config file read/write issues | | 5xxx | Validation | Invalid input or missing required fields | | 6xxx | Context | Repository/workspace detection issues | | 7xxx | Network | Transport/network failures before API response | | 8xxx | Output formatting | `--json fields` and `--jq` evaluation issues | | 9xxx | Shell completion / Unknown | Completion install/uninstall failures and uncategorized errors | | 10xxx | CI outcome | A watched pipeline or pull request check did not pass | *** ## Authentication errors (1xxx) ### 1001 - AUTH\_REQUIRED **Message:** Authentication required **Cause:** You haven’t logged in yet, or your credentials have been cleared. `bb auth login` also returns it outside an interactive terminal when no API token is given, because browser login needs a person to finish it. **Solution:** ```bash bb auth login # Scripts, CI and agents: BB_USERNAME=you@example.com BB_API_TOKEN=... bb auth login ``` ### 1002 - AUTH\_INVALID **Message:** Invalid credentials **Cause:** Your credentials were rejected by Bitbucket at request time. This is the code emitted for any 401 response that was *not* recovered by an OAuth refresh; e.g. a wrong API token, a revoked password, or basic-auth requests with bad credentials. **Solution:** 1. Verify your Atlassian account email for API-token login 2. Generate a new API token at [Bitbucket API Tokens](https://bitbucket.org/account/settings/api-tokens/) 3. Re-authenticate: ```bash bb auth logout bb auth login ``` The CLI prints this next step alongside the error: > Your Bitbucket credentials were rejected. Run `bb auth login` to re-authenticate. ### 1003 - AUTH\_EXPIRED **Message:** one of: * `OAuth token expired. Run 'bb auth login' to re-authenticate.` * `Failed to refresh OAuth token. Run 'bb auth login' to re-authenticate.`, followed by Bitbucket’s reason when it gives one **Cause:** Specifically: an OAuth access token expired *and* Bitbucket rejected the refresh (refresh token revoked or expired). The CLI refreshes before a request whose token has expired or is about to, and again after a 401; a rejected refresh at either point reports this code. For non-OAuth flows, a stale token surfaces as `AUTH_INVALID` (1002) instead. **Solution:** 1. Re-run `bb auth login` to perform a fresh OAuth flow 2. If that also fails, create a new API token in Bitbucket settings and switch auth methods *** ## API errors (2xxx) ### 2001 - API\_REQUEST\_FAILED **Message:** API request failed **Cause:** Bitbucket answered with an unexpected HTTP status below 500 that isn’t 401/403/404/429; typically 400 (malformed request body) or 409 (conflict, e.g. merging a pull request that is already merged). The server did respond, so this is not a connectivity problem. **Solution:** Read `message` and `response` in the JSON error payload; they carry Bitbucket’s own explanation. When Bitbucket returns a bare plain-text body (e.g. `Bad Request` from the request parser before the endpoint runs), the body text becomes `message` instead of axios’s generic “Request failed with status code 400”. ```bash bb pr merge 42 --json 2> err.json jq '.statusCode, .message, .response' err.json ``` `bb api` goes further: its `--json` error object also includes `headers` and the transport’s `statusText`, and `bb api -i` prints the status line and headers to stderr on failure. See [API Command](/commands/api/#notes). Connection failures are [7001](#7001---network_error); server faults are [2005](#2005---api_server_error). ### 2002 - API\_NOT\_FOUND **Message:** Resource not found **Cause:** The requested repository, PR, or resource doesn’t exist. **Solution:** * Check spelling of workspace and repository names * Verify the resource exists in Bitbucket * Ensure you have access to the repository Where the message doesn’t already name the missing resource, the CLI appends: > Verify the id or slug you passed, and that –workspace/–repo point at the right repository your token can see. The hint is omitted when the message is already specific (for example `Pull request 999 not found in acme/demo.`) and for `bb api`, where you supplied the URL yourself. ### 2003 - API\_FORBIDDEN **Message:** Access denied **Cause:** Bitbucket rejected the operation. Missing scopes, account permissions, or repository restrictions can cause a 403. **Solution:** 1. Check account permissions and repository restrictions. 2. For API tokens, check the [command scopes](/reference/token-scopes/) and create a new token if needed. Existing API-token scopes cannot be changed. 3. For OAuth, check the consumer’s permissions and sign in again. The CLI prints this next step alongside the error: > Your token may be missing a required scope, or your account may lack permission. For API tokens, create a token with the required scopes. For OAuth, check consumer permissions, then run `bb auth login`. > > Docs: ### 2004 - API\_RATE\_LIMITED **Message:** Rate limit exceeded **Cause:** Bitbucket returned 429 and the CLI’s three automatic retries; which honour the `Retry-After` header; were all exhausted. **Solution:** Reduce request volume. Prefer `--limit` over `--all`, and batch with `bb api --paginate` instead of looping single calls: ```bash bb pr list --limit 25 --json bb api /repositories/myworkspace/myrepo/pullrequests --paginate --json ``` ### 2005 - API\_SERVER\_ERROR **Message:** Bitbucket server error **Cause:** Bitbucket’s servers are experiencing issues. **Solution:** * Check [status.bitbucket.org](https://status.bitbucket.org) * Retry after a few minutes *** ## Git errors (3xxx) ### 3001 - GIT\_NOT\_REPOSITORY Reserved. No code path currently emits `3001`; running outside a git repository surfaces as [6001 CONTEXT\_REPO\_NOT\_FOUND](#6001---context_repo_not_found). Don’t branch on `3001` in scripts. ### 3002 - GIT\_COMMAND\_FAILED **Message:** Git command failed **Cause:** A git operation (clone, fetch, checkout) failed. **Solution:** * Ensure git is installed and in your PATH * Check git error message for details * Verify you have proper SSH keys or credentials configured ### 3003 - GIT\_REMOTE\_NOT\_FOUND **Message:** No Bitbucket remote found **Cause:** The repository has no remote pointing to Bitbucket. **Solution:** * Add a Bitbucket remote: ```bash git remote add origin git@bitbucket.org:workspace/repo.git ``` * Or use explicit flags: `-w -r ` *** ## Config errors (4xxx) ### 4001 - CONFIG\_READ\_FAILED **Message:** Cannot read config file **Cause:** The configuration file is corrupted or unreadable, or the config file or directory is readable by other users. The insecure-permissions case says so and names the fix: ```plaintext Config directory has insecure permissions (755); expected 700. Run: chmod 700 /Users/you/.config/bb ``` **Solution:** 1. Check file permissions (run the `chmod` from the message for the insecure-permissions case) 2. If corrupted, delete and recreate: ```bash # macOS/Linux rm ~/.config/bb/config.json bb auth login # Windows del %APPDATA%\bb\config.json bb auth login ``` ### 4002 - CONFIG\_WRITE\_FAILED **Message:** Cannot write config file **Cause:** No write permission to the config directory. **Solution:** * Check directory permissions * Ensure the parent directory exists * On shared systems, verify you own the config directory ### 4003 - CONFIG\_INVALID\_KEY **Message:** Invalid configuration key **Cause:** Attempted to get or set an unknown configuration key. **Solution:** Use a valid key. `bb config set` accepts six: | Key | Meaning | | --------------------------------- | ------------------------------------------------------------------------- | | `defaultWorkspace` | Workspace used when `-w` is omitted | | `credentialStorage` | Where account secrets live: `file` or `keychain` | | `skipVersionCheck` | Disable update notifications | | `versionCheckInterval` | Days between update checks | | `prCreateIncludeDefaultReviewers` | Add the repository’s default reviewers on `bb pr create` | | `gitProtocol` | `ssh` or `https` for `bb repo clone` and fork fetches in `bb pr checkout` | `bb config get` accepts those six plus `username`. ```bash bb config set defaultWorkspace myworkspace bb config get username bb config list ``` Two keys get a distinct message instead of the unknown-key one. `bb config set username` and `bb config set apiToken` point you at `bb auth login`; `bb config get apiToken` points you at `bb auth token`. Every other credential field (`oauthAccessToken` and friends) is not a config key at all and falls through to the unknown-key message above. *** ## Validation errors (5xxx) ### 5001 - VALIDATION\_REQUIRED **Message:** Required field missing **Cause:** A required option or argument was not provided. **Solution:** Check command help for required options: ```bash bb --help ``` Common required fields: * `bb pr create` requires `--title` * `bb repo create` requires a name argument ### 5002 - VALIDATION\_INVALID **Message:** Invalid value **Cause:** A provided value doesn’t match expected format, or an unknown top-level command was given. **Solution:** * Check the expected format in command help * Common issues: * Pull request ID must be a number * State must be one of: OPEN, MERGED, DECLINED, SUPERSEDED * An unknown command (`bb prr`) * `--json` placed before the subcommand, so it ate the subcommand as its field list; see [Troubleshooting](/help/troubleshooting/) Argument-parsing errors (an unknown option or subcommand, too many arguments) are `5002` too, and a missing argument or option value is `5001`. With `--json` or `--jq` before any `--` they come back as an envelope with `context.parseError` (`unknownOption`, `unknownCommand`, `excessArguments`, `missingArgument`, `missingSubcommand`, …), `context.commandPath`, and a `hint` naming the help page: ```bash bb pr list --stat --json # {"name":"BBError","code":5002,"message":"unknown option '--stat'","context":{"parseError":"unknownOption","commandPath":"pr list"},"hint":"Run `bb pr list --help` for usage."} ``` The misplaced-`--json` case names the token it swallowed. Because `--json` did take effect, it comes back as an error envelope on stderr: ```bash bb --json pr list ``` ```json { "name": "BBError", "code": 5002, "message": "--json consumed 'pr' as its field list, so 'list' was parsed as a top-level command.\nPut --json after the subcommand: bb pr list --json", "context": { "command": "pr", "args": ["list"] } } ``` When a value or name is close to a valid one, the CLI suggests the correction. This fires for unknown commands, enum option values, config keys, `bb pr activity --type` tokens, and `bb api` HTTP methods: ```bash bb prr # ✗ unknown command 'prr' # (Did you mean pr?) bb pr list --state opne # ✗ --state must be one of: OPEN, MERGED, DECLINED, SUPERSEDED # (Did you mean OPEN?) bb config set defaultWorkspce foo # ✗ Unknown config key 'defaultWorkspce'. Valid keys: defaultWorkspace, credentialStorage, skipVersionCheck, versionCheckInterval, prCreateIncludeDefaultReviewers, gitProtocol # (Did you mean defaultWorkspace?) ``` Enum values ignore case, so `--state open` is accepted as `OPEN`. ### 5003 - FILE\_NOT\_FOUND **Message:** File not found: `` **Cause:** A file referenced by an option (`--file` on `bb snippet create`/`edit`, `--body-file` on `bb pr create`/`edit` or `bb pr comments add`/`reply`, `-F key=@file` or `--input` on `bb api`) doesn’t exist on disk, or a named file isn’t present inside a snippet. **Solution:** * Verify the path is correct relative to the current working directory * For `bb snippet view --file `, list snippet files first to confirm the name The `context` field carries the offending path, so scripts can tell this apart from a generic `VALIDATION_INVALID`. The key depends on the command: | Key | Set by | | ---------- | ------------------------------------------------------------------- | | `file` | `bb snippet create/edit/view` | | `bodyFile` | `--body-file` on `bb pr create/edit` and `bb pr comments add/reply` | | `path` | `bb api -F key=@file`, `bb api --input ` | `bb snippet view --file` also attaches `available`, the list of filenames the snippet actually contains. `--body-file` only reports `5003` when the read failed with `ENOENT`. Any other read failure (a permission error, or pointing at a directory) is reported as [9999 UNKNOWN](#9999---unknown). ### 5004 - PROMPT\_CANCELLED **Message:** `Cancelled.` (a declined confirmation) or `Prompt cancelled.` (Ctrl+C or Ctrl+D at any prompt) **Cause:** You answered no at a `Continue? (y/N)` confirmation, or interrupted an interactive prompt. Prompts only appear in an interactive terminal, so scripts and CI never see this code. See [`--no-input`](/reference/global-flags/#--no-input). The process exits with status 1, except after Ctrl+C, which exits with 130 like any other interrupted command. **Solution:** * Run the command again and answer `y`, or pass `--yes` to skip the question ### 5005 - CONFIRMATION\_REQUIRED **Message:** `` followed by `Use --yes to confirm.` **Cause:** A destructive command (`repo delete`, `pr comments delete`, `webhook delete`, …) ran without `--yes` where it could not ask: a pipe, CI, `--json`, or [`--no-input`](/reference/global-flags/#--no-input). **Solution:** Rerun with `--yes`. `context.retry` holds the command with `--yes` added, quoted for POSIX shells on macOS and Linux and for PowerShell on Windows (`cmd.exe` is not supported): ```bash bb repo delete acme/old-site --json ``` ```json { "name": "BBError", "code": 5005, "message": "This will permanently delete acme/old-site.\nUse --yes to confirm.", "context": { "retry": "bb repo delete acme/old-site --json --yes" } } ``` Before this code existed the same failure used `5001`. *** ## Context errors (6xxx) ### 6001 - CONTEXT\_REPO\_NOT\_FOUND **Message:** Depends on the missing context: `Not in a git repository.`, `Git repository has no remote configured.`, or `Remote … is not a Bitbucket URL.`, followed by instructions to supply `--workspace` and `--repo`. **Cause:** The CLI could not resolve a Bitbucket repository. JSON errors include `context.reason` and, when available, `context.remoteUrl`. **Solution:** 1. Use explicit flags: ```bash bb pr list -w myworkspace -r myrepo ``` 2. Supply both values outside a checkout; a default workspace alone does not identify a repository. 3. Run from within a cloned Bitbucket repository See [Understanding Repository Context](/guides/repository-context/) for details. ### 6002 - CONTEXT\_WORKSPACE\_NOT\_FOUND **Message:** Could not determine workspace **Cause:** The CLI couldn’t figure out which workspace to use. **Solution:** 1. Use the `-w` flag: ```bash bb repo list -w myworkspace ``` 2. Set a default workspace: ```bash bb config set defaultWorkspace myworkspace ``` *** ## Network errors (7xxx) ### 7001 - NETWORK\_ERROR **Cause:** The request failed before an HTTP response was received (offline, DNS issue, proxy/TLS issue, etc.). Transient failures (dropped connections, temporary DNS errors, timeouts) on read requests are automatically retried up to 3 times with exponential backoff before this error surfaces. Permanent-looking failures (e.g. unknown host, connection refused) and write requests fail immediately. `7001` has two distinct messages. The connectivity one: > Network error: Unable to reach Bitbucket API. Run with BB\_DEBUG=http for details. If you’re behind a proxy or using a custom CA, check your environment. And the timeout one, when the server accepted the connection but never answered: > Network error: Request to Bitbucket API timed out after 30000ms. The server accepted the connection but did not respond in time. Increase or disable the timeout via BB\_HTTP\_TIMEOUT (milliseconds; set BB\_HTTP\_TIMEOUT=0 to disable), or run with BB\_DEBUG=http for details. **Solution:** * Check internet and DNS connectivity * Verify proxy/TLS settings if applicable * If the message says *timed out*, raise or remove the 30-second default: ```bash BB_HTTP_TIMEOUT=60000 bb pr list # 60 seconds BB_HTTP_TIMEOUT=0 bb pr list # no timeout ``` See [Environment Variables](/reference/environment-variables/) for the full list. *** ## Output formatting errors (8xxx) ### 8001 - JQ\_FAILED **Message:** jq evaluation failed: `` **Cause:** The expression passed to `--jq` is invalid or produced a runtime error. **Solution:** * Test the expression interactively against the full output first: ```bash bb pr list --json | jq '' ``` * The embedded jq is jq 1.8.x; module imports (`include`, `import`) are not supported. * Remember `--jq` runs *after* field projection; when combined with `--json fields`, the input is already a flat array (the wrapper has been dropped). ### 8002 - JSON\_FORMAT\_INVALID `8002` covers flag combinations that can’t work and an empty field list. **`--jq requires --json`** (or **`--lean requires --json`**); add `--json`, with or without a field list: ```bash bb pr list --json --jq '.count' ``` `bb api` is exempt, because its output is already JSON: `bb api /repositories/my-ws --jq '.values[].name'` needs no `--json`. `--jq` and `--lean` signal that you want machine-readable output, so this error is a JSON envelope on stderr even though `--json` is missing. **`--raw-output requires --jq`**; `--raw-output` only changes how `--jq` prints strings, so pass a `--jq` expression with it. **`--json field list cannot be empty`**; triggered by `--json ""` or `--json ,,,`. Drop the argument to get the full output, or name at least one field: ```bash bb pr list --json bb pr list --json id,title ``` *** ## Shell completion errors (9001–9002) ### 9001 - COMPLETION\_INSTALL\_FAILED **Message:** Failed to install completions: `` **Cause:** `bb completion install` couldn’t write the shell completion hooks; typically because the target shell profile isn’t writable, or the underlying `tabtab` install failed. **Solution:** * Check write permissions on your shell rc file (e.g. `~/.zshrc`, `~/.bashrc`) * If the issue persists, install completions manually by following your shell’s documentation for `tabtab` ### 9002 - COMPLETION\_UNINSTALL\_FAILED **Message:** Failed to uninstall completions: `` **Cause:** `bb completion uninstall` couldn’t remove the completion hooks; usually because the rc file isn’t writable or the entry was already removed manually. **Solution:** * Check write permissions on your shell rc file * Manually remove the `tabtab` block from your shell rc file if needed *** ## CI outcome errors (10001) ### 10001 - CI\_FAILED **Message:** `Pipeline #42 finished with status FAILED.` or `1 check did not pass on pull request #7: build.` **Cause:** `bb pipeline watch` saw the run end without passing (failed, stopped, error, expired) or pause on a manual step, or `bb pr checks --watch` saw a check end as failed or stopped, or no check was reported at all. The command still prints the final state first; with `--json` the state goes to stdout and this error to stderr. **Solution:** ```bash bb pipeline logs 42 --step 2 # read the failing step bb pr checks 7 # see which check failed ``` *** ## Unknown errors (9999) ### 9999 - UNKNOWN **Message:** An unexpected error occurred **Cause:** The CLI encountered an error that doesn’t fit any known category. **Solution:** * Check the error message for details * Retry the command * If the issue persists, [report it](https://github.com/0pilatos0/bitbucket-cli/issues) with full debug output: ```bash BB_DEBUG=verbose bb 2>&1 ``` *** ## Exit codes and error codes `0` on success, normally `1` on failure, and `130` for Ctrl+C at a prompt. Set `BB_DETAILED_EXIT_CODES` to any non-empty value to get a status per failure class instead. It is opt-in so scripts that compare `$?` against `1` keep working: | Status | Meaning | Error codes | | ------ | --------------------- | ------------------------------------------------------------------------------- | | `0` | Success | | | `1` | Any other failure | everything not listed below | | `2` | Usage error | `4003`, `5001`, `5002`, `5003`, `6001`, `6002`, `8002`, argument-parsing errors | | `3` | Not found | `2002` (any 404, including a missing diff or log) | | `4` | Authentication needed | `1001`, `1002`, `1003` | | `5` | Confirmation required | `5005` | | `130` | Ctrl+C at a prompt | `5004` after an interrupt | ```bash export BB_DETAILED_EXIT_CODES=1 bb pr view 999 --json 2> err.json case $? in 0) ;; 3) echo "no such PR" ;; 4) echo "run bb auth login" ;; *) jq -r '.message' err.json ;; esac ``` A `403` (`2003`) stays `1`: it can mean a missing scope or a repository permission, so read `code` and `hint` for it. The exit status never carries the full error code; read it from the stderr payload instead: ```bash bb pr view 999 --json 2> err.json || jq -r '.code' err.json # 2002 ``` That payload is a single-line JSON object: ```json { "name": "APIError", "code": 2002, "message": "Pull request 999 not found in acme/demo.", "context": { "status": 404, "method": "GET", "url": "/repositories/acme/demo/pullrequests/999" }, "statusCode": 404 } ``` See [Exit Codes in the scripting guide](/guides/scripting/#exit-codes) for a worked example, and [JSON Output](/reference/json-output/) for every payload field and the `context` shape per code. ## Handling errors in scripts Keep stdout and stderr on separate files. Folding stderr into the data file (`> prs.json 2>&1`) puts the error envelope; and any warning the CLI prints; inside `prs.json`, which breaks `jq` on the next line. ```bash #!/bin/bash set -euo pipefail if ! bb pr list -w myworkspace -r myrepo --json > prs.json 2> pr-error.json; then echo "Failed to list PRs: $(jq -r '.message // "unknown error"' pr-error.json)" exit 1 fi jq -r '.pullRequests[].title' prs.json ``` # Global Flags > Flags accepted by every bb command These flags are accepted by every `bb` command; the same set `bb --help` prints. `--limit` and `--all` look global but are not; see [List-command flags](#list-command-flags). ## Quick reference | Flag | Type | Default | Description | | ----------------------------- | ------------ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `--json [fields]` | optional CSV | *off* | Emit machine-readable JSON. Pass a comma-separated field list to project the output. | | `--jq ` | string | *off* | Run the JSON output through an in-process `jq` filter. Requires `--json` everywhere except `bb api`. | | `--raw-output` | boolean | *off* | Print `--jq` string results without JSON quotes, like `jq -r`. Requires `--jq`. | | `--lean` | boolean | *off* | Trim every `links` map in JSON output to its web URL (`links.html`). Requires `--json` everywhere except `bb api`. | | `--no-color` | boolean | colors on | Disable ANSI colors. Also honoured: `NO_COLOR`, `FORCE_COLOR`. | | `--no-unicode` | boolean | Unicode on | Use ASCII fallbacks for separators, arrows, and status icons. Also honoured: `BB_NO_UNICODE`. | | `--no-truncate` | boolean | *off* | Print every table cell in full instead of fitting the table to the terminal width. | | `--no-input` | boolean | *off* | Never prompt, even in an interactive terminal (except `bb completion install`, see [`--no-input`](#--no-input)). Also honoured: `BB_PROMPT_DISABLED`. | | `--locale ` | BCP-47 | system | Locale for date/time formatting (e.g. `de-DE`, `ja-JP`). Falls back to `BB_LOCALE`, then `LC_TIME`/`LC_ALL`/`LANG`, then `en-US`. | | `-w, --workspace ` | string | git remote / env / config | Override the workspace. | | `-r, --repo ` | string | git remote | Override the repository. | | `--account ` | string | active account | Use this saved account. Also honoured: `BB_ACCOUNT`. | | `-h, --help` | boolean | n/a | Print help for the command. | | `-V, --version` | boolean | n/a | Print the CLI version. | The positive forms `--color`, `--unicode` and `--truncate` are also accepted, even though `bb --help` does not list them. Only `--color` changes anything; see [Precedence summary](#precedence-summary). `bb help ` is equivalent to `bb --help` and works at every depth (`bb help pr`, `bb help pr comments`). The root `Commands:` list does not show `help`, but it is there. Every subcommand’s help lists these flags under `Global Options:`. A group run without a subcommand (`bb pr`) prints its help and exits 0. ## Output flags ### `--json [fields]` Switch a command to JSON output. With no argument, the full response is printed. Pass a comma-separated field list to project to just those keys: ```bash bb pr list --json bb pr list --json id,title,state ``` JSON mode also disables spinners, colors, and progress notes so the output is safe to pipe. JSON is pretty-printed on a terminal and compact (one line) when stdout is a pipe or a file. `--json` takes an optional value, so a bare `--json` swallows the next plain token. Put it after the subcommand: ```bash bb pr list --json # correct bb --json=id,title pr list # correct; the '=' form swallows nothing bb --json pr list # error 5002 ``` The last one fails with `--json consumed 'pr' as its field list, so 'list' was parsed as a top-level command.`; rendered as a JSON envelope, since `--json` was set. The other global flags are not position-sensitive in this way. ### `--jq ` Filter the JSON output through a `jq` expression. The filter runs in-process via the embedded jq engine, so you don’t need the `jq` binary installed. Requires `--json`: ```bash bb pr list --json --jq '.pullRequests[] | select(.state == "OPEN") | .title' ``` `bb api` is the one exception. Its output is already JSON, so it takes `--jq` on its own: ```bash bb api /repositories/my-ws --jq '.values[].name' ``` Everywhere else, `--jq` without `--json` fails with `--jq requires --json` (error 8002), printed as plain text on stderr rather than as a JSON envelope. Field projection runs *before* jq and drops the wrapper key, so when you combine `--json ` with `--jq` the filter sees a flat array; start it with `.[]`, not `.pullRequests[]`: ```bash bb pr list --json id,title --jq '.[] | .title' ``` ### `--raw-output` Print string results from `--jq` as plain text instead of JSON strings, like `jq -r`. Requires `--jq`: ```bash bb pr list --json --jq '.pullRequests[].title' --raw-output ``` ### `--lean` Trim Bitbucket’s `links` maps, which make up most of a list payload, down to the web URL (`links.html`). Requires `--json`, except in `bb api`: ```bash bb pr list --json --lean ``` See [Lean output](/reference/json-output/#lean-output---lean). ### `--no-truncate` On a terminal, tables are fitted to the window width. Long titles, descriptions, messages and comment bodies shrink and end in `...`; ids, hashes, UUIDs and URLs keep their full width. A column never shrinks below 10 characters, so on a very narrow terminal rows wrap instead. `--no-truncate` turns the fitting off and prints every cell in full: ```bash bb pr comments list 42 --no-truncate ``` When stdout is not a terminal (piped or redirected), tables are printed as tab-separated values: one line per row, no header, no padding and nothing cut off, so `bb pr list | cut -f2` gives the full titles. Date columns are relative on a terminal (`3 hours ago`) and ISO 8601 when piped. The “Showing N results” hint goes to stderr. JSON is never truncated, so `--no-truncate` does nothing with `--json`. ### `--no-color` and `--no-unicode` `--no-color` disables ANSI colors. `--no-unicode` swaps Unicode separators and status glyphs for plain ASCII equivalents; useful for log aggregators that mangle Unicode, or terminals that don’t render the symbols cleanly. Colors switch off on their own when stdout is not a TTY, so piping to a file already gives you plain text. Unicode does not: `bb pr list > out.txt` still writes the `─` separator rules, `→` branch arrows and `✓`/`✗`/`○` status glyphs. Pass `--no-unicode` or set `BB_NO_UNICODE` if you need ASCII in a redirect. Tables themselves are already ASCII; the header underline is plain `-` characters and there are no vertical bars or outer border, so `--no-unicode` does not change the table frame. The two environment variables are not symmetric: * `NO_COLOR` triggers on any value, including the empty string. Both `FORCE_COLOR` (any value except `0`) and a literal `--color` in argv override it. * `BB_NO_UNICODE` triggers on any non-empty value and cannot be overridden per command. `--unicode` is accepted but has no effect while `BB_NO_UNICODE` is set; unset the variable instead. ## Write-command flag ### `--dry-run` Commands that change something on Bitbucket accept `--dry-run`. The command runs as usual up to its first write request (`POST`, `PUT`, `PATCH` or `DELETE`), prints that request instead of sending it, and exits 0. Read requests it needs along the way, such as looking up a user or the current pull request, still go out. Confirmation prompts and the `--yes` gate are skipped, because nothing is changed. ```bash $ bb pr decline 42 --dry-run ℹ Dry run: this request was not sent. POST https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/42/decline ``` The body follows the URL when the request has one, pretty-printed as JSON. Multipart uploads list their fields, with files shown by name and size. Credential-like keys (`password`, `secret`, `token`, …) are redacted in the body and the URL query. When a command would send several writes, only the first is shown. With `--json` the output is `{ "dryRun": true, "request": { "method", "url", "body" } }`; see [JSON output](/reference/json-output/#dry-run). `--dry-run` is available on `bb api` and on every command that writes: `repo create`, `repo delete`, `repo default-reviewers add|remove`, `repo downloads upload|delete`, `pr create`, `pr edit`, `pr merge`, `pr approve`, `pr unapprove`, `pr request-changes`, `pr decline`, `pr ready`, `pr comments add|edit|delete|reply|resolve|unresolve`, `pr reviewers add|remove`, `snippet create|edit|delete|watch|unwatch`, `snippet comments add|edit|delete`, `pipeline run|stop`, `status set`, `project create`, `webhook create|delete`, `branch-restriction create|delete`, `ssh-key add|delete` and `gpg-key add|delete`. Commands that only change local files (`auth`, `config`, `alias`, `completion`, `repo clone`, `pr checkout`) do not accept it. ## Prompt flag ### `--no-input` A few commands ask for what is missing when you run them in an interactive terminal: * Destructive commands (`bb repo delete`, `bb repo default-reviewers remove`, `bb repo downloads delete`, `bb pr comments delete`, `bb snippet delete`, `bb snippet comments delete`, `bb webhook delete`, `bb branch-restriction delete`, `bb ssh-key delete`, `bb gpg-key delete`) name what they are about to change and ask `Continue? (y/N)` instead of failing without `--yes`. * `bb pr create` asks for the title, and then an optional description, when `--title` is missing. * `bb auth login` asks whether to use the browser (OAuth) or an API token when no auth flag is given, and asks for a missing Atlassian account email or token. Prompts only appear when stdin **and** stdout are both terminals. Piped input, redirected output, `--json`, `--no-input` and a non-empty `BB_PROMPT_DISABLED` each switch them off, and the command then behaves exactly as it does in a script: a missing `--yes` or `--title` is an error, and `bb auth login` with no token fails with [1001 AUTH\_REQUIRED](/reference/error-codes/#1001---auth_required) instead of opening a browser. `--json` alone still lets a person in a terminal finish a browser login. `bb completion install` is the exception. Its shell question comes from the completion installer and ignores all of these switches; see [`bb completion install`](/commands/completion/#bb-completion-install). ```bash bb repo delete myworkspace/old-repo # asks Continue? (y/N) bb repo delete myworkspace/old-repo --yes # never asks bb repo delete myworkspace/old-repo --no-input # fails: Use --yes to confirm. ``` Answering anything other than `y` or `yes`, or pressing Ctrl+C or Ctrl+D at a prompt, stops the command with error [5004 PROMPT\_CANCELLED](/reference/error-codes/#5004---prompt_cancelled) and nothing is changed. Ctrl+C exits with status 130, the rest with 1. ## List-command flags `--limit` and `--all` are options on list commands, not global flags. `bb pr view --limit 5` fails with `✗ unknown option '--limit'`. ### `--limit ` Cap the number of items returned. The default is 25 on every list command. This caps items, not pages. The CLI fetches up to 50 items per request and keeps paging until the cap is met or no next page remains. Short pages and client-side filtering can require more requests. Values below 1 fail with `--limit must be a positive integer`. ### `--all` Fetch every page. `--all` overrides `--limit`. Both flags are available on these commands: * `bb repo list` * `bb repo ls` * `bb repo downloads list` * `bb pr list` * `bb pr activity` * `bb pr comments list` * `bb snippet list` * `bb snippet comments list` * `bb pipeline list` * `bb deployment list` * `bb deployment environments` * `bb branch-restriction list` * `bb ssh-key list` * `bb gpg-key list` * `bb commit list` * `bb status list` * `bb workspace list` * `bb project list` * `bb search code` * `bb webhook list` `bb pr checks` reads every page and `bb pr reviewers list`, `bb repo default-reviewers list` and `bb config list` are single-request; none of them accepts these flags. `bb api` uses `--paginate` instead. When `--limit` cuts a table short, the CLI prints a hint: ```text Showing 25 pull requests. Use --limit or --all to see more. ``` That hint is table output only. Under `--json` nothing is printed, so compare the envelope’s `count` against your limit yourself. ## Context flags ### `-w, --workspace` and `-r, --repo` Override the workspace and repository for the current command: ```bash bb pr list -w myworkspace -r myrepo ``` Repository-scoped commands fall back to the git remote of the current directory. Workspace-only commands never look at git; they resolve `--workspace`, then `BB_WORKSPACE`, then `defaultWorkspace`, and fail with `No workspace specified.` if none is set. That covers `bb workspace view`, every `bb project` and `bb snippet` command, `bb repo list`, `bb repo create`, `bb repo clone`, and `bb api` when it fills a `{workspace}` placeholder. Being inside a Bitbucket clone is not enough for those. The full order is in [Environment Variables](/reference/environment-variables/#resolution-order), with worked examples in [Understanding Repository Context](/guides/repository-context/). ## Account flag ### `--account ` Run one command as another saved account without changing the active one: ```bash bb --account personal pr list BB_ACCOUNT=personal bb pr list # same, for a whole shell or CI job ``` The flag beats `BB_ACCOUNT`, which beats the account picked with `bb auth switch`. On `bb auth login` it names the account to create or re-authenticate. See [Multiple accounts](/getting-started/authentication/#multiple-accounts). ## Locale flag ### `--locale ` Pin a BCP-47 locale tag for date/time formatting. This affects every human-readable date the CLI prints; PR timestamps, activity entries, comment dates. JSON output is locale-independent. ```bash bb pr list --locale de-DE bb pr view 42 --locale ja-JP ``` If `--locale` is unset, the CLI walks `BB_LOCALE`, then the POSIX locale variables in the order `LC_TIME` → `LC_ALL` → `LANG`, then falls back to `en-US`. Note that `LC_TIME` is checked *before* `LC_ALL`, which is the reverse of the usual POSIX override order; if you set both, `LC_TIME` wins here. Values are normalised before use: a `.UTF-8` codeset or `@euro` modifier suffix is stripped, `_` becomes `-`, and `C` / `POSIX` map to `en-US`. An invalid BCP-47 tag never errors; it silently falls back to `en-US`. ## Precedence summary For settings that can come from multiple sources, the order is usually: 1. Command-line flag 2. Environment variable 3. Config file 4. Built-in default Two documented exceptions: `FORCE_COLOR` beats `--no-color`, and `--unicode` cannot override `BB_NO_UNICODE`. Color has one more wrinkle. `--color` is the highest-precedence color input of all, ahead of `FORCE_COLOR`, `--no-color` and `NO_COLOR`. It is matched by a raw scan of argv. So `bb pr diff 42 --color never` turns global color on even though it leaves the diff body uncolored, because `bb pr diff` takes `--color ` with the choices `auto`, `always` and `never`. ## See also * [Environment Variables](/reference/environment-variables/); env-var equivalents and precedence * [JSON Output](/reference/json-output/); schema, field projection, scripting tips * [Configuration File](/reference/configuration/); persistent settings # JSON Output > Reference for JSON output behavior across all commands All commands support the global `--json` flag, with optional field selection and a built-in `--jq` filter modeled on the [`gh` CLI](https://cli.github.com/manual/gh_help_formatting). `--json` writes command data to stdout and command errors to stderr. Update banners and retry notices are suppressed. HTTP debug traces from `BB_DEBUG` also go to stderr, so stdout stays parseable with tracing on. Parser errors such as unknown flags are JSON too (see [Errors](#errors)). JSON is pretty-printed with two-space indentation when stdout is a terminal, and compact (one line, no indentation) when stdout is a pipe or a file. The data is the same either way; pipe to `jq .` if you want indentation in a file. `--jq` results follow the same rule. ## Quick usage ```bash # Full JSON output bb pr list --json # Project to a comma-separated field list bb pr list --json id,title,state # Filter through jq (no external jq binary required) bb pr list --json id,title,state --jq '.[] | select(.state == "OPEN") | .title' # Disable color formatting and return JSON bb repo view --json --no-color ``` ## Field selection (`--json fields`) Pass a comma-separated field list to project the output to just those fields, matching the way `gh` CLI handles `--json`: ```bash bb pr list --json id,title,state bb pr list --json id,title,author.display_name ``` **Rules:** * Bare `--json` (no field list) keeps the full output; backwards compatible. * Top-level field names become the keys of each result object verbatim, including dotted paths (`author.display_name` becomes the literal key `"author.display_name"`). * Dotted paths traverse nested objects; missing intermediates yield `null`. * If the output is already an array, each item is projected. * If the output is an envelope object, the CLI looks for the first of these keys holding an **array** and projects that array instead, dropping the envelope; the result is a flat array. The lookup is based on key names, not on the command or resource type. ```text pullRequests repositories snippets comments reviewers activities statuses files pipelines commits workspaces projects entries downloads results webhooks branchRestrictions sshKeys gpgKeys deployments environments values ``` * If no key in that list holds an array, the envelope object itself is projected. #### Example: `bb pr list --json id,title,author.display_name` ```json [ { "id": 42, "title": "Add feature", "author.display_name": "Alice" }, { "id": 41, "title": "Fix bug", "author.display_name": "Bob" } ] ``` #### Before / after: shape change after projection `bb pr list --json` returns the full envelope with metadata and a named array: ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": { "mine": false, "author": null, "reviewer": null, "source": null, "destination": null, "query": null }, "count": 2, "hasMore": false, "limit": 25, "pullRequests": [ { "id": 42, "title": "Add feature", "state": "OPEN", "author": { "display_name": "Alice", "nickname": "alice" }, "links": { "html": { "href": "..." } } }, { "id": 41, "title": "Fix bug", "state": "OPEN", "author": {...}, "links": {...} } ] } ``` The same command with `--json id,title,state` drops the envelope and projects each item to a flat array of just those fields: ```json [ { "id": 42, "title": "Add feature", "state": "OPEN" }, { "id": 41, "title": "Fix bug", "state": "OPEN" } ] ``` This is why `--jq` filters use `.[]` after projection (`.[] | select(...)`) but `.pullRequests[]` against the full envelope. #### Trap: envelopes whose array key is not in the list `bb pipeline view` returns `{ workspace, repoSlug, pipeline, steps }`. `steps` is not a wrapper key, so nothing is unwrapped and the field list is applied to the envelope; where `build_number` does not exist: ```bash bb pipeline view 7 --json build_number ``` ```json { "build_number": null } ``` Use `--jq` to reach into the envelope instead: ```bash bb pipeline view 7 --json --jq '.pipeline.build_number' ``` `bb pipeline logs` (without `--step`) puts its array under `steps` too. `bb snippet view --files` returns `files` as an **object**, not an array, so it is not unwrapped either. The reverse trap also exists. `bb pr view` returns a bare pull request, and that payload carries a `reviewers` array (one of the recognized wrapper keys), so `bb pr view 42 --json links.html.href` projects the reviewers instead of the pull request. Use `--jq` against the full output: ```bash bb pr view 42 --json --jq '.links.html.href' ``` `bb api --paginate` merges every page into `{"values": [...]}`, and `values` **is** a wrapper key, so projection does unwrap it: ```bash bb api /repositories/my-ws --paginate --json name,full_name ``` ## Built-in jq (`--jq`) The `--jq ` flag pipes the JSON output through an embedded jq engine ([jq-wasm](https://www.npmjs.com/package/jq-wasm)); no external `jq` binary required, no `bash` pipe to fail on Windows. ```bash # One value per line (strings come out quoted, see the raw-output note below) bb pr list --json --jq '.pullRequests[].title' # Same, as plain text for the shell bb pr list --json --jq '.pullRequests[].title' --raw-output # Combine with field selection (jq runs after projection) bb pr list --json id,title,state --jq '.[] | select(.state == "OPEN") | .title' # Aggregate bb pr list --json --jq '.count' # Repository names bb repo list --json --jq '.repositories[].full_name' # Diff file paths bb pr diff 42 --stat --json --jq '.files[].path' # Auth status as a bare boolean bb auth status --json --jq '.authenticated' # Project + format with jq string interpolation as raw TSV bb pr list --json id,title,author.display_name --raw-output \ --jq '.[] | "\(.id)\t\(.["author.display_name"])\t\(.title)"' # Pull request URLs bb pr list --json --jq '.pullRequests[].links.html.href' # Project a nested URL out of a single resource bb repo view --json links.html.href --jq '.["links.html.href"]' # Top open PRs by author with built-in jq (no projection — full shape) bb pr list --json --jq '.pullRequests | group_by(.author.display_name) | map({ author: .[0].author.display_name, count: length }) | sort_by(-.count)' ``` Every expression above also works with an external `jq` binary; pipe the full output instead: `bb pr list --json | jq '.pullRequests[].title'`. **Rules:** * By default `--jq` behaves like `jq` **without** `-r`, so string results keep their quotes: ```bash bb pr list --json --jq '.pullRequests[].title' # "Add feature" # "Fix bug" ``` Add `--raw-output` (the built-in equivalent of `jq -r`) when a script needs unquoted text. Objects, arrays, numbers and booleans are unaffected: ```bash bb pr list --json --jq '.pullRequests[].title' --raw-output # Add feature # Fix bug ``` Without `--jq`, `--raw-output` exits non-zero with error 8002 (`--raw-output requires --jq`). * `--jq` requires `--json` everywhere except `bb api` (next item). Elsewhere, `--jq` alone exits non-zero with error 8002 (`--jq requires --json`), reported as a JSON envelope on stderr because `--jq` asked for machine-readable output. * `bb api` is the exception: its output is already JSON, so `bb api /repositories/my-ws --jq '.values[].name'` works without `--json`. * jq runs *after* field projection, so `.` refers to the projected shape; a flat array when both `--json fields` and a wrapper-style command are involved. * Invalid jq expressions exit non-zero with the underlying jq compiler error (error code [8001](/reference/error-codes/#8001---jq_failed)). Caution The embedded jq is jq 1.8.x. Module imports (`include`, `import`) and filesystem-dependent features are not supported. ## Lean output (`--lean`) Almost every Bitbucket object carries a `links` map of API, avatar and action URLs, and in list output those maps are most of the bytes. `--lean` trims every `links` map, at any depth, down to its `html` entry (the web URL) and drops maps that have none. Everything else is untouched, so `.links.html.href` keeps working: ```bash bb pr list --json --lean bb pr list --json --lean --jq '.pullRequests[].links.html.href' ``` ```json { "id": 42, "title": "Add feature", "author": { "display_name": "Jane Doe", "uuid": "{...}" }, "links": { "html": { "href": "https://bitbucket.org/ws/repo/pull-requests/42" } } } ``` `--lean` is opt-in and needs `--json` (except in `bb api`). It runs after field projection and before `--jq`, so `--json links --lean` also comes back with only `links.html`. Clone URLs (`links.clone`) and other non-web links are removed too; leave `--lean` off when you need them. ## Output patterns ### Pattern 1: collection (list commands) Used by: `pr list`, `repo list`, `pr comments list`, `pr activity`, `snippet list`, `snippet comments list`, `pipeline list`, `commit list`, `status list`, `workspace list`, `project list`, `search code`, `webhook list` Returns an envelope with metadata, a `count`, paging info, and a named array: ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": { "mine": false }, "count": 2, "hasMore": false, "limit": 25, "pullRequests": [...] } ``` `hasMore` is `true` when `--limit` cut the list short and Bitbucket has more results; rerun with a higher `--limit` or `--all` to get them. `limit` is the effective cap (`25` by default) and `null` under `--all`. `--json ` drops the envelope, and these two keys with it, so check paging with `--jq`: ```bash if [ "$(bb pr list --json --jq '.hasMore')" = "true" ]; then bb pr list --all --json > prs.json fi ``` **Common envelope fields:** | Field | Type | Present in | | ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `workspace` | string | All collections except `workspace list` (whose slugs live in the items) and the account-scoped `ssh-key list` / `gpg-key list` | | `repoSlug` | string | Repository-scoped collections: `pr list`, `pr comments list`, `pr activity`, `pipeline list`, `commit list`, `status list`, `repo ls`, `repo downloads list`, `webhook list` (default `--scope repo` only), `branch-restriction list`, `deployment list`, `deployment environments` | | `count` | number | All collections | | `hasMore` | bool | All paginated collections (every command with `--limit`/`--all`) | | `limit` | number | All paginated collections; `null` under `--all` | | `state` | string | `pr list` | | `filters` | object | `pr list`, `pr activity`, `pr comments list`, `branch-restriction list`; always present, with an unset value inside (`{"mine": false}`, `{"types": []}`, `{"resolution": null}`) or `{}` | | `commit` | string | `status list` | | `sort` | string | `pipeline list` | | `ref`, `path` | string | `repo ls` | | `query` | string | `search code`; the query as sent, including any `repo:` scope added by `--repo` | **Collection key names:** | Command | Array key | | ------------------------- | -------------------- | | `pr list` | `pullRequests` | | `repo list` | `repositories` | | `pr comments list` | `comments` | | `pr activity` | `activities` | | `pipeline list` | `pipelines` | | `commit list` | `commits` | | `status list` | `statuses` | | `workspace list` | `workspaces` | | `project list` | `projects` | | `repo ls` | `entries` | | `repo downloads list` | `downloads` | | `branch-restriction list` | `branchRestrictions` | | `ssh-key list` | `sshKeys` | | `gpg-key list` | `gpgKeys` | | `deployment list` | `deployments` | | `deployment environments` | `environments` | | `snippet list` | `snippets` | | `snippet comments list` | `comments` | | `search code` | `results` | | `webhook list` | `webhooks` | Three commands return collections without going through the shared list plumbing. They have no `--limit`/`--all`, and so carry no `hasMore` or `limit` (`pr checks` always reads every page): | Command | Shape | | ----------------------------- | --------------------------------------------------------------------------- | | `pr reviewers list` | `{ workspace, repoSlug, pullRequestId, count, reviewers }` | | `repo default-reviewers list` | `{ workspace, repoSlug, mode, count, reviewers }` | | `pr checks` | `{ pullRequestId, workspace, repoSlug, summary, statuses }`; **no `count`** | #### Example: `bb pr list --json` ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": { "mine": false }, "count": 2, "hasMore": false, "limit": 25, "pullRequests": [ { "id": 42, "title": "Add feature", "state": "OPEN", "draft": false, "author": { "display_name": "Alice", "nickname": "alice" }, "source": { "branch": { "name": "feature/add-feature" } }, "destination": { "branch": { "name": "main" } }, "created_on": "2026-01-15T10:00:00.000000+00:00", "updated_on": "2026-01-16T14:30:00.000000+00:00", "links": { "html": { "href": "https://bitbucket.org/myworkspace/myrepo/pull-requests/42" } } } ] } ``` #### Example: `bb pr list --mine --json` ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": { "mine": true, "author": null, "reviewer": "@me", "source": null, "destination": null, "query": null }, "count": 1, "hasMore": false, "limit": 25, "pullRequests": [...] } ``` #### Example: `bb pr checks 42 --json` ```json { "pullRequestId": 42, "workspace": "myworkspace", "repoSlug": "myrepo", "summary": { "successful": 2, "failed": 0, "pending": 1 }, "statuses": [...] } ``` *** ### Pattern 2a: bare resource Used by: `pr view`, `pr create`, `pr edit`, `pr comments view`, `repo view`, `repo create`, `snippet view` (no `--file`/`--files`), `snippet create`, `snippet edit` Returns the raw Bitbucket API resource object directly, with no envelope: ```json { "type": "pullrequest", "id": 42, "title": "Add feature", "state": "OPEN", "author": {...}, "source": {...}, "destination": {...}, "participants": [...], "links": {...} } ``` ### Pattern 2b: enveloped resource The rest of the single-resource commands wrap the payload in a context envelope. Read the resource from the named key, not from the root: | Command | Shape | | --------------------------------- | ------------------------------------------------------------------------------------------------- | | `commit view` | `{ workspace, repoSlug, commit }` | | `project view`, `project create` | `{ workspace, project }` | | `webhook view`, `webhook create` | `{ workspace, repoSlug, webhook }` (no `repoSlug` with `--scope workspace`) | | `workspace view` | `{ workspace }` (the workspace object) | | `status set` | `{ workspace, repoSlug, commit, status }` | | `pr status` | `{ workspace, repoSlug, currentBranch, currentBranchPullRequest, createdByYou, reviewRequested }` | | `pipeline view`, `pipeline watch` | `{ workspace, repoSlug, pipeline, steps }` | | `pipeline run` | `{ workspace, repoSlug, pipeline }` | | `pipeline logs --step ` | `{ workspace, repoSlug, pipelineId, stepUuid, log }` | | `pipeline logs` (no `--step`) | `{ workspace, repoSlug, pipelineId, count, steps }` | | `snippet view --file ` | `{ file, content }` | | `snippet view --files` | `{ snippet, files }` (`files` is an object keyed by filename) | `pipeline view` and `pipeline logs` put their array under `steps`, which is not a projection wrapper key; see the trap above. *** ### Pattern 3: action (approve/merge/decline commands) Used by: `pr approve`, `pr unapprove`, `pr request-changes`, `pr decline`, `pr merge`, `pr ready`, `pr comments add/edit/delete/reply/resolve/unresolve`, `pr reviewers add/remove`, `webhook delete`, `auth logout` Returns a success indicator plus resource identifiers: ```json { "success": true, "pullRequestId": 42 } ``` Some action commands include the full resource in the response: ```json { "success": true, "pullRequestId": 42, "pullRequest": {...} } ``` `webhook delete` returns `{ "success": true, "workspace": "...", "repoSlug": "...", "webhookId": "{...}" }` (no `repoSlug` with `--scope workspace`). Two action-style commands do **not** emit `success`; branch on their own key instead: * `auth login` → `{ "authenticated": true, "method": "oauth" | "api_token", "account": "...", "user": {...} }` * `pipeline stop` → `{ "workspace": "...", "repoSlug": "...", "pipelineId": "...", "stopped": true }` (`auth logout` does emit `success`: `{ "authenticated": false, "success": true, "account": "..." }`, plus `"switchedTo"` when another saved account became active and `"revokeFailed": true` when the OAuth token could not be revoked.) *** ### Dry run Any command run with [`--dry-run`](/reference/global-flags/#--dry-run) prints the write request it would have sent instead of its usual JSON, and exits 0: ```json { "dryRun": true, "request": { "method": "POST", "url": "https://api.bitbucket.org/2.0/repositories/myworkspace/myrepo/pullrequests/42/merge", "body": { "type": "pullrequest_merge_parameters", "merge_strategy": "squash" } } } ``` `body` is left out when the request has none. *** ### Pattern 4: mode-based (diff command) Used by: `pr diff` (with `--stat`, `--web`, `--name-only`, or default diff mode) Returns context metadata plus mode-specific data. Every mode includes the same context envelope (`workspace`, `repoSlug`, `pullRequestId`, `mode`); the remaining fields depend on the mode. | Mode | Trigger | Mode-specific fields | | ----------- | ------------- | ------------------------------------------------------------- | | `diff` | (default) | `diff` (string; the raw unified diff text) | | `stat` | `--stat` | `filesChanged`, `totalAdditions`, `totalDeletions`, `files[]` | | `name-only` | `--name-only` | `files[]` (string array of paths) | | `web` | `--web` | `url` | #### `bb pr diff 42 --json` (default mode) The default mode returns the raw unified diff as a single string under `diff`. Newlines are preserved, so the value is suitable for piping to `git apply` or writing to a `.patch` file. ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "diff", "diff": "diff --git a/src/index.ts b/src/index.ts\nindex abc123..def456 100644\n--- a/src/index.ts\n+++ b/src/index.ts\n@@ -1,3 +1,4 @@\n import { foo } from './foo';\n+import { bar } from './bar';\n \n export const main = () => {\n" } ``` Extract just the diff text in scripts with `--raw-output`; without it, `--jq` would emit the value as a quoted, escaped JSON string: ```bash bb pr diff 42 --json --jq '.diff' --raw-output > pr-42.patch ``` #### `bb pr diff 42 --stat --json` ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "stat", "filesChanged": 3, "totalAdditions": 27, "totalDeletions": 11, "files": [{ "path": "src/index.ts", "additions": 10, "deletions": 3 }] } ``` #### `bb pr diff 42 --name-only --json` ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "name-only", "files": ["src/index.ts", "src/utils.ts"] } ``` #### `bb pr diff 42 --web --json` ```json { "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "web", "url": "https://bitbucket.org/myworkspace/myrepo/pull-requests/42/diff" } ``` *** ### Pattern 5: auth & config (custom) These commands have unique output shapes: #### `bb auth status --json` ```json { "authenticated": true, "account": "default", "accounts": ["default"], "method": "oauth", "user": { "username": "myuser", "displayName": "My Name", "accountId": "70121:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, "defaultWorkspace": "myworkspace", "tokenExpiresAt": 1767225600 } ``` `tokenExpiresAt` (a Unix timestamp in seconds) is present only for OAuth credentials. `account` is the account in use and `accounts` lists every saved account. When the current account has no credentials the payload is: ```json { "authenticated": false, "account": "default", "accounts": [] } ``` The command also exits 1 in that case. Under `set -e`, append `|| true` when you want to read the payload instead of stopping. `method` is `"api_token"` or `"oauth"`, the same values `auth login --json` reports. The config file stores API-token auth as `authMethod: "basic"`, which is what `auth status --json` reported in earlier releases. Check `authenticated` first, and accept both values if your script must run against older versions. #### `bb auth token --json` OAuth credentials return the access token: ```json { "token": "", "type": "bearer" } ``` API-token credentials return the base64 of `email:apiToken` (the stored key is named `username`): ```json { "token": "", "type": "basic" } ``` #### `bb config list --json` ```json { "configPath": "/Users/you/.config/bb/config.json", "config": { "username": "you@example.com", "defaultWorkspace": "myworkspace", "apiToken": "********", "skipVersionCheck": false } } ``` #### `bb config get --json` ```json { "key": "defaultWorkspace", "value": "myworkspace" } ``` *** ## Scripting notes * Prefer `--json` in scripts rather than parsing text output. * Under `--json`, stdout carries only the JSON document. Status lines such as “Opening browser…” go to stderr. * Do not rely on text formatting symbols (`✓`, table separators, colors). * Prefer the built-in `--jq` and `--json fields` over external pipes; they avoid an extra binary dependency and behave identically across platforms. * Envelope keys (`count`, `hasMore`, `limit`, `pullRequests`, `repositories`, `success`) are owned by the CLI and stable. Item fields come straight from the Bitbucket API, so read `.pullRequests[].id` rather than assuming a shape for the whole item. * `pr list` returns lightweight PR objects. Use `pr view ` for full details including `participants` and `reviewers`. ## Errors When a command fails, the CLI exits non-zero (see [exit codes](/reference/error-codes/#exit-codes-and-error-codes), including the opt-in `BB_DETAILED_EXIT_CODES` map). * In normal mode, errors are plain text on stderr. * In `--json` mode, errors are compact JSON objects on stderr. `--jq` without `--json` also reports its error this way. Argument-parsing failures follow the same rule whenever `--json` or `--jq` appears before any `--`: an unknown subcommand (`bb pr lst --json`), an unknown option, a missing argument or too many arguments all come back as an envelope with `context.parseError`, `context.commandPath` and a `hint`. An unknown top-level command (`bb prr --json`) keeps its own shape, `context: { command, args }`, naming the token that didn’t match. A group run without a subcommand (`bb pr --json`) reports `parseError: "missingSubcommand"` instead of printing its help. ```json { "name": "BBError", "code": 5002, "message": "unknown option '--stat'", "context": { "parseError": "unknownOption", "commandPath": "pr list" }, "hint": "Run `bb pr list --help` for usage." } ``` Example: ```bash bb config get invalidKey --json 2>error.json cat error.json # {"name":"BBError","code":4003,"message":"Unknown config key 'invalidKey'. Valid keys: username, defaultWorkspace, credentialStorage, skipVersionCheck, versionCheckInterval, prCreateIncludeDefaultReviewers, gitProtocol","context":{"key":"invalidKey"}} ``` Error JSON fields: | Field | Type | Description | | ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | string | Error class name (usually `BBError`; can also be `APIError`, `AuthError`, `GitError`, `ValidationError`) | | `code` | number | Numeric [error code](/reference/error-codes/) | | `message` | string | Human-readable message | | `context` | object | Optional structured metadata. Shape depends on `code`; see the table below | | `hint` | string | Optional. A single actionable next step: present for `401`, `403`, and `404` failures where the CLI has advice to give, and for argument-parsing errors. In human-readable mode the API hints print as a dimmed continuation line beneath the message. | | `statusCode` | number | `APIError` only. The upstream HTTP status. | | `response` | object \| string | `APIError` only, and only when the upstream response had a body. The raw Bitbucket error payload; usually a JSON object, but a plain-text body (e.g. a request-parser `Bad Request`) is passed through as a string. | | `headers` | object | `bb api` only. The upstream response headers, so scripts can inspect rate-limit or request-id headers on failure. | | `statusText` | string | `bb api` only, and only when the transport provides a reason phrase (HTTP/2 omits it). | `cause` (the underlying `Error`) and stack traces are never exposed in `--json` mode. Do not treat the field list above as closed: new optional keys such as `hint` may be added, so parse defensively rather than asserting an exact key set. #### `context` fields by error code The `context` object is omitted when there’s nothing useful to attach. When present, the shape is: | Code | Typical `context` keys | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `1001` AUTH\_REQUIRED | *(none)* | | `1002` AUTH\_INVALID | `status` (HTTP status, OAuth verification failures only) | | `1003` AUTH\_EXPIRED | `status` (HTTP status from the token endpoint) | | `2001`–`2005` API\_\* | `status`, `method`, `url`; plus the top-level `statusCode` and `response` fields described above. `url` is the request path only; pagination and filter parameters the CLI sends are not part of it, though a query string you write yourself (`bb api '/repositories/acme?role=owner'`) is | | `3001` GIT\_NOT\_REPOSITORY | *(none; never thrown)* | | `3002` GIT\_COMMAND\_FAILED | `command`, `exitCode` | | `3003` GIT\_REMOTE\_NOT\_FOUND | `remote` | | `4001` CONFIG\_READ\_FAILED | *(none)* | | `4002` CONFIG\_WRITE\_FAILED | *(none)* | | `4003` CONFIG\_INVALID\_KEY | `key` | | `5001` VALIDATION\_REQUIRED | `field` (when thrown by `ValidationError`); `parseError`, `commandPath` for a missing argument or option value | | `5002` VALIDATION\_INVALID | varies; usually `{ key, value }`, the offending option name, or the rejected ID. Argument-parsing errors carry `parseError` (`unknownOption`, `unknownCommand`, `excessArguments`, …) and `commandPath` | | `5003` FILE\_NOT\_FOUND | `file` (`snippet create/edit/view`), `bodyFile` (`--body-file` on `pr create/edit`, `pr comments add/reply`), or `path` (`bb api -F key=@file` / `--input`). `snippet view --file` also attaches `available`, the list of filenames in the snippet | | `5005` CONFIRMATION\_REQUIRED | `retry`: the command to rerun with `--yes`, quoted for `sh` (PowerShell on Windows) | | `6001` CONTEXT\_REPO\_NOT\_FOUND | `reason` (`not_a_git_repo`, `no_remote`, `remote_not_bitbucket`, `ambiguous_remote`, or `unknown`), optional `remoteUrl` or `remotes` (remote names) | | `6002` CONTEXT\_WORKSPACE\_NOT\_FOUND | *(none)* | | `7001` NETWORK\_ERROR | *(usually none; OAuth revoke failures include `status`, `body`)* | | `8001` JQ\_FAILED | `expression`, optional `exitCode` | | `8002` JSON\_FORMAT\_INVALID | *(none)* | | `9001` COMPLETION\_INSTALL\_FAILED | *(none)* | | `9002` COMPLETION\_UNINSTALL\_FAILED | *(none)* | | `10001` CI\_FAILED | `pipeline`, `status` (`pipeline watch`) or `pullRequestId` (`pr checks --watch`) | | `9999` UNKNOWN | *(none)* | In automation, always check the process exit code before parsing stdout JSON. # Token Scopes > Required Bitbucket API token scopes for each bb command When you authenticate with an [API token](/getting-started/authentication/#api-token-for-cicd-and-headless-environments), you pick the scopes it grants. The tables below map command workflows to scopes; some commands read a resource before changing it. API-token write scopes do not include read scopes, and scopes do not override account permissions or repository restrictions. See the [Bitbucket scope reference](https://developer.atlassian.com/cloud/bitbucket/rest/). Generate tokens at [Bitbucket API tokens](https://bitbucket.org/account/settings/api-tokens/). OAuth permissions come from the consumer The CLI requests the legacy OAuth scopes `account repository repository:admin pullrequest pullrequest:write`. Bitbucket grants the scopes configured on the consumer; asking for fewer does not narrow the token. A custom consumer can have additional permissions. Check its settings for the commands you use, or use a scoped API token. See [Atlassian’s OAuth documentation](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/). ## Scope reference Bitbucket Cloud API token scopes follow the format `::bitbucket`. The CLI uses these scopes: | Scope | Grants | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `read:user:bitbucket` | Read user profiles; verifies any login, resolves your UUID for `bb pr list --author/--reviewer/--mine`, `bb pr status` and the `bb ssh-key` / `bb gpg-key` commands, and user lookups | | `read:repository:bitbucket` | List and view repositories, their files, downloads, commits, and commit build statuses; search code | | `write:repository:bitbucket` | Upload and delete downloads | | `admin:repository:bitbucket` | Create repositories; add and remove default reviewers; list, view, create, and delete branch restrictions | | `delete:repository:bitbucket` | Delete repositories | | `read:pullrequest:bitbucket` | List, view, and diff pull requests; read comments, activity, checks, reviewers, and default reviewers | | `write:pullrequest:bitbucket` | Create, edit, approve, unapprove, request changes, decline, merge, and mark pull requests ready; add/remove reviewers; add, edit, and delete comments | | `read:pipeline:bitbucket` | List and view pipelines and their steps, read step logs; list and view deployments and environments | | `write:pipeline:bitbucket` | Trigger and stop pipelines | | `read:workspace:bitbucket` | List and view workspaces | | `read:project:bitbucket` | List and view projects | | `admin:project:bitbucket` | Create projects | | `read:snippet:bitbucket` | List and view snippets, read snippet comments | | `write:snippet:bitbucket` | Create and edit snippets; watch and unwatch; add, edit, and delete snippet comments | | `delete:snippet:bitbucket` | Delete snippets | | `read:webhook:bitbucket` | List and view webhooks | | `write:webhook:bitbucket` | Create webhooks | | `delete:webhook:bitbucket` | Delete webhooks | | `read:ssh-key:bitbucket` | List SSH keys on your account | | `write:ssh-key:bitbucket` | Add SSH keys | | `delete:ssh-key:bitbucket` | Delete SSH keys | | `read:gpg-key:bitbucket` | List GPG keys on your account | | `write:gpg-key:bitbucket` | Add GPG keys | | `delete:gpg-key:bitbucket` | Delete GPG keys | ## Command → scope map ### Auth (`bb auth …`) | Command | Required scopes | | ---------------- | ------------------------------------------------------------------------- | | `bb auth login` | `read:user:bitbucket` (verifies the credentials) | | `bb auth logout` | *(none; local-only for API tokens; OAuth revoke uses the existing token)* | | `bb auth status` | `read:user:bitbucket` | | `bb auth token` | *(none; prints the locally-stored token)* | ### Repositories (`bb repo …`) | Command | Required scopes | | ---------------------------------- | --------------------------------------------------- | | `bb repo list` | `read:repository:bitbucket` | | `bb repo view` | `read:repository:bitbucket` | | `bb repo create` | `admin:repository:bitbucket` | | `bb repo delete` | `delete:repository:bitbucket` | | `bb repo default-reviewers list` | `read:pullrequest:bitbucket` | | `bb repo default-reviewers add` | `admin:repository:bitbucket`, `read:user:bitbucket` | | `bb repo default-reviewers remove` | `admin:repository:bitbucket`, `read:user:bitbucket` | | `bb repo cat` | `read:repository:bitbucket` | | `bb repo ls` | `read:repository:bitbucket` | | `bb repo downloads list` | `read:repository:bitbucket` | | `bb repo downloads upload` | `write:repository:bitbucket` | | `bb repo downloads delete` | `write:repository:bitbucket` | `admin:` and `delete:` are separate scopes. A token with `admin:repository:bitbucket` can create repositories but cannot delete them. Default reviewers live under the pull request resource in Bitbucket’s API, which is why reading them needs a pull request scope while changing them needs the repository admin scope. `add` and `remove` also resolve the target account via `GET /users/{selected_user}` first; add `read:user:bitbucket` for that lookup. See [Get a user](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-users/#api-users-selected-user-get). ### Pull requests (`bb pr …`) `read:pullrequest:bitbucket` covers `list`, `status`, `view`, `diff`, `checkout`, `activity`, `checks`, `comments list`, `comments view` and `reviewers list`. `write:pullrequest:bitbucket` covers PR mutations (`create`, `edit`, `ready`, `approve`, `unapprove`, `request-changes`, `decline`, `merge`, and reviewer changes). `ready` and reviewer changes also fetch the PR first, so grant `read:pullrequest:bitbucket` for those workflows. `edit` without a PR ID lists open PRs to find the current branch, and `create --default-reviewers` reads the effective defaults; those paths also need the pull request read scope. Atlassian currently documents `read:pullrequest:bitbucket` for comment mutations, including add, edit, reply, resolve, unresolve and delete. A scope named `read` is therefore not a guarantee that a token cannot change comments. Check the [endpoint reference](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/) and account permissions before treating a token as read-only. `bb pr status` and `bb pr list --mine` (or `--author`/`--reviewer`) also need `read:user:bitbucket`; they call `GET /user` (or `GET /users/{selected_user}`) to resolve the UUID before filtering. So do the commands that take a `` argument: `bb pr create --reviewer`, `bb pr reviewers add|remove`, and `bb repo default-reviewers add|remove` all resolve an account ID or `{uuid}` through `GET /users/{selected_user}` (and `@me` through `GET /user`) before making the change. Add `read:user:bitbucket` to any token that runs those. `bb pr create --reviewer`, `bb pr reviewers add|remove` and `bb pr list --author|--reviewer` also accept a nickname, display name or email, which they look up through `GET /workspaces/{workspace}/members`. That needs `read:workspace:bitbucket`. `bb pr checkout` runs `git fetch` and `git checkout` locally after reading the pull request, so it uses your normal git credentials too. No `bb pr` command needs `read:repository:bitbucket`. The CLI resolves the workspace and repository from your git remote (or `--workspace`/`--repo`), not from the API. ### Pipelines (`bb pipeline …`) | Command | Required scopes | | ------------------ | ----------------------------------------------------- | | `bb pipeline list` | `read:pipeline:bitbucket` | | `bb pipeline view` | `read:pipeline:bitbucket` | | `bb pipeline logs` | `read:pipeline:bitbucket` | | `bb pipeline run` | `read:pipeline:bitbucket`, `write:pipeline:bitbucket` | | `bb pipeline stop` | `write:pipeline:bitbucket` | ### Commits and build statuses (`bb commit …`, `bb status …`) | Command | Required scopes | | ---------------- | ------------------------------------------------------------------- | | `bb commit list` | `read:repository:bitbucket` | | `bb commit view` | `read:repository:bitbucket` | | `bb status list` | `read:repository:bitbucket` | | `bb status set` | `read:repository:bitbucket` (as documented by Atlassian; see below) | Atlassian currently lists `read:repository:bitbucket` for [creating and updating build statuses](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commit-statuses/). As with PR comments, a scope named `read` is not a guarantee against mutations. These tables describe the published API contract; server enforcement has not been tested with live tokens. ### Workspaces (`bb workspace …`) | Command | Required scopes | | ------------------- | -------------------------- | | `bb workspace list` | `read:workspace:bitbucket` | | `bb workspace view` | `read:workspace:bitbucket` | `bb workspace list` calls `GET /user/workspaces`; the entries wrap each workspace in a `workspace` object. Both that endpoint and `GET /workspaces/{workspace}` require the workspace read scope in the [current API reference](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-workspaces/). The deprecated `GET /workspaces` endpoint is not used by the CLI. ### Projects (`bb project …`) | Command | Required scopes | | ------------------- | ------------------------- | | `bb project list` | `read:project:bitbucket` | | `bb project view` | `read:project:bitbucket` | | `bb project create` | `admin:project:bitbucket` | ### Branch restrictions (`bb branch-restriction …`) | Command | Required scopes | | ------------------------------ | ---------------------------- | | `bb branch-restriction list` | `admin:repository:bitbucket` | | `bb branch-restriction view` | `admin:repository:bitbucket` | | `bb branch-restriction create` | `admin:repository:bitbucket` | | `bb branch-restriction delete` | `admin:repository:bitbucket` | Even reading branch restrictions needs the admin scope. `create --user` also resolves each user through `GET /users/{user}`, which needs `read:user:bitbucket`. ### Deployments (`bb deployment …`) | Command | Required scopes | | ---------------------------- | ------------------------- | | `bb deployment list` | `read:pipeline:bitbucket` | | `bb deployment view` | `read:pipeline:bitbucket` | | `bb deployment environments` | `read:pipeline:bitbucket` | ### SSH and GPG keys (`bb ssh-key …`, `bb gpg-key …`) | Command | Required scopes | | ------------------- | -------------------------------------------------------------------------- | | `bb ssh-key list` | `read:ssh-key:bitbucket`, `read:user:bitbucket` | | `bb ssh-key add` | `read:ssh-key:bitbucket`, `write:ssh-key:bitbucket`, `read:user:bitbucket` | | `bb ssh-key delete` | `delete:ssh-key:bitbucket`, `read:user:bitbucket` | | `bb gpg-key list` | `read:gpg-key:bitbucket`, `read:user:bitbucket` | | `bb gpg-key add` | `read:gpg-key:bitbucket`, `write:gpg-key:bitbucket`, `read:user:bitbucket` | | `bb gpg-key delete` | `delete:gpg-key:bitbucket`, `read:user:bitbucket` | Every key command calls `GET /user` first to resolve your account, hence `read:user:bitbucket`. Bitbucket’s spec lists both the read and write scope for adding a key. ### Snippets (`bb snippet …`) | Command | Required scopes | | ---------------------------- | --------------------------------------------------- | | `bb snippet list` | `read:snippet:bitbucket` | | `bb snippet view` | `read:snippet:bitbucket` | | `bb snippet create` | `read:snippet:bitbucket`, `write:snippet:bitbucket` | | `bb snippet edit` | `read:snippet:bitbucket`, `write:snippet:bitbucket` | | `bb snippet delete` | `delete:snippet:bitbucket` | | `bb snippet watch` | `write:snippet:bitbucket` | | `bb snippet unwatch` | `write:snippet:bitbucket` | | `bb snippet comments list` | `read:snippet:bitbucket` | | `bb snippet comments add` | `read:snippet:bitbucket`, `write:snippet:bitbucket` | | `bb snippet comments edit` | `read:snippet:bitbucket`, `write:snippet:bitbucket` | | `bb snippet comments delete` | `write:snippet:bitbucket` | `delete:snippet:bitbucket` is separate from the write scope. The [snippet endpoint reference](https://developer.atlassian.com/cloud/bitbucket/rest/api-group-snippets/) requires both snippet read and write scopes for adding and editing comments, and write for deleting them. ### Search (`bb search …`) | Command | Required scopes | | ---------------- | --------------------------- | | `bb search code` | `read:repository:bitbucket` | A token with the right scope still gets a `404` when code search is not enabled for the workspace. See [Search Commands](/commands/search/). ### Webhooks (`bb webhook …`) | Command | Required scopes | | ------------------- | --------------------------------------------------- | | `bb webhook list` | `read:webhook:bitbucket` | | `bb webhook view` | `read:webhook:bitbucket` | | `bb webhook create` | `read:webhook:bitbucket`, `write:webhook:bitbucket` | | `bb webhook delete` | `delete:webhook:bitbucket` | Bitbucket also requires the scope that applies to each event a new webhook subscribes to, for example `read:repository:bitbucket` for `repo:push` and `read:pullrequest:bitbucket` for `pullrequest:created`. Only workspace owners can create `--scope workspace` webhooks, whatever the token grants. ### Raw API (`bb api`) `bb api` needs whatever scope the endpoint you call requires; look the endpoint up in Atlassian’s Bitbucket Cloud API reference. A `403` can indicate missing scopes, insufficient account permissions, or a repository restriction; inspect the response instead of assuming a scope problem. ### Local-only commands These don’t hit the API and don’t need any scope: * `bb repo clone` (builds the clone URL from workspace and repository names, then shells out to git; uses your normal git credentials) * `bb browse` (builds the URL from local git context) * `bb config` (all subcommands) * `bb completion` * `bb` (root, including the version-check) ## Common profiles ### Read-only automation (status checks, dashboards) ```text read:user:bitbucket read:repository:bitbucket read:pullrequest:bitbucket read:pipeline:bitbucket ``` ### Bot account that creates and merges pull requests ```text read:user:bitbucket read:pullrequest:bitbucket write:pullrequest:bitbucket ``` Add `read:repository:bitbucket` only if the bot also runs `bb repo`, `bb commit` or `bb status` commands. ### Repo provisioning automation ```text read:user:bitbucket read:repository:bitbucket admin:repository:bitbucket delete:repository:bitbucket ``` Drop `delete:repository:bitbucket` unless the automation actually tears repositories down. ### CI/CD automation (trigger pipelines, report build statuses) ```text read:user:bitbucket read:repository:bitbucket write:repository:bitbucket read:pipeline:bitbucket write:pipeline:bitbucket ``` ### CI monitor bot ```text read:user:bitbucket read:repository:bitbucket read:pipeline:bitbucket ``` ## Troubleshooting If a command exits with [`2003` API\_FORBIDDEN](/reference/error-codes/#2003---api_forbidden), check the scopes above, account permissions, and repository restrictions. You can’t add scopes to an existing token; mint a new one and re-authenticate: ```bash bb auth logout printf '%s' "$NEW_TOKEN" | bb auth login -u you@example.com --with-token ``` `--with-token` reads the token from stdin, keeping it out of your shell history and out of `ps` output. `-p new-token` works too, but writes the secret into both.