Skip to content

Scripting & Automation - Bitbucket CLI for CI/CD Workflows

The Bitbucket CLI is designed for both interactive use and automation. This guide covers JSON output, exit codes, and scripting patterns.

All commands support the --json flag for machine-readable output:

Terminal window
bb pr list --json
bb repo list --json
bb pr view 42 --json

For list-style commands, results are limited by default. Pass --limit when your automation needs more rows.

jq is the standard tool for processing JSON:

Terminal window
# List all PR titles
bb pr list --json | jq '.pullRequests[].title'
# Get PR count
bb pr list --json | jq '.count'
# Filter by state
bb pr list -s MERGED --json | jq '.count'
# Get specific fields
bb pr list --json | jq '.pullRequests[] | {id, title, author: .author.display_name}'
# Filter by author
bb pr list --json | jq '.pullRequests[] | select((.author.nickname // .author.display_name) == "alice")'
# Get PR URLs
bb pr list --json | jq -r '.pullRequests[].links.html.href'
# Get web diff URL for a PR
bb pr diff 42 --web --json | jq -r '.url'
Terminal window
# PRs updated in the last 7 days
bb pr list --json | jq --arg date "$(date -d '7 days ago' -Iseconds 2>/dev/null || \
date -v-7d -Iseconds)" \
'.pullRequests[] | select(.updated_on > $date)'
# PRs targeting main branch
bb pr list --json | jq '.pullRequests[] | select(.destination.branch.name == "main")'
# Group PRs by author
bb pr list --json | jq '.pullRequests | group_by(.author.nickname // .author.display_name) | map({author: (.[0].author.nickname // .[0].author.display_name), count: length})'

The CLI uses standard exit codes:

CodeMeaning
0Success
1Error (authentication, API, validation, etc.)
#!/bin/bash
if bb pr list -w myworkspace -r myrepo > /dev/null 2>&1; then
echo "Success"
else
echo "Failed with exit code: $?"
fi
#!/bin/bash
set -e # Exit on any error
# Or handle errors explicitly
bb pr list --json > prs.json || {
echo "Failed to list PRs"
exit 1
}
Terminal window
if ! bb config get invalidKey --json > out.json 2> err.json; then
jq '.' err.json
exit 1
fi

For non-interactive scripts, use environment variables:

Terminal window
export BB_USERNAME=myuser
export BB_API_TOKEN=ATBB_token
bb auth login # Uses env vars
bb pr list -w workspace -r repo

See Environment Variables Reference for details.


The CLI handles common transient failures automatically — you don’t need to implement this yourself in most cases.

API requests that fail with transient errors (HTTP 429, 502, 503, 504) are retried automatically up to 3 times with exponential backoff. This means rate-limited or temporarily unavailable responses are handled without any extra scripting.

OAuth access tokens expire after 2 hours. The CLI refreshes them automatically — both proactively (before expiry) and reactively (on 401 responses). Long-running scripts using OAuth don’t need manual re-authentication.


Don’t rely on context detection in scripts:

Terminal window
# Good - explicit and reliable
bb pr list -w myworkspace -r myrepo --json
# Bad - may fail if context can't be detected
bb pr list --json

The CLI retries transient errors automatically (see Built-in Resilience above). For persistent failures, handle the exit code:

#!/bin/bash
if ! bb pr list -w workspace -r repo --json > prs.json 2>error.json; then
echo "Failed to list PRs"
cat error.json
exit 1
fi

When making many sequential API calls, add short delays to avoid hitting rate limits:

#!/bin/bash
for pr_id in $(bb pr list --json | jq -r '.pullRequests[].id'); do
bb pr view "$pr_id" --json
sleep 1
done

Approve all open PRs from a specific author:

#!/bin/bash
set -e
WORKSPACE="myworkspace"
REPO="myrepo"
AUTHOR="trusted-bot"
echo "Finding PRs from $AUTHOR..."
pr_ids=$(bb pr list -w "$WORKSPACE" -r "$REPO" --json | \
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!"

Generate a markdown report of open PRs:

#!/bin/bash
WORKSPACE="myworkspace"
REPO="myrepo"
echo "# Open Pull Requests"
echo ""
echo "Generated: $(date)"
echo ""
bb pr list -w "$WORKSPACE" -r "$REPO" --json | 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"'

Decline PRs not updated in 30 days:

#!/bin/bash
set -e
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 | \
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