Skip to content

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.

Terminal window
# 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

Pass a comma-separated field list to project the output to just those fields, matching the way gh CLI handles --json:

Terminal window
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.
  • 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 matches gh CLI 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.

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.

Terminal window
# Strings, one per line
bb 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'
# Aggregate
bb pr list --json --jq '.count'
# Project + format with jq string interpolation
bb pr list --json id,title,author.display_name \
--jq '.[] | "\(.id)\t\(.["author.display_name"])\t\(.title)"'
# Project a nested URL out of a single resource
bb 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:

  • --jq requires --json. Using --jq alone 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 fields and a wrapper-style command are involved.
  • Invalid jq expressions exit non-zero with the underlying jq compiler error (error code 8001).

The CLI uses a small number of consistent output patterns. Understanding these patterns lets you predict the JSON shape of any command.

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:

FieldTypePresent in
workspacestringPR and repo collections
repoSlugstringPR collections
countnumberAll collections
statestringpr list
filtersobjectpr list (when --mine is used), pr activity

Collection key names:

CommandArray key
pr listpullRequests
repo listrepositories
pr comments listcomments
pr activityactivities
pr reviewers listreviewers
pr checksstatuses (inside a summary + statuses structure)
{
"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" } }
}
]
}
{
"workspace": "myworkspace",
"repoSlug": "myrepo",
"state": "OPEN",
"filters": { "mine": true },
"count": 1,
"pullRequests": [...]
}
{
"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": {...}
}

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.

ModeTriggerMode-specific fields
diff(default)diff (string — the raw unified diff text)
stat--statfilesChanged, totalAdditions, totalDeletions, files[]
name-only--name-onlyfiles[] (string array of paths)
web--weburl

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:

Terminal window
bb pr diff 42 --json --jq '.diff' > pr-42.patch
{
"workspace": "myworkspace",
"repoSlug": "myrepo",
"pullRequestId": 42,
"mode": "stat",
"filesChanged": 3,
"totalAdditions": 27,
"totalDeletions": 11,
"files": [
{ "path": "src/index.ts", "additions": 10, "deletions": 3 }
]
}
{
"workspace": "myworkspace",
"repoSlug": "myrepo",
"pullRequestId": 42,
"mode": "name-only",
"files": ["src/index.ts", "src/utils.ts"]
}
{
"workspace": "myworkspace",
"repoSlug": "myrepo",
"pullRequestId": 42,
"mode": "web",
"url": "https://bitbucket.org/myworkspace/myrepo/pull-requests/42/diff"
}

These commands have unique output shapes:

{
"authenticated": true,
"user": {
"username": "myuser",
"displayName": "My Name",
"accountId": "70121:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
{
"token": "base64-encoded-username:apiToken"
}
{
"configPath": "/Users/you/.config/bb/config.json",
"config": {
"username": "myuser",
"apiToken": "********",
"defaultWorkspace": "myworkspace",
"skipVersionCheck": false
}
}
{
"key": "defaultWorkspace",
"value": "myworkspace"
}

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.

Terminal window
# 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 PRs
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'
# PR URLs
bb pr list --json --jq '.pullRequests[].links.html.href'
# Auth status
bb auth status --json --jq '.authenticated'
  • Prefer --json in scripts rather than parsing text output.
  • 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.
  • If you need stable fields, prefer command-specific documented keys (pullRequests, repositories, files, success).
  • pr list returns lightweight PR objects. Use pr view <id> for full details including participants and reviewers.

When a command fails, the CLI exits non-zero.

  • In normal mode, errors are plain text on stderr.
  • In --json mode, errors are compact JSON objects on stderr.

Example:

Terminal window
bb config get invalidKey --json 2>error.json
cat error.json
# {"name":"BBError","code":4003,"message":"Unknown config key 'invalidKey'. Valid keys: username, defaultWorkspace, skipVersionCheck, versionCheckInterval","context":{"key":"invalidKey"}}

Error JSON fields:

FieldTypeDescription
namestringError class name (usually BBError; can also be APIError, AuthError, GitError, ValidationError)
codenumberNumeric error code
messagestringHuman-readable message
contextobjectOptional 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.

The context object is omitted when there’s nothing useful to attach. When present, the shape is:

CodeTypical context keys
1001 AUTH_REQUIRED(none)
1002 AUTH_INVALIDstatus (HTTP status, OAuth verification failures only)
1003 AUTH_EXPIREDstatus (HTTP status from the token endpoint)
20012005 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_FAILEDcommand, exitCode
3003 GIT_REMOTE_NOT_FOUNDremote
4001 CONFIG_READ_FAILED(none)
4002 CONFIG_WRITE_FAILED(none)
4003 CONFIG_INVALID_KEYkey
5001 VALIDATION_REQUIREDfield (when thrown by ValidationError)
5002 VALIDATION_INVALIDvaries — 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_FAILEDexpression, optional exitCode
8002 JSON_FORMAT_INVALID(none)
9999 UNKNOWN(none)

In automation, always check the process exit code before parsing stdout JSON.