The real problem
Why this is harder than it looks
The Bitbucket REST API is straightforward and the OAuth 2.0 flow follows standard patterns. You can build a working prototype against your own workspace in a couple of hours. The complexity arrives when you try to do this for real users in a multi-tenant product.
Bitbucket's OAuth scopes are coarse-grained and workspace-scoped. A token granted repository:write can write to every repository the user has access to across any workspace. Getting the scope selection wrong — requesting more than needed — raises trust friction on the consent screen and creates a broader attack surface if a token is compromised. Bitbucket also uses a workspace-slug-based routing model: every API call goes to api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}, so your infrastructure must correctly associate each connected user with their workspace slug and route calls accordingly. Mixing up workspace context across users in a multi-tenant system doesn't produce helpful errors — it either returns 403s or, worse, operates on the wrong user's data.
Token lifecycle is another operational burden. Bitbucket OAuth issues both access tokens and refresh tokens. Access tokens are short-lived; if your refresh logic fires after a token has already expired rather than proactively before expiry, your agent surfaces errors to users at the worst possible time. Bitbucket also supports OAuth consumers registered at the workspace level — meaning each of your customers may have different consumers with different callback URL requirements, scope grants, and secret rotation schedules.
Scalekit handles OAuth consumer registration, token refresh, revocation detection, and per-user workspace routing. Your agent names a tool and passes parameters. The plumbing is not your problem.
Capabilities
What your agent can do with Bitbucket
Once connected, your agent has 130+ pre-built tools covering the full Bitbucket API surface:
- Manage repositories end-to-end: create, update, fork, and delete repositories; list repos across workspaces; read file contents and metadata
- Work with branches, tags, and commits: create and delete branches; list commits with filtering; approve commits; read diffs and diffstats between any two refs
- Automate pull requests: create, update, approve, merge, and decline PRs; post comments and tasks; request or remove changes; list all PR activity
- Control CI/CD pipelines: trigger pipeline runs on any branch or commit; stop running pipelines; read step logs; manage schedules and pipeline variables
- Manage deployment environments: create and configure Test, Staging, and Production environments; manage deployment variables with secret masking
- Govern workspace access: manage branch restrictions, deploy keys, default reviewers, and per-user or per-group repository permissions
Setup context
What we're building
This guide connects a developer assistant agent to Bitbucket — helping engineers create branches, open PRs, trigger pipelines, and monitor deployments without leaving your product.
🤖
Example agent
Developer assistant creating branches, managing pull requests, triggering pipelines, and reading CI logs on behalf of each engineer
🔐
Auth model
B2B SaaS — each engineer connects their own Bitbucket workspace. identifier = your user ID
🔑
Bitbucket OAuth consumer
Register an OAuth consumer in your Bitbucket workspace settings and supply the Key and Secret in the Scalekit dashboard
Setup
1 Setup: One SDK, One credential
Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Bitbucket secrets, no user tokens, nothing belonging to your users.
pip install scalekit-sdk-python
npm install @scalekit-sdk/node
import scalekit.client
import os
from dotenv import load_dotenv
load_dotenv()
scalekit = scalekit.client.ScalekitClient(
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
env_url=os.getenv("SCALEKIT_ENV_URL"),
)
actions = scalekit.actions
import { ScalekitClient } from '@scalekit-sdk/node';
import 'dotenv/config';
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENV_URL,
process.env.SCALEKIT_CLIENT_ID,
process.env.SCALEKIT_CLIENT_SECRET
);
const actions = scalekit.actions;
Already have credentials?
Connected Accounts
2 Per-User Auth: Creating connected accounts
Each engineer gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.
response = actions.get_or_create_connected_account(
connection_name="bitbucket",
identifier="user_bb_789" # your internal user ID
)
connected_account = response.connected_account
print(f"Status: {connected_account.status}")
# Status: PENDING — user hasn't authorized yet
const response = await actions.getOrCreateConnectedAccount({
connectionName: "bitbucket",
identifier: "user_bb_789" // your internal user ID
});
const connectedAccount = response.connectedAccount;
console.log(`Status: ${connectedAccount.status}`);
// Status: PENDING — user hasn't authorized yet
This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.
Authorization Flow
3 The authorization flow
The user authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.
if connected_account.status != "ACTIVE":
link = actions.get_authorization_link(
connection_name="bitbucket",
identifier="user_bb_789"
)
# Redirect user → Bitbucket's native OAuth consent screen
# Scalekit captures the token on callback
return redirect(link.link)
if (connectedAccount.status !== "ACTIVE") {
const { link } = await actions.getAuthorizationLink({
connectionName: "bitbucket",
identifier: "user_bb_789"
});
// Redirect user → Bitbucket's native OAuth consent screen
// Scalekit captures the token on callback
return redirect(link);
}
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access tokens refresh before expiry. If a token is revoked — by the user or via workspace admin action — the account moves to REVOKED. No silent failures. Check account.status before critical operations.
Bring Your Own Credentials — required for production
Bitbucket requires you to register your own OAuth consumer in your workspace settings and supply your Key and Secret. Register the consumer in Bitbucket, copy the Scalekit redirect URI into the consumer's Callback URL, then paste your Key and Secret into the Scalekit dashboard under your Bitbucket connection. Token management stays fully handled.
Calling Bitbucket
4 Calling Bitbucket: What your agent writes
With the connected account active, your agent calls Bitbucket actions using actions.execute_tool(). Name the tool, pass parameters. Scalekit handles token retrieval and request construction.
List repositories in a workspace
Returns all repositories the connected user can access in a workspace. Use q to filter by name pattern and sort to order results.
result = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_repositories_list",
tool_input={
"workspace": "my-team-workspace",
"sort": "-updated_on"
}
)
# Returns: list of repos with name, slug, description, language, size, clone URLs
const result = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_repositories_list",
toolInput: {
"workspace": "my-team-workspace",
"sort": "-updated_on"
}
});
// Returns: list of repos with name, slug, description, language, size, clone URLs
Create a branch and open a pull request
Create a branch from a commit hash, then open a PR against the destination branch. Both operations are common starting points for automated code workflows.
# 1. Create a feature branch
branch = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_branch_create",
tool_input={
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"name": "feature/add-payment-webhook",
"target_hash": "a3f9c21d"
}
)
# 2. Open a pull request
pr = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_pull_request_create",
tool_input={
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"title": "Add payment webhook handler",
"source_branch": "feature/add-payment-webhook",
"destination_branch": "main",
"description": "Implements the Stripe webhook endpoint for payment events.",
"close_source_branch": True
}
)
# Returns: { "id": 42, "title": "...", "state": "OPEN", "links": {...} }
// 1. Create a feature branch
const branch = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_branch_create",
toolInput: {
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"name": "feature/add-payment-webhook",
"target_hash": "a3f9c21d"
}
});
// 2. Open a pull request
const pr = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_pull_request_create",
toolInput: {
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"title": "Add payment webhook handler",
"source_branch": "feature/add-payment-webhook",
"destination_branch": "main",
"description": "Implements the Stripe webhook endpoint for payment events.",
"close_source_branch": true
}
});
// Returns: { "id": 42, "title": "...", "state": "OPEN", "links": {...} }
Trigger a pipeline and read step logs
Trigger a pipeline run on a specific branch or commit, then fetch the step log to surface CI output directly in your agent's response.
# Trigger the pipeline
pipeline = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_pipeline_trigger",
tool_input={
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"branch": "main",
"variables": [{"key": "DEPLOY_ENV", "value": "staging"}]
}
)
pipeline_uuid = pipeline["uuid"]
# Read the first step's log
steps = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_pipeline_steps_list",
tool_input={
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"pipeline_uuid": pipeline_uuid
}
)
step_uuid = steps["values"][0]["uuid"]
log = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_pipeline_step_log_get",
tool_input={
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"pipeline_uuid": pipeline_uuid,
"step_uuid": step_uuid
}
)
# Returns: raw log output for the step
// Trigger the pipeline
const pipeline = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_pipeline_trigger",
toolInput: {
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"branch": "main",
"variables": [{"key": "DEPLOY_ENV", "value": "staging"}]
}
});
const pipelineUuid = pipeline.uuid;
// Read the first step's log
const steps = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_pipeline_steps_list",
toolInput: {
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"pipeline_uuid": pipelineUuid
}
});
const stepUuid = steps.values[0].uuid;
const log = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_pipeline_step_log_get",
toolInput: {
"workspace": "my-team-workspace",
"repo_slug": "backend-api",
"pipeline_uuid": pipelineUuid,
"step_uuid": stepUuid
}
});
// Returns: raw log output for the step
Search code across a workspace
Run a code search across all repositories in a workspace using Bitbucket's native search syntax. Useful for impact analysis, dependency audits, or finding usage of a deprecated API.
result = actions.execute_tool(
identifier="user_bb_789",
tool_name="bitbucket_workspace_search_code",
tool_input={
"workspace": "my-team-workspace",
"search_query": "PaymentWebhookHandler",
"pagelen": 20
}
)
# Returns: list of matching file locations with repo, path, and matched lines
const result = await actions.executeTool({
identifier: "user_bb_789",
toolName: "bitbucket_workspace_search_code",
toolInput: {
"workspace": "my-team-workspace",
"search_query": "PaymentWebhookHandler",
"pagelen": 20
}
});
// Returns: list of matching file locations with repo, path, and matched lines
Framework wiring
5 Wiring into your agent framework
Scalekit integrates directly with LangChain. The agent decides what to call; Scalekit handles auth on every invocation. No token plumbing in your agent logic.
from langchain_anthropic import ChatAnthropic
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from scalekit.langchain import get_tools
bb_tools = get_tools(
connection_name="bitbucket",
identifier="user_bb_789"
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a developer assistant. Use the available tools to help manage Bitbucket repositories, pull requests, pipelines, and deployments."),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), bb_tools, prompt)
result = AgentExecutor(agent=agent, tools=bb_tools).invoke({
"input": "Find all open PRs in the backend-api repo, summarize each, and add a comment to any that have been open more than 7 days"
})
import { ChatAnthropic } from "@langchain/anthropic";
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { getTools } from "@scalekit-sdk/langchain";
const bbTools = getTools({
connectionName: "bitbucket",
identifier: "user_bb_789"
});
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a developer assistant. Use the available tools to help manage Bitbucket repositories, pull requests, pipelines, and deployments."],
new MessagesPlaceholder("chat_history", true),
["human", "{input}"],
new MessagesPlaceholder("agent_scratchpad"),
]);
const agent = await createToolCallingAgent({
llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }),
tools: bbTools,
prompt
});
const result = await AgentExecutor.fromAgentAndTools({
agent,
tools: bbTools
}).invoke({
input: "Find all open PRs in the backend-api repo, summarize each, and add a comment to any that have been open more than 7 days"
});
Other frameworks supported
Tool reference
All Bitbucket tools
Grouped by capability. Your agent calls tools by name — no API wrappers to write.
bitbucket_repository_create
Create a new repository in a workspace with privacy, SCM type, and issue tracker settings
Return repository details including description, language, size, and clone URLs
bitbucket_repository_update
Update repository description, privacy, issue tracker, or wiki settings
bitbucket_repository_delete
Permanently delete a repository and all its data
bitbucket_repository_fork
Fork a repository into the authenticated user's workspace or a specified target workspace
bitbucket_repositories_list
List all repositories in a workspace with filtering and sort support
bitbucket_repository_watchers_list
List all users watching a repository
Retrieve file or directory metadata (size, type, last commit) at a specific branch or commit
List all branches and tags (refs) for a repository
Create a new branch from a specified commit hash or branch name
Return details of a specific branch including its target commit
Delete a branch from a repository
List all branches with optional filter query and sort
bitbucket_branching_model_get
Return the effective branching model (e.g. Gitflow config) for a repository
bitbucket_branching_model_settings_get
Return the branching model configuration settings for a repository
bitbucket_branching_model_settings_update
Update the development or production branch in the branching model settings
bitbucket_branch_restriction_create
Create a branch protection rule by kind (push, delete, require approvals, etc.) and pattern
bitbucket_branch_restriction_get
Return a specific branch restriction rule by numeric ID
bitbucket_branch_restriction_update
Update an existing branch restriction rule's kind, pattern, or value
bitbucket_branch_restriction_delete
Delete a branch protection rule by numeric ID
bitbucket_branch_restrictions_list
List all branch protection rules for a repository
Return commit details including author, message, date, and diff stats
List commits for a repository, optionally filtered by branch or tag
Approve a specific commit on behalf of the authenticated user
bitbucket_commit_unapprove
Remove the authenticated user's approval from a commit
bitbucket_commit_comment_create
Post a new comment on a specific commit (Markdown supported)
bitbucket_commit_comment_get
Return a specific comment on a commit by comment ID
bitbucket_commit_comment_update
Update an existing comment on a commit
bitbucket_commit_comment_delete
Delete a specific comment from a commit
bitbucket_commit_comments_list
List all comments on a specific commit
bitbucket_commit_build_status_create
Create or update a CI/CD build status for a commit (SUCCESSFUL, FAILED, INPROGRESS, STOPPED)
bitbucket_commit_build_status_get
Return the build status for a specific commit and build key
bitbucket_commit_build_status_update
Update an existing build status for a specific commit and key
bitbucket_commit_statuses_list
List all build statuses (CI results) for a specific commit
bitbucket_file_history_list
List commits that modified a specific file path in the repository
Return the common ancestor (merge base) between two commits
Return a JSON summary of file changes between two commits or branches, showing added/modified/deleted files
Return diff stats between two commits or a branch/commit spec with line-level counts
Create a new tag pointing to a specific commit hash, with optional annotated message
Delete a tag from a repository
List all tags with optional filter and sort support
bitbucket_pull_request_create
Create a new PR with title, source/destination branches, description, and reviewers
bitbucket_pull_request_get
Return PR details including state, branches, reviewers, and participant status
bitbucket_pull_request_update
Update a PR's title, description, destination branch, or reviewers
bitbucket_pull_request_merge
Merge a PR using merge_commit, squash, or fast_forward strategy
bitbucket_pull_request_decline
Decline (reject) an open pull request
bitbucket_pull_request_approve
Approve a pull request on behalf of the authenticated user
bitbucket_pull_request_unapprove
Remove the authenticated user's approval from a PR
bitbucket_pull_request_request_changes
Request changes on a PR, blocking it from merging until addressed
bitbucket_pull_request_remove_request_changes
Remove a previously submitted change request from a PR
bitbucket_pull_request_comment_create
Post a new comment on a pull request (Markdown supported)
bitbucket_pull_request_comment_delete
Delete a comment from a pull request
bitbucket_pull_request_comments_list
List all comments on a pull request
bitbucket_pull_request_commits_list
List all commits included in a pull request
bitbucket_pull_request_diffstat_get
Return the diffstat for a PR using source and destination commit hashes
bitbucket_pull_request_statuses_list
List all commit build statuses for commits in a pull request
bitbucket_pull_request_task_create
Create a new task on a pull request with pending or resolved state
bitbucket_pull_request_task_get
Return a specific task on a pull request by task ID
bitbucket_pull_request_task_update
Update a PR task — resolve, reopen, or change content
bitbucket_pull_request_task_delete
Delete a task from a pull request
bitbucket_pull_request_tasks_list
List all tasks on a pull request
bitbucket_pull_request_activity_list
List all activity (comments, approvals, updates) for a specific PR
bitbucket_pull_requests_list
List PRs filterable by state: OPEN, MERGED, DECLINED, SUPERSEDED
bitbucket_pull_requests_activity_list
List overall activity for all pull requests in a repository
bitbucket_pipeline_trigger
Trigger a pipeline run on a branch, tag, or commit with optional variables
Return details of a specific pipeline run by UUID
Stop a running pipeline by UUID
List pipeline runs for a repository, optionally sorted by creation date
bitbucket_pipeline_steps_list
List all steps for a specific pipeline run
bitbucket_pipeline_step_log_get
Retrieve the log output for a specific step of a pipeline run
bitbucket_pipeline_schedule_create
Create a new cron-based pipeline schedule for a branch
bitbucket_pipeline_schedule_get
Return a specific pipeline schedule by UUID
bitbucket_pipeline_schedule_update
Update a pipeline schedule's cron expression or enabled state
bitbucket_pipeline_schedule_delete
Delete a pipeline schedule
bitbucket_pipeline_schedules_list
List all pipeline schedules for a repository
bitbucket_pipeline_variable_create
Create a new repository-level pipeline variable with optional secret masking
bitbucket_pipeline_variable_update
Update an existing repository pipeline variable by UUID
bitbucket_pipeline_variable_delete
Delete a pipeline variable from a repository
bitbucket_pipeline_variables_list
List all pipeline variables defined for a repository
bitbucket_environment_create
Create a deployment environment (Test, Staging, or Production) for a repository
bitbucket_environment_get
Return a specific deployment environment by UUID
bitbucket_environment_delete
Delete a deployment environment by UUID
bitbucket_environments_list
List all deployment environments (Test, Staging, Production) for a repository
Return a specific deployment record by UUID
bitbucket_deployments_list
List all deployments for a repository
bitbucket_deployment_variable_create
Create a variable for a deployment environment with optional secret masking
bitbucket_deployment_variable_update
Update an existing deployment environment variable
bitbucket_deployment_variable_delete
Delete a variable from a deployment environment
bitbucket_deployment_variables_list
List all variables for a deployment environment
Create a new issue with title, description, kind (bug/task/etc.), priority, and assignee
Return details of a specific issue by ID
Update an issue's title, content, kind, priority, or status
Delete an issue from a repository's issue tracker
List all issues with filter query and sort support
bitbucket_issue_comment_create
Post a new comment on an issue (Markdown supported)
bitbucket_issue_comment_update
Update an existing comment on an issue
bitbucket_issue_comment_delete
Delete a comment from an issue
bitbucket_issue_comments_list
List all comments on an issue
Cast a vote for an issue on behalf of the authenticated user
Check if the authenticated user has voted for an issue
Remove a vote from an issue
Start watching an issue to receive notifications
bitbucket_issue_watch_get
Check if the authenticated user is watching an issue
Return details of a specific workspace by its slug
bitbucket_workspace_members_list
List all members of a Bitbucket workspace
bitbucket_workspace_search_code
Search code across all repositories in a workspace with pagination support
bitbucket_workspace_project_create
Create a new project in a workspace with a unique key and optional privacy setting
bitbucket_workspace_project_get
Return a specific workspace project by project key
bitbucket_workspace_project_update
Update a project's name, description, or privacy settings
bitbucket_workspace_project_delete
Delete a project from a workspace
bitbucket_workspace_projects_list
List all projects in a workspace
bitbucket_workspace_pipeline_variable_create
Create a workspace-level pipeline variable with optional secret masking
bitbucket_workspace_pipeline_variable_get
Return a specific workspace pipeline variable by UUID
bitbucket_workspace_pipeline_variable_update
Update a workspace-level pipeline variable
bitbucket_workspace_pipeline_variable_delete
Delete a workspace pipeline variable
bitbucket_workspace_pipeline_variables_list
List all pipeline variables defined at the workspace level
bitbucket_default_reviewer_add
Add a user as a default reviewer for a repository
bitbucket_default_reviewer_get
Check if a user is configured as a default reviewer
bitbucket_default_reviewer_remove
Remove a user from the default reviewer list for a repository
bitbucket_default_reviewers_list
List all default reviewers configured for a repository
bitbucket_repository_permission_user_update
Set explicit read, write, or admin permission for a user on a repository
bitbucket_repository_permission_user_get
Return the explicit permission level for a specific user on a repository
bitbucket_repository_permission_user_delete
Remove a user's explicit permission from a repository
bitbucket_repository_permissions_users_list
List all users with explicit permissions on a repository
bitbucket_repository_permission_group_update
Set explicit read, write, or admin permission for a group on a repository
bitbucket_repository_permission_group_get
Return the explicit permission level for a specific group on a repository
bitbucket_repository_permission_group_delete
Remove a group's explicit permission from a repository
bitbucket_repository_permissions_groups_list
List all groups with explicit permissions on a repository
bitbucket_deploy_key_create
Add an SSH public key as a deploy key for read-only or read-write repository access
bitbucket_deploy_key_delete
Remove a deploy key from a repository by key ID
bitbucket_deploy_keys_list
List all deploy keys configured on a repository
Create a webhook on a repository to receive event notifications at a specified URL
Return the details of a specific webhook by UID
Update a webhook's URL, subscribed events, or active status
Delete a webhook from a repository
List all webhooks installed on a repository
List all download artifacts for a repository
bitbucket_download_delete
Delete a specific download artifact by filename
Return a specific milestone from the issue tracker by ID
bitbucket_milestones_list
List all milestones defined for a repository's issue tracker
Return a specific version from the issue tracker by ID
List all versions defined for a repository's issue tracker
Return a specific issue tracker component by ID
bitbucket_components_list
List all components defined for a repository's issue tracker
Return the authenticated user's Bitbucket profile including display name and account ID
bitbucket_user_emails_list
Return all email addresses associated with the authenticated Bitbucket user
Connector notes
Bitbucket-specific behavior
Callback URL must match exactly — including trailing slashes
Bitbucket performs an exact string match on the OAuth consumer callback URL. Any mismatch — including a trailing slash difference — will cause the OAuth flow to fail with a redirect_uri_mismatch error. Copy the redirect URI from the Scalekit dashboard exactly as shown and paste it into the Bitbucket consumer's Callback URL field without modification.
Scope changes require re-authorization
Bitbucket OAuth consumers have fixed scope grants. If your agent needs additional permissions after a user has already authorized, you must update the consumer's scopes in Bitbucket, update the scope configuration in Scalekit, and prompt the user to re-authorize. The existing token will not gain new scopes automatically.
PR diffstat requires commit hashes, not branch names
bitbucket_pull_request_diffstat_get requires the source and destination commit hashes from the PR object — not branch names. Fetch these from bitbucket_pull_request_get using source.commit.hash and destination.commit.hash before calling the diffstat tool.
Infrastructure decision
Why not build this yourself
The Bitbucket OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for:
PROBLEM 01
Per-user workspace routing — every API call must carry the correct workspace slug for that user, and mixing up workspace context across tenants produces silent 403s or operates on the wrong data
PROBLEM 02
OAuth consumer scope grants are fixed at registration — adding new capabilities requires consumer updates, re-authorization prompts, and a re-credentialing flow your backend must handle
PROBLEM 03
Per-user token isolation in a multi-tenant system — one engineer's Bitbucket credentials must never route calls into another user's workspace or repositories
PROBLEM 04
Proactive token refresh, revocation detection, and encrypted storage — all required before you write a single branch or pipeline operation
That's one connector. Your agent product will eventually need GitHub, GitLab, Jira, Linear, and whatever else your customers ask for. Each has its own auth quirks and failure modes.
Scalekit maintains every connector. You maintain none of them.