Protected branches API
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Use this API to manageprotected branches.
GitLab Premium and GitLab Ultimate support more granular protections for pushing to branches.Administrators can grant permission to modify and push to protected branches only to deploy keys,instead of specific users.
Valid access levels
TheProtectedRefAccess.allowed_access_levels method defines the following access levels:
0: No access30: Developer role40: Maintainer role60: Administrator
List protected branches
Get a list ofprotected branches from a projectas they are defined in the UI. If a wildcard is set, it is returned instead of the exact nameof the branches that match that wildcard.
GET /projects/:id/protected_branchesSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | Yes | ID orURL-encoded path of the project. |
search | string | No | Name or part of the name of protected branches to search for. |
If successful, returns200 OK and thefollowing response attributes:
| Attribute | Type | Description |
|---|---|---|
allow_force_push | boolean | Iftrue, force push is allowed on this branch. |
code_owner_approval_required | boolean | Iftrue, code owner approval is required for pushes to this branch. |
id | integer | ID of the protected branch. |
inherited | boolean | Iftrue, protection settings are inherited from parent group. Premium and Ultimate only. |
merge_access_levels | array | Array of merge access level configurations. |
merge_access_levels[].access_level | integer | Access level for merging. |
merge_access_levels[].access_level_description | string | Human-readable description of the access level. |
merge_access_levels[].group_id | integer | ID of the group with merge access. Premium and Ultimate only. |
merge_access_levels[].id | integer | ID of the merge access level configuration. |
merge_access_levels[].user_id | integer | ID of the user with merge access. Premium and Ultimate only. |
name | string | Name of the protected branch. |
push_access_levels | array | Array of push access level configurations. |
push_access_levels[].access_level | integer | Access level for pushing. |
push_access_levels[].access_level_description | string | Human-readable description of the access level. |
push_access_levels[].deploy_key_id | integer | ID of the deploy key with push access. |
push_access_levels[].group_id | integer | ID of the group with push access. Premium and Ultimate only. |
push_access_levels[].id | integer | ID of the push access level configuration. |
push_access_levels[].user_id | integer | ID of the user with push access. Premium and Ultimate only. |
In the following example request, the project ID is5.
curl --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches"The following example response includes:
- Two protected branches with IDs
100and101. push_access_levelswith IDs1001,1002, and1003.merge_access_levelswith IDs2001and2002.
[{"id":100,"name":"main","push_access_levels":[{"id":1001,"access_level":40,"access_level_description":"Maintainers"},{"id":1002,"access_level":40,"access_level_description":"Deploy key","deploy_key_id":1}],"merge_access_levels":[{"id":2001,"access_level":40,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false},{"id":101,"name":"release/*","push_access_levels":[{"id":1003,"access_level":40,"access_level_description":"Maintainers"}],"merge_access_levels":[{"id":2002,"access_level":40,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}]Users on GitLab Premium or Ultimate also seetheuser_id,group_id, andinherited parameters. If theinherited parameterexists, the setting was inherited from the project’s group.
The following example response includes:
- One protected branch with ID
100. push_access_levelswith IDs1001and1002.merge_access_levelswith ID2001.
[{"id":101,"name":"main","push_access_levels":[{"id":1001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"},{"id":1002,"access_level":40,"access_level_description":"Deploy key","deploy_key_id":1,"user_id":null,"group_id":null}],"merge_access_levels":[{"id":2001,"access_level":null,"user_id":null,"group_id":1234,"access_level_description":"Example Merge Group"}],"allow_force_push":false,"code_owner_approval_required":false,"inherited":true}]Get a single protected branch or wildcard protected branch
Get a single protected branch or wildcard protected branch.
GET /projects/:id/protected_branches/:nameSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | Yes | ID orURL-encoded path of the project. |
name | string | Yes | Name of the branch or wildcard. |
If successful, returns200 OK and thefollowing response attributes:
| Attribute | Type | Description |
|---|---|---|
allow_force_push | boolean | Iftrue, force push is allowed on this branch. |
code_owner_approval_required | boolean | Iftrue, code owner approval is required for pushes to this branch. |
id | integer | ID of the protected branch. |
merge_access_levels | array | Array of merge access level configurations. |
merge_access_levels[].access_level | integer | Access level for merging. |
merge_access_levels[].access_level_description | string | Human-readable description of the access level. |
merge_access_levels[].group_id | integer | ID of the group with merge access. Premium and Ultimate only. |
merge_access_levels[].id | integer | ID of the merge access level configuration. |
merge_access_levels[].user_id | integer | ID of the user with merge access. Premium and Ultimate only. |
name | string | Name of the protected branch. |
push_access_levels | array | Array of push access level configurations. |
push_access_levels[].access_level | integer | Access level for pushing. |
push_access_levels[].access_level_description | string | Human-readable description of the access level. |
push_access_levels[].group_id | integer | ID of the group with push access. Premium and Ultimate only. |
push_access_levels[].id | integer | ID of the push access level configuration. |
push_access_levels[].user_id | integer | ID of the user with push access. Premium and Ultimate only. |
In the following example request, the project ID is5 and branch name ismain:
curl --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches/main"Example response:
{"id":101,"name":"main","push_access_levels":[{"id":1001,"access_level":40,"access_level_description":"Maintainers"}],"merge_access_levels":[{"id":2001,"access_level":40,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}Users on GitLab Premium or Ultimate also seetheuser_id andgroup_id parameters.
Example response:
{"id":101,"name":"main","push_access_levels":[{"id":1001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"}],"merge_access_levels":[{"id":2001,"access_level":null,"user_id":null,"group_id":1234,"access_level_description":"Example Merge Group"}],"allow_force_push":false,"code_owner_approval_required":false}Protect repository branches
Protect a single repository branch or several project repositorybranches using a wildcard protected branch.
POST /projects/:id/protected_branchesSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | Yes | ID orURL-encoded path of the project. |
name | string | Yes | Name of the branch or wildcard. |
allow_force_push | boolean | No | Iftrue, members who can push to this branch can also force push. Default isfalse. |
allowed_to_merge | array | No | Array of merge access levels, with each described by a hash of the form{user_id: integer},{group_id: integer}, or{access_level: integer}. Premium and Ultimate only. |
allowed_to_push | array | No | Array of push access levels, with each described by a hash of the form{user_id: integer},{group_id: integer},{deploy_key_id: integer}, or{access_level: integer}. Premium and Ultimate only. |
allowed_to_unprotect | array | No | Array of unprotect access levels, with each described by a hash of the form{user_id: integer},{group_id: integer}, or{access_level: integer}. Access levelNo access is not available for this field. Premium and Ultimate only. |
code_owner_approval_required | boolean | No | Iftrue, prevents pushes to this branch if it matches an item in theCODEOWNERS file. Default isfalse. Premium and Ultimate only. |
merge_access_level | integer | No | Access levels allowed to merge. Default is40 (Maintainer role). |
push_access_level | integer | No | Access levels allowed to push. Default is40 (Maintainer role). |
unprotect_access_level | integer | No | Access levels allowed to unprotect. Default is40 (Maintainer role). |
When you configure access levels:
- You can set multiple access levels simultaneously for
allowed_to_pushandallowed_to_merge. - The most permissive access level determines who can perform the action.
This behavior differs from the UI, which automatically clears other role selectionswhen you selectNo one (access_level: 0).
If successful, returns201 Created and thefollowing response attributes:
| Attribute | Type | Description |
|---|---|---|
allow_force_push | boolean | Iftrue, force push is allowed on this branch. |
code_owner_approval_required | boolean | Iftrue, code owner approval is required for pushes to this branch. |
id | integer | ID of the protected branch. |
merge_access_levels | array | Array of merge access level configurations. |
merge_access_levels[].access_level | integer | Access level for merging. |
merge_access_levels[].access_level_description | string | Human-readable description of the access level. |
merge_access_levels[].group_id | integer | ID of the group with merge access. Premium and Ultimate only. |
merge_access_levels[].id | integer | ID of the merge access level configuration. |
merge_access_levels[].user_id | integer | ID of the user with merge access. Premium and Ultimate only. |
name | string | Name of the protected branch. |
push_access_levels | array | Array of push access level configurations. |
push_access_levels[].access_level | integer | Access level for pushing. |
push_access_levels[].access_level_description | string | Human-readable description of the access level. |
push_access_levels[].deploy_key_id | integer | ID of the deploy key with push access. |
push_access_levels[].group_id | integer | ID of the group with push access. Premium and Ultimate only. |
push_access_levels[].id | integer | ID of the push access level configuration. |
push_access_levels[].user_id | integer | ID of the user with push access. Premium and Ultimate only. |
unprotect_access_levels | array | Array of unprotect access level configurations. |
unprotect_access_levels[].access_level | integer | Access level for unprotecting. |
unprotect_access_levels[].access_level_description | string | Human-readable description of the access level. |
unprotect_access_levels[].group_id | integer | ID of the group with unprotect access. Premium and Ultimate only. |
unprotect_access_levels[].id | integer | ID of the unprotect access level configuration. |
unprotect_access_levels[].user_id | integer | ID of the user with unprotect access. Premium and Ultimate only. |
In the following example request, the project ID is5 and branch name is*-stable.
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches?name=*-stable&push_access_level=30&merge_access_level=30&unprotect_access_level=40"The example response includes:
- A protected branch with ID
101. push_access_levelswith ID1001.merge_access_levelswith ID2001.unprotect_access_levelswith ID3001.
{"id":101,"name":"*-stable","push_access_levels":[{"id":1001,"access_level":30,"access_level_description":"Developers + Maintainers"}],"merge_access_levels":[{"id":2001,"access_level":30,"access_level_description":"Developers + Maintainers"}],"unprotect_access_levels":[{"id":3001,"access_level":40,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}Users on GitLab Premium or Ultimate also seetheuser_id andgroup_id parameters:
The following example response includes:
- A protected branch with ID
101. push_access_levelswith ID1001.merge_access_levelswith ID2001.unprotect_access_levelswith ID3001.
{"id":1,"name":"*-stable","push_access_levels":[{"id":1001,"access_level":30,"user_id":null,"group_id":null,"access_level_description":"Developers + Maintainers"}],"merge_access_levels":[{"id":2001,"access_level":30,"user_id":null,"group_id":null,"access_level_description":"Developers + Maintainers"}],"unprotect_access_levels":[{"id":3001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}Example with user push access and group merge access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Elements in theallowed_to_push /allowed_to_merge /allowed_to_unprotect array should take theform{user_id: integer},{group_id: integer}, or{access_level: integer}.Each user must have access to the project and each group musthave this project shared.These access levels allow more granular control over protected branch access.For more information, seeconfigure group permissions.
The following example request creates a protected branch with user push access and group merge access.Theuser_id is2 and thegroup_id is3.
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches?name=*-stable&allowed_to_push%5B%5D%5Buser_id%5D=2&allowed_to_merge%5B%5D%5Bgroup_id%5D=3"The following example response includes:
- A protected branch with ID
101. push_access_levelswith ID1001.merge_access_levelswith ID2001.unprotect_access_levelswith ID3001.
{"id":101,"name":"*-stable","push_access_levels":[{"id":1001,"access_level":null,"user_id":2,"group_id":null,"access_level_description":"Administrator"}],"merge_access_levels":[{"id":2001,"access_level":null,"user_id":null,"group_id":3,"access_level_description":"Example Merge Group"}],"unprotect_access_levels":[{"id":3001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}Example with deploy key access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Elements in theallowed_to_push array should take the form{user_id: integer},{group_id: integer},{deploy_key_id: integer}, or{access_level: integer}.The deploy key must be enabled for your project and it must have write access to your project repository.For other requirements, seeAllow deploy keys to push to a protected branch.
Example request:
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches?name=*-stable&allowed_to_push[][deploy_key_id]=1"The following example response includes:
- A protected branch with ID
101. push_access_levelswith ID1001.merge_access_levelswith ID2001.unprotect_access_levelswith ID3001.
{"id":101,"name":"*-stable","push_access_levels":[{"id":1001,"access_level":null,"user_id":null,"group_id":null,"deploy_key_id":1,"access_level_description":"Deploy"}],"merge_access_levels":[{"id":2001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"}],"unprotect_access_levels":[{"id":3001,"access_level":40,"user_id":null,"group_id":null,"access_level_description":"Maintainers"}],"allow_force_push":false,"code_owner_approval_required":false}Example with allow to push and allow to merge access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Example request:
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --header"Content-Type: application/json"\ --data'{ "name": "main", "allowed_to_push": [ {"access_level": 30} ], "allowed_to_merge": [ {"access_level": 30}, {"access_level": 40} ] }'\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches"The following example response includes:
- A protected branch with ID
105. push_access_levelswith ID1001.merge_access_levelswith IDs2001and2002.unprotect_access_levelswith ID3001.
{"id":105,"name":"main","push_access_levels":[{"id":1001,"access_level":30,"access_level_description":"Developers + Maintainers","user_id":null,"group_id":null}],"merge_access_levels":[{"id":2001,"access_level":30,"access_level_description":"Developers + Maintainers","user_id":null,"group_id":null},{"id":2002,"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}],"unprotect_access_levels":[{"id":3001,"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}],"allow_force_push":false,"code_owner_approval_required":false}Examples with unprotect access levels
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
To create a protected branch where only a specific group can unprotect the branch:
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --header"Content-Type: application/json"\ --data'{ "name": "production", "allowed_to_unprotect": [ {"group_id": 789} ] }'\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches"To allow multiple types of users to unprotect a branch:
curl --request POST\ --header"PRIVATE-TOKEN: <your_access_token>"\ --header"Content-Type: application/json"\ --data'{ "name": "main", "allowed_to_unprotect": [ {"user_id": 123}, {"group_id": 456}, {"access_level": 40} ] }'\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches"This configuration allows these users to unprotect the branch:
- The user with ID
123. - Members of the group with ID
456. - Users with at least the Maintainer role (access level 40).
Unprotect repository branches
Unprotect the given protected branch or wildcard protected branch.
DELETE /projects/:id/protected_branches/:nameSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | Yes | ID orURL-encoded path of the project. |
name | string | Yes | Name of the branch. |
If successful, returns204 No Content.
In the following example request, the project ID is5 and branch name is*-stable:
curl --request DELETE\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches/*-stable"Update a protected branch
Update a protected branch.
PATCH /projects/:id/protected_branches/:nameSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | Yes | ID orURL-encoded path of the project. |
name | string | Yes | Name of the branch or wildcard. |
allow_force_push | boolean | No | Iftrue, members who can push to this branch can also force push. |
allowed_to_merge | array | No | Array of merge access levels, with each described by a hash of the form{user_id: integer},{group_id: integer}, or{access_level: integer}. Premium and Ultimate only. |
allowed_to_push | array | No | Array of push access levels, with each described by a hash of the form{user_id: integer},{group_id: integer},{deploy_key_id: integer}, or{access_level: integer}. Premium and Ultimate only. |
allowed_to_unprotect | array | No | Array of unprotect access levels, with each described by a hash of the form{user_id: integer},{group_id: integer},{access_level: integer}, or{id: integer, _destroy: true} to destroy an existing access level. Access levelNo access is not available for this field. Premium and Ultimate only. |
code_owner_approval_required | boolean | No | Iftrue, prevents pushes to this branch if it matches an item in theCODEOWNERS file. Premium and Ultimate only. |
For information about how access levels interact when you set multiple values,seeProtect repository branches.
If successful, returns200 OK and thefollowing response attributes:
| Attribute | Type | Description |
|---|---|---|
allow_force_push | boolean | Iftrue, force push is allowed on this branch. |
code_owner_approval_required | boolean | Iftrue, code owner approval is required for pushes to this branch. |
id | integer | ID of the protected branch. |
merge_access_levels | array | Array of merge access level configurations. |
merge_access_levels[].access_level | integer | Access level for merging. |
merge_access_levels[].access_level_description | string | Human-readable description of the access level. |
merge_access_levels[].group_id | integer | ID of the group with merge access. Premium and Ultimate only. |
merge_access_levels[].id | integer | ID of the merge access level configuration. |
merge_access_levels[].user_id | integer | ID of the user with merge access. Premium and Ultimate only. |
name | string | Name of the protected branch. |
push_access_levels | array | Array of push access level configurations. |
push_access_levels[].access_level | integer | Access level for pushing. |
push_access_levels[].access_level_description | string | Human-readable description of the access level. |
push_access_levels[].deploy_key_id | integer | ID of the deploy key with push access. |
push_access_levels[].group_id | integer | ID of the group with push access. Premium and Ultimate only. |
push_access_levels[].id | integer | ID of the push access level configuration. |
push_access_levels[].user_id | integer | ID of the user with push access. Premium and Ultimate only. |
unprotect_access_levels | array | Array of unprotect access level configurations. |
unprotect_access_levels[].access_level | integer | Access level for unprotecting. |
unprotect_access_levels[].access_level_description | string | Human-readable description of the access level. |
unprotect_access_levels[].group_id | integer | ID of the group with unprotect access. Premium and Ultimate only. |
unprotect_access_levels[].id | integer | ID of the unprotect access level configuration. |
unprotect_access_levels[].user_id | integer | ID of the user with unprotect access. Premium and Ultimate only. |
Example request:
curl --request PATCH\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches/feature-branch?allow_force_push=true&code_owner_approval_required=true"Elements in theallowed_to_push,allowed_to_merge, andallowed_to_unprotect arrays shouldbe one ofuser_id,group_id, oraccess_level, and take the form{user_id: integer},{group_id: integer} or{access_level: integer}.
allowed_to_push includes an extra element,deploy_key_id, that takes the form{deploy_key_id: integer}.
To update:
user_id: Ensure the updated user has access to the project. You must also pass theidof theaccess_levelin the respective hash.group_id: Ensure the updated grouphas this project shared.You must also pass theidof theaccess_levelin the respective hash.deploy_key_id: Ensure the deploy key is enabled for your project and it must have write access to your project repository.
To delete, you must pass_destroy set totrue. See the following examples.
Example: create apush_access_level record
curl --header'Content-Type: application/json' --request PATCH\ --data'{"allowed_to_push": [{"access_level": 40}]}'\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/22034114/protected_branches/main"Example response:
{"name":"main","push_access_levels":[{"id":12,"access_level":40,"access_level_description":"Maintainers","user_id":null,"group_id":null}]}Example: update apush_access_level record
curl --header'Content-Type: application/json' --request PATCH\ --data'{"allowed_to_push": [{"id": 12, "access_level": 0}]}'\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/22034114/protected_branches/main"Example response:
{"name":"main","push_access_levels":[{"id":12,"access_level":0,"access_level_description":"No One","user_id":null,"group_id":null}]}Example: delete apush_access_level record
curl --header'Content-Type: application/json' --request PATCH\ --data'{"allowed_to_push": [{"id": 12, "_destroy": true}]}'\ --header"PRIVATE-TOKEN: <your_access_token>"\ --url"https://gitlab.example.com/api/v4/projects/22034114/protected_branches/main"Example response:
{"name":"main","push_access_levels":[]}Example: update anunprotect_access_level record
Prerequisites:
- Users calling this API must be included in the
allowed_to_unprotectconfiguration. - The user specified by
user_idmust be a project member. - Groups specified by
group_idmust have access to the project.
To modify who can unprotect an existing protected branch, include theid of the existing accesslevel record. For example:
curl --request PATCH\ --header"PRIVATE-TOKEN: <your_access_token>"\ --header"Content-Type: application/json"\ --data'{ "allowed_to_unprotect": [ {"id": 17486, "user_id": 3791} ] }'\ --url"https://gitlab.example.com/api/v4/projects/5/protected_branches/main"To remove specific access levels, use_destroy: true.