fix(bitbucket): replace retired CHANGE-3022 API endpoints#883
fix(bitbucket): replace retired CHANGE-3022 API endpoints#883drazisil-codecov wants to merge 5 commits intomainfrom
Conversation
- list_permissions: GET /user/permissions/repositories → GET /repositories?role=member
- get_authenticated: GET /user/permissions/repositories → GET /repositories?role=contributor
- get_is_admin: workspace UUID → slug in /workspaces/{slug}/permissions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #883 +/- ##
==========================================
- Coverage 92.26% 92.25% -0.01%
==========================================
Files 1307 1307
Lines 48014 47984 -30
Branches 1632 1625 -7
==========================================
- Hits 44298 44270 -28
- Misses 3407 3408 +1
+ Partials 309 306 -3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
GET /user/permissions/repositories (the endpoint list_permissions called)
was retired by Bitbucket CHANGE-3022. The replacement GET /repositories?role=member
is itself deprecated — Bitbucket's new access model is workspace-based.
list_permissions existed to discover repos where a user had collaborator access
outside of workspace membership. Since Bitbucket is retiring that collaborator
model, list_teams() workspace slugs are sufficient — the per-workspace
GET /repositories/{workspace} calls in list_repos already cover everything.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Follow-up to initial fix: removed
The initial fix in this PR replaced that with The root cause is that Bitbucket's new access model is workspace-based. The "repo-level collaborator without workspace membership" pattern is the deprecated
So we dropped |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f3232d3. Configure here.
GET /repositories?role=contributor is deprecated per the swagger spec.
Replace with GET /repositories/{workspace}?role=contributor which is
the non-deprecated workspace-scoped equivalent and returns the same shape.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
repos_to_log was always [] after list_permissions was removed. Simplify _get_teams_and_username_to_list to return usernames directly and drop the empty list from callers and log statements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /repositories mock was left over from list_permissions removal. Nothing calls that URL anymore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| async def get_is_admin(self, user, token=None): | ||
| user_uuid = "{" + user["service_id"] + "}" | ||
| workspace_slug = self.data["owner"]["service_id"] | ||
| workspace_slug = self.data["owner"]["username"] |
There was a problem hiding this comment.
Just checking: do we receive webhooks on name changes? I want to make sure that users who rename their org/slug (if that's possible) will have it reflected in Codecov.
| """ | ||
| Lists all repositories a user is part of. | ||
| *Note: | ||
| Bitbucket API V2 does not provide a dedicated endpoint which returns all repos a user is part of. | ||
| It provides however, an endpoint to get all the repos a user is part of from an specific org or user. | ||
| Endpoint to list repos from an specific user: | ||
| - /repositories/{username} | ||
| In order to get all the repositories a user is part of, we first need to get all the orgs and repo owners | ||
| - Orgs/Teams can be obtained using the 'list_teams' method | ||
| - Usernames of repo owners is a bit tricky since Bitbucket doesnt provide an endpoint for this | ||
| - Solution: | ||
| Use the 'list_permissions' method to get all repo permissions and exctract owner's username | ||
| from the repository 'full_name' attribute | ||
| Once we have all orgs/teams and owner's usernames we should call "/repositories/{username}" endpoint | ||
| for each of the orgs/teams and owner's usernames. | ||
| """ |
There was a problem hiding this comment.
I feel like we're losing some potential useful context with the removal of this docstring. Can we update it to better reflect what it's actually doing?

Summary
Bitbucket retired three endpoints on 2026-04-14 (CHANGE-3022), all returning 410. This PR replaces them with current equivalents and removes one deprecated call entirely.
Dead endpoints fixed (410 → working):
GET /user/permissions/repositories→ removed (list_permissions) and replaced (get_authenticated)GET /workspaces/{uuid}/permissions→GET /workspaces/{slug}/permissions— was passing UUID where slug is required, causing 404s inget_is_adminChanges:
get_is_admin: useself.data["owner"]["username"](slug) instead ofservice_id(UUID)get_authenticated:GET /repositories/{workspace}?role=contributor&q=full_name="..."— presence in results = write accesslist_permissions(): deleted entirely (see below)Why
list_permissions()was dropped, not replaced:list_permissionscalledGET /repositories?role=memberto discover repos where a user had collaborator access outside of workspace membership. That endpoint is itself deprecated per the Bitbucket swagger spec — the recommended replacement is the workspace-scopedGET /repositories/{workspace}.Since
list_reposalready callsGET /repositories/{workspace}for every workspace slug returned bylist_teams(), and Bitbucket is retiring the "repo-level collaborator without workspace membership" access model,list_permissionswas redundant. Dropping it means the sync path uses only non-deprecated workspace-scoped calls.Swagger spec used for response shape verification: https://dac-static.atlassian.com/cloud/bitbucket/swagger.v3.json
Test plan
sync_reposcalls only workspace-scoped endpoints (GET /repositories/{workspace})get_is_admincorrectly returns 403 for a user who is a workspace member but not an ownerget_authenticatedcorrectly denies edit access for a user not in the workspacesync_repos/sync_teamsno longer produce 410s in Sentry after deploy🤖 Generated with Claude Code