Summary and recommendation
The Bitbucket Cloud REST API (api.bitbucket.org/2.0) supports OAuth 2.0 (Authorization Code, Client Credentials, Implicit), App Passwords via HTTP Basic, and workspace/repository Access Tokens as Bearer tokens. Scopes are defined at the OAuth consumer level and cannot be narrowed per-request. The Resource Owner Password Credentials grant is no longer supported.
Critical caveat: the Bitbucket REST API does not support creating, inviting, or deleting workspace users. User lifecycle operations must go through admin.atlassian.com or the SCIM 2.0 API at api.atlassian.com/scim/directory/{directoryId}/Users, which itself requires an Atlassian Guard Standard subscription and a pre-configured SSO/SAML setup.
For teams needing deeper, pre-built coverage across the Atlassian stack and adjacent identity systems, Stitchflow's MCP server with ~100 deep IT/identity integrations provides a structured alternative to building and maintaining direct API connections for each product.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (Authorization Code, Client Credentials, Implicit grants); also App Passwords (HTTP Basic) and Repository/Project/Workspace Access Tokens (Bearer) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Atlassian Guard Standard subscription (separate add-on, ~$3–4/user/month billed per managed user across the Atlassian organization). SSO must be configured before enabling SCIM. |
Authentication
Auth method: OAuth 2.0 (Authorization Code, Client Credentials, Implicit grants); also App Passwords (HTTP Basic) and Repository/Project/Workspace Access Tokens (Bearer)
Setup steps
- Go to Workspace Settings > Apps and features > OAuth consumers.
- Click 'Add consumer', provide a name and a Callback URL (required for OAuth 2.0).
- Select the required permission scopes (e.g., 'account', 'repository:admin').
- Save; Bitbucket generates a Client Key and Client Secret.
- Exchange the authorization code for an access token via POST https://bitbucket.org/site/oauth2/access_token with grant_type=authorization_code.
- Use the returned Bearer token in the Authorization header for all API requests.
- Alternatively, create an App Password under Personal Settings > App passwords and use HTTP Basic Auth (email:app_password).
- For server-to-server use, create a Workspace/Repository Access Token under Workspace Settings > Access tokens.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| account | Read access to all account information for the authorizing user. | GET /user, GET /user/emails, GET /users/{selected_user} |
| account:write | Ability to change properties on the user's account. | Updating account-level properties |
| Read access to the user's primary email address only. | Lightweight login/identity flows | |
| repository | Read access to all repositories the authorizing user can access. | GET /repositories, listing repo members |
| repository:admin | Admin access to repositories; required for permission CRUD endpoints. | GET/PUT/DELETE /repositories/{workspace}/{repo_slug}/permissions-config/users/{selected_user} |
| webhook | Read and write access to webhook subscriptions on all accessible resources. | Creating, listing, and deleting webhooks |
| read:user:bitbucket | Forge/API token scope to read user profile data. | API token or Forge app access to /user endpoints |
| read:workspace:bitbucket | Forge/API token scope to read workspace membership data. | API token access to /workspaces and /workspaces/{workspace}/members |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| type | string | Always 'user' for user objects. | n/a (read-only) | n/a | Discriminator field per Bitbucket object model. |
| uuid | string | Globally unique identifier for the Atlassian account, formatted as {uuid}. | n/a (system-assigned) | immutable | Preferred stable identifier for API references. |
| account_id | string | Atlassian account ID (e.g., 557058:c0b72ad0-...). Primary key for cross-product identity. | n/a (system-assigned) | immutable | Replaced username as the canonical user identifier after April 2019 (GDPR change). |
| display_name | string | User's display name; may be controlled by user privacy settings. | n/a | user-controlled | May be obscured if user has restricted visibility. |
| nickname | string | User's chosen nickname/handle on Bitbucket. | n/a | user-controlled | Introduced as a replacement for the deprecated username field. |
| links | object | HAL-style links including self, avatar (href), and html (profile URL). | n/a | n/a | avatar.href and html.href are informative, not REST resources. |
| created_on | string (ISO 8601) | Timestamp when the account was created. | n/a (system-assigned) | immutable | Returned on /user (authenticated user) endpoint. |
| is_staff | boolean | Whether the user is an Atlassian staff member. | n/a | n/a | Informational only. |
| has_2fa_enabled | boolean | Whether the authenticated user has two-factor authentication enabled. | n/a | n/a | Only returned on GET /user (self); not available for other users. |
| username | string | Legacy username field. Deprecated since April 2019. | n/a | n/a | Still returned on GET /user (self only) for backward compatibility; removed from all other user objects. |
Core endpoints
Get authenticated user (self)
- Method: GET
- URL:
https://api.bitbucket.org/2.0/user - Watch out for: Requires 'account' OAuth 2.0 scope. This is the only endpoint that still returns the legacy 'username' field.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/user' \
--header 'Authorization: Bearer <access_token>' \
--header 'Accept: application/json'
Response example
{
"type": "user",
"uuid": "{abc123}",
"account_id": "557058:abc",
"display_name": "Jane Doe",
"nickname": "janedoe",
"created_on": "2020-01-01T00:00:00Z"
}
Get a specific user by account_id or uuid
- Method: GET
- URL:
https://api.bitbucket.org/2.0/users/{selected_user} - Watch out for: Returns only public profile data. Username-based URLs are deprecated; use account_id or uuid. User privacy settings may limit display_name and avatar visibility.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/users/557058:abc' \
--header 'Authorization: Bearer <access_token>'
Response example
{
"type": "user",
"uuid": "{abc123}",
"account_id": "557058:abc",
"display_name": "Jane Doe",
"nickname": "janedoe"
}
List authenticated user's email addresses
- Method: GET
- URL:
https://api.bitbucket.org/2.0/user/emails - Watch out for: Requires 'email' or 'account' scope. Returns both confirmed and unconfirmed addresses for the authenticated user only.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/user/emails' \
--header 'Authorization: Bearer <access_token>'
Response example
{
"pagelen": 10,
"values": [
{"type": "email", "email": "jane@example.com",
"is_primary": true, "is_confirmed": true}
]
}
List workspace members
- Method: GET
- URL:
https://api.bitbucket.org/2.0/workspaces/{workspace}/members - Watch out for: Requires workspace membership to call. Does not support adding or removing members via this endpoint; membership is managed through Atlassian Admin or SCIM.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/workspaces/myworkspace/members' \
--header 'Authorization: Bearer <access_token>'
Response example
{
"pagelen": 10,
"values": [
{"type": "workspace_membership",
"user": {"type": "user", "uuid": "{abc}"},
"workspace": {"type": "workspace"}}
]
}
Get a specific workspace member
- Method: GET
- URL:
https://api.bitbucket.org/2.0/workspaces/{workspace}/members/{member} - Watch out for: Member must be identified by account_id or uuid, not username.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/workspaces/myworkspace/members/557058:abc' \
--header 'Authorization: Bearer <access_token>'
Response example
{
"type": "workspace_membership",
"user": {"type": "user", "uuid": "{abc}"},
"workspace": {"type": "workspace", "slug": "myworkspace"}
}
List repository-level user permissions
- Method: GET
- URL:
https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/permissions-config/users - Watch out for: Requires 'repository:admin' scope and repository admin permission. Only returns explicit (direct) permissions, not inherited group permissions.
Request example
curl --request GET \
--url 'https://api.bitbucket.org/2.0/repositories/myws/myrepo/permissions-config/users' \
--header 'Authorization: Bearer <access_token>'
Response example
{
"pagelen": 10,
"values": [
{"type": "repository_user_permission",
"permission": "write",
"user": {"type": "user", "uuid": "{abc}"}}
]
}
Set/update a user's repository permission
- Method: PUT
- URL:
https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/permissions-config/users/{selected_user} - Watch out for: Requires 'repository:admin' scope. Valid permission values: 'read', 'write', 'admin'. Cannot grant permissions higher than the caller's own.
Request example
curl --request PUT \
--url 'https://api.bitbucket.org/2.0/repositories/myws/myrepo/permissions-config/users/557058:abc' \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--data '{"permission": "write"}'
Response example
{
"type": "repository_user_permission",
"permission": "write",
"user": {"type": "user", "uuid": "{abc}"}
}
Remove a user's explicit repository permission
- Method: DELETE
- URL:
https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/permissions-config/users/{selected_user} - Watch out for: Only removes the explicit direct permission. User may still have access via group membership. Requires 'repository:admin' scope.
Request example
curl --request DELETE \
--url 'https://api.bitbucket.org/2.0/repositories/myws/myrepo/permissions-config/users/557058:abc' \
--header 'Authorization: Bearer <access_token>'
Response example
HTTP 204 No Content
Rate limits, pagination, and events
- Rate limits: Rate limits are applied per rolling one-hour window. Authenticated requests are measured per user ID; unauthenticated requests per IP address. A 429 response is returned when the limit is exceeded. Scaled rate limits apply to the 'api resource group' (most /2.0/repositories/* endpoints) for requests made via Access Tokens or Forge asApp, adding 10 requests/hour per paid workspace seat, capped at 10,000 requests/hour.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: Rate limit headers returned for scaled endpoints: X-RateLimit-Limit (total permitted/hour), X-RateLimit-Resource (endpoint group), X-RateLimit-NearLimit (boolean, true when <20% remaining). No Retry-After header is documented; back-off and retry after waiting is recommended. Resource and rate limits are not part of the stable API contract and may change dynamically.
- Pagination method: offset
- Default page size: 10
- Max page size: 100
- Pagination pointer: pagelen (page size), page (page number)
| Plan | Limit | Concurrent |
|---|---|---|
| Unauthenticated | 60 requests/hour | 0 |
| Authenticated (all plans, standard endpoints) | 1,000 requests/hour | 0 |
| Scaled (Standard/Premium, Access Token or Forge asApp, api resource group) | 1,000 + (10 × paid_seats) requests/hour, max 10,000/hour | 0 |
- Webhooks available: Yes
- Webhook notes: Bitbucket Cloud supports repository-scoped and workspace-scoped webhooks. After creation, Bitbucket sends an HTTP POST payload to the configured URL whenever a subscribed event occurs. Webhooks are not rate-limited unlike API requests. The 'webhook' scope is required to create or manage webhook subscriptions.
- Alternative event strategy: No native user lifecycle webhook events (e.g., user added/removed from workspace) exist in Bitbucket Cloud. For user provisioning events, use the Atlassian SCIM API or poll /workspaces/{workspace}/members.
- Webhook events: repo:push, repo:fork, repo:updated, repo:commit_comment_created, repo:commit_status_created, repo:commit_status_updated, pullrequest:created, pullrequest:updated, pullrequest:approved, pullrequest:unapproved, pullrequest:fulfilled, pullrequest:rejected, pullrequest:comment_created, pullrequest:comment_updated, pullrequest:comment_deleted, issue:created, issue:updated, issue:comment_created
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Atlassian Guard Standard subscription (separate add-on, ~$3–4/user/month billed per managed user across the Atlassian organization). SSO must be configured before enabling SCIM.
Endpoint: https://api.atlassian.com/scim/directory/{directoryId}/Users
Supported operations: GET /scim/directory/{directoryId}/Users - list users (paginated via startIndex, default 100/page), POST /scim/directory/{directoryId}/Users - create/provision a user, GET /scim/directory/{directoryId}/Users/{userId} - get a single user, PUT /scim/directory/{directoryId}/Users/{userId} - full replace of user attributes, PATCH /scim/directory/{directoryId}/Users/{userId} - partial update (e.g., set active=false to deactivate), DELETE /scim/directory/{directoryId}/Users/{userId} - deprovision/delete a user
Limitations:
- Group sync (SCIM Groups) is NOT available for Bitbucket - only user account provisioning is supported. Group sync is available for Jira and Confluence only.
- SCIM only provisions Atlassian accounts; it does not assign repository-level permissions in Bitbucket. Repository access must be managed manually or via the Bitbucket permissions API.
- SCIM API keys expire after one year (enforced from January 2025); annual rotation is required.
- The directoryId is generated at setup time in admin.atlassian.com and is not shown again after initial configuration - store it securely.
- Deactivating a SCIM-provisioned user deactivates them across all Atlassian products in the organization, not just Bitbucket.
- Updating a SCIM-provisioned user's email must be done via the SCIM API, not the Bitbucket user management API.
- Batch group membership syncs above 10,000 users will suspend subsequent directory syncs.
- SSO (SAML) must be configured and domain verified before SCIM provisioning can be enabled.
Common scenarios
Three primary automation scenarios are well-supported by the API, each with explicit constraints to plan around.
Looking up workspace members and permissions: Use GET /workspaces/{workspace}/members (paginated, pagelen max 100) to list members, then GET /repositories/{workspace}/{repo_slug}/permissions-config/users to retrieve explicit repo-level permissions. Note that workspace member listing does not return email addresses - email is only available for the authenticated user via GET /user/emails. Always reference users by account_id or uuid; username-based paths are deprecated since April 2019.
Granting or updating repository permissions: PUT /repositories/{workspace}/{repo_slug}/permissions-config/users/{account_id} with a body of {"permission": "read|write|admin"} requires the repository:admin scope. The caller cannot grant a permission level higher than their own. This sets only the explicit direct permission; group-inherited access is managed separately and is not visible from this endpoint.
Provisioning a new user via SCIM: POST to api.atlassian.com/scim/directory/{directoryId}/Users creates the Atlassian account. SCIM alone does not grant Bitbucket product access or assign any repository permissions - a separate group assignment in Atlassian Admin and a subsequent Bitbucket permissions API call are both required. SCIM API keys expire after one year and must be rotated manually. Group sync via SCIM is not supported for Bitbucket; it is available only for Jira and Confluence.
Look up a workspace member's profile and permissions
- Authenticate via OAuth 2.0 (Authorization Code) or App Password with 'account' and 'repository' scopes.
- GET https://api.bitbucket.org/2.0/workspaces/{workspace}/members to list all members; paginate using ?page=N&pagelen=50.
- Identify the target user's account_id or uuid from the response.
- GET https://api.bitbucket.org/2.0/users/{account_id} to retrieve the user's public profile.
- GET https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/permissions-config/users to list explicit repo permissions; filter for the target user.
Watch out for: Workspace member listing does not include email addresses. Email is only available for the authenticated user via GET /user/emails. User privacy settings may hide display_name.
Grant a user write access to a repository
- Authenticate with an account that has repository admin rights; use 'repository:admin' scope.
- Resolve the target user's account_id (e.g., from GET /workspaces/{workspace}/members).
- PUT https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/permissions-config/users/{account_id} with body {"permission": "write"}.
- Verify the response returns the updated permission object (HTTP 200).
Watch out for: You cannot grant a permission level higher than your own. This sets an explicit direct permission; the user may already have access via group membership which is managed separately.
Provision a new user via SCIM and verify Bitbucket access
- Ensure Atlassian Guard Standard is active and SSO is configured; retrieve the directoryId and SCIM API key from admin.atlassian.com > Security > Identity providers > Set up user provisioning.
- POST https://api.atlassian.com/scim/directory/{directoryId}/Users with SCIM 2.0 user payload (userName, name.givenName, name.familyName, emails[primary]).
- Confirm the user appears in admin.atlassian.com under Managed Accounts.
- Assign the user to a Bitbucket-access group in Atlassian Admin to grant product access (SCIM alone does not grant Bitbucket access).
- Use the Bitbucket permissions API (PUT /repositories/.../permissions-config/users/{account_id}) to assign repository-level permissions as needed.
Watch out for: SCIM creates the Atlassian account but does NOT assign Bitbucket repository permissions or group memberships within Bitbucket. Group sync via SCIM is not supported for Bitbucket. SCIM API keys expire annually and must be rotated manually.
Why building this yourself is a trap
The most significant API trap is the split between account lifecycle and permission management. SCIM handles account creation and deactivation at the Atlassian org level, but it has no mechanism to assign or sync Bitbucket repository or project permissions.
Any automation that stops at SCIM provisioning leaves users with an Atlassian account but no usable Bitbucket access - or, on offboarding, deactivates the account org-wide across all Atlassian products, not just Bitbucket.
Rate limits are applied per rolling one-hour window: 1,000 requests/hour for authenticated standard endpoints, scaling to a maximum of 10,000/hour for Access Token or Forge asApp calls against the api resource group (1,000 + 10 × paid seats). No Retry-After header is returned on 429 responses; implement exponential backoff.
Rate limit thresholds are not part of the stable API contract and may change dynamically.
Additional sharp edges: the directoryId generated during SCIM setup in admin.atlassian.com is not displayed again after initial configuration - store it securely at setup time. Access tokens (workspace/project/repository) appear as a bot user in logs and cannot call the /user (self) endpoint. The /user/workspaces endpoint is deprecated and scheduled for removal by end of 2025.
Automate Atlassian Bitbucket workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.