JSON Output
All commands support the global --json flag, with optional field selection
and a built-in --jq filter modeled on the gh CLI.
Use JSON mode for automation, scripts, and CI/CD pipelines.
Quick Usage
Section titled “Quick Usage”# Full JSON outputbb pr list --json
# Project to a comma-separated field listbb 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 JSONbb repo view --json --no-colorField Selection (--json fields)
Section titled “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:
bb pr list --json id,title,statebb pr list --json id,title,author.display_nameRules:
- 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_namebecomes the literal key"author.display_name"). - Dotted paths traverse nested objects; missing intermediates yield
null. - For collection commands (
pr list,repo list, etc.), the wrapper envelope is dropped and the field list is projected per-item — the result is a flat array. This matchesghCLI semantics.
Example: bb pr list --json id,title,author.display_name
Section titled “Example: bb pr list --json id,title,author.display_name”[ { "id": 42, "title": "Add feature", "author.display_name": "Alice" }, { "id": 41, "title": "Fix bug", "author.display_name": "Bob" }]Before / after: shape change after projection
Section titled “Before / after: shape change after projection”bb pr list --json returns the full envelope with metadata and a named array:
{ "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "count": 2, "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:
[ { "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.
Built-in jq (--jq)
Section titled “Built-in jq (--jq)”The --jq <expression> flag pipes the JSON output through an embedded jq
engine (jq-wasm) — no external jq
binary required, no bash pipe to fail on Windows.
# Strings, one per linebb pr list --json --jq '.pullRequests[].title'
# Combine with field selection (jq runs after projection)bb pr list --json id,title,state --jq '.[] | select(.state == "OPEN") | .title'
# Aggregatebb pr list --json --jq '.count'
# Project + format with jq string interpolationbb pr list --json id,title,author.display_name \ --jq '.[] | "\(.id)\t\(.["author.display_name"])\t\(.title)"'
# Project a nested URL out of a single resourcebb pr view 42 --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)'Rules:
--jqrequires--json. Using--jqalone exits non-zero with a clear error (✗ --jq requires --json).- jq runs after field projection, so
.refers to the projected shape — a flat array when both--json fieldsand a wrapper-style command are involved. - Invalid jq expressions exit non-zero with the underlying jq compiler error (error code 8001).
Output Patterns
Section titled “Output Patterns”The CLI uses a small number of consistent output patterns. Understanding these patterns lets you predict the JSON shape of any command.
Pattern 1: Collection (List Commands)
Section titled “Pattern 1: Collection (List Commands)”Used by: pr list, repo list, pr comments list, pr activity, pr reviewers list, pr checks
Returns an envelope with metadata and a named array:
{ "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": {}, "count": 2, "pullRequests": [...]}Common envelope fields:
| Field | Type | Present in |
|---|---|---|
workspace | string | PR and repo collections |
repoSlug | string | PR collections |
count | number | All collections |
state | string | pr list |
filters | object | pr list (when --mine is used), pr activity |
Collection key names:
| Command | Array key |
|---|---|
pr list | pullRequests |
repo list | repositories |
pr comments list | comments |
pr activity | activities |
pr reviewers list | reviewers |
pr checks | statuses (inside a summary + statuses structure) |
Example: bb pr list --json
Section titled “Example: bb pr list --json”{ "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "count": 2, "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
Section titled “Example: bb pr list --mine --json”{ "workspace": "myworkspace", "repoSlug": "myrepo", "state": "OPEN", "filters": { "mine": true }, "count": 1, "pullRequests": [...]}Example: bb pr checks 42 --json
Section titled “Example: bb pr checks 42 --json”{ "pullRequestId": 42, "workspace": "myworkspace", "repoSlug": "myrepo", "summary": { "successful": 2, "failed": 0, "pending": 1 }, "statuses": [...]}Pattern 2: Resource (View/Create Commands)
Section titled “Pattern 2: Resource (View/Create Commands)”Used by: pr view, repo view, pr create, pr edit
Returns the raw Bitbucket API resource object directly, with no envelope:
{ "type": "pullrequest", "id": 42, "title": "Add feature", "state": "OPEN", "author": {...}, "source": {...}, "destination": {...}, "participants": [...], "links": {...}}Pattern 3: Action (Approve/Merge/Decline Commands)
Section titled “Pattern 3: Action (Approve/Merge/Decline Commands)”Used by: pr approve, pr decline, pr merge, pr ready, pr comments add/edit/delete, pr reviewers add/remove, auth login, auth logout
Returns a success indicator plus resource identifiers:
{ "success": true, "pullRequestId": 42}Some action commands include the full resource in the response:
{ "success": true, "pullRequestId": 42, "pullRequest": {...}}Pattern 4: Mode-Based (Diff Command)
Section titled “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)
Section titled “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.
{ "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:
bb pr diff 42 --json --jq '.diff' > pr-42.patchbb pr diff 42 --stat --json
Section titled “bb pr diff 42 --stat --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
Section titled “bb pr diff 42 --name-only --json”{ "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "name-only", "files": ["src/index.ts", "src/utils.ts"]}bb pr diff 42 --web --json
Section titled “bb pr diff 42 --web --json”{ "workspace": "myworkspace", "repoSlug": "myrepo", "pullRequestId": 42, "mode": "web", "url": "https://bitbucket.org/myworkspace/myrepo/pull-requests/42/diff"}Pattern 5: Auth & Config (Custom)
Section titled “Pattern 5: Auth & Config (Custom)”These commands have unique output shapes:
bb auth status --json
Section titled “bb auth status --json”{ "authenticated": true, "user": { "username": "myuser", "displayName": "My Name", "accountId": "70121:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }}bb auth token --json
Section titled “bb auth token --json”{ "token": "base64-encoded-username:apiToken"}bb config list --json
Section titled “bb config list --json”{ "configPath": "/Users/you/.config/bb/config.json", "config": { "username": "myuser", "apiToken": "********", "defaultWorkspace": "myworkspace", "skipVersionCheck": false }}bb config get <key> --json
Section titled “bb config get <key> --json”{ "key": "defaultWorkspace", "value": "myworkspace"}jq Examples
Section titled “jq Examples”The same expressions work with the built-in --jq flag or with an external
jq binary. The built-in form avoids the dependency and works identically on
Windows, macOS, and Linux.
# Print PR titles (built-in)bb pr list --json --jq '.pullRequests[].title'
# Print PR titles (external jq, equivalent)bb pr list --json | jq -r '.pullRequests[].title'
# Count PRsbb pr list --json --jq '.count'
# Repository namesbb repo list --json --jq '.repositories[].full_name'
# Diff file pathsbb pr diff 42 --stat --json --jq '.files[].path'
# PR URLsbb pr list --json --jq '.pullRequests[].links.html.href'
# Auth statusbb auth status --json --jq '.authenticated'Scripting Notes
Section titled “Scripting Notes”- Prefer
--jsonin scripts rather than parsing text output. - Do not rely on text formatting symbols (
✓, table separators, colors). - Prefer the built-in
--jqand--json fieldsover external pipes — they avoid an extra binary dependency and behave identically across platforms. - If you need stable fields, prefer command-specific documented keys (
pullRequests,repositories,files,success). pr listreturns lightweight PR objects. Usepr view <id>for full details includingparticipantsandreviewers.
Errors
Section titled “Errors”When a command fails, the CLI exits non-zero.
- In normal mode, errors are plain text on stderr.
- In
--jsonmode, errors are compact JSON objects on stderr.
Example:
bb config get invalidKey --json 2>error.jsoncat error.json# {"name":"BBError","code":4003,"message":"Unknown config key 'invalidKey'. Valid keys: username, defaultWorkspace, skipVersionCheck, versionCheckInterval","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 |
message | string | Human-readable message |
context | object | Optional structured metadata. Shape depends on code — see the table below |
The Bitbucket API response body is not nested inside the error JSON.
APIError keeps the upstream HTTP status and response body as instance
properties for the in-process error path, but only name, code, message,
and context are serialized to stderr. Likewise, cause (the underlying
Error) and stack traces are never exposed in --json mode.
context fields by error code
Section titled “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_* | (none — the upstream HTTP status code is in APIError.statusCode in-process but is not serialized) |
3001 GIT_NOT_REPOSITORY | (none) |
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) |
5002 VALIDATION_INVALID | varies — usually { key, value }, the offending option name, or the rejected ID |
6001 CONTEXT_REPO_NOT_FOUND | (none) |
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) |
9999 UNKNOWN | (none) |
In automation, always check the process exit code before parsing stdout JSON.