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, pipeline list, commit list, status list, issue list, workspace list, project list, snippet list
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 | Repository- and workspace-scoped collections (PR, repo, pipeline, commit, status, issue, project) |
repoSlug | string | Repository-scoped collections (PR, pipeline, commit, status, issue) |
count | number | All collections |
state | string | pr list |
filters | object | pr list (when --mine is used), pr activity, issue list, workspace list |
commit | string | status list |
sort | string | pipeline list |
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) |
pipeline list | pipelines |
commit list | commits |
status list | statuses |
issue list | issues |
workspace list | workspaces |
project list | projects |
snippet list | snippets |
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.