Group-level protected branches API
- Tier: Premium, Ultimate
- Offering: GitLab Self-Managed
Version history
- Generally available in GitLab 17.6. Feature flag
group_protected_branchesremoved.
Use this API to manage protected branch settings that are inherited by all projects in a group. Group protected branches only support valid access levels and, on GitLab Ultimate, custom roles. Individual users and groups cannot be specified.
Warning
Protected branch settings for groups are restricted to top-level groups only.
Valid access levels
The access levels are defined in the ProtectedRefAccess.allowed_access_levels method.
These levels are recognized:
0 => No access
30 => Developer access
40 => Maintainer access
60 => Admin accessList protected branches
Gets a list of protected branches from a group. If a wildcard is set, it is returned instead of the exact name of the branches that match that wildcard.
GET /groups/:id/protected_branches| Attribute | Type | Required | Description |
|---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the group. |
search |
string | no | Name or part of the name of protected branches to be searched for. |
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches"Example response:
[
{
"id": 1,
"name": "main",
"push_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"allow_force_push":false,
"code_owner_approval_required": false
},
{
"id": 1,
"name": "release/*",
"push_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"allow_force_push":false,
"code_owner_approval_required": false
},
...
]Get a single protected branch or wildcard protected branch
Gets a single protected branch or wildcard protected branch.
GET /groups/:id/protected_branches/:name| Attribute | Type | Required | Description |
|---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the group. |
name |
string | yes | The name of the branch or wildcard. |
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches/main"Example response:
{
"id": 1,
"name": "main",
"push_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"allow_force_push":false,
"code_owner_approval_required": false
}Protect repository branches
Version history
member_role_idparameter introduced in GitLab 19.2 with a feature flag namedcustom_roles_for_protected_branches. Disabled by default.- Generally available in GitLab 19.4. Feature flag
custom_roles_for_protected_branchesremoved.
Protects a single repository branch using a wildcard protected branch.
POST /groups/:id/protected_branchescurl --request POST \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches?name=*-stable&push_access_level=30&merge_access_level=30&unprotect_access_level=40"| Attribute | Type | Required | Description |
|---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the group. |
name |
string | yes | The name of the branch or wildcard. |
allow_force_push |
boolean | no | Allow all users with push access to force push. Default: false. |
allowed_to_merge |
array | no | Array of access levels allowed to merge, with each described by a hash of the form {access_level: integer} or {member_role_id: integer}. member_role_id is Ultimate only. |
allowed_to_push |
array | no | Array of access levels allowed to push, with each described by a hash of the form {access_level: integer} or {member_role_id: integer}. member_role_id is Ultimate only. |
allowed_to_unprotect |
array | no | Array of access levels allowed to unprotect, with each described by a hash of the form {access_level: integer} or {member_role_id: integer}. member_role_id is Ultimate only. |
code_owner_approval_required |
boolean | no | Prevent pushes to this branch if it matches an item in the CODEOWNERS file. Default: false. |
merge_access_level |
integer | no | Access levels allowed to merge. Defaults: 40, Maintainer role. Does not support custom roles. Use allowed_to_merge with member_role_id instead. |
push_access_level |
integer | no | Access levels allowed to push. Defaults: 40, Maintainer role. Does not support custom roles. Use allowed_to_push with member_role_id instead. |
unprotect_access_level |
integer | no | Access levels allowed to unprotect. Defaults: 40, Maintainer role. Does not support custom roles. Use allowed_to_unprotect with member_role_id instead. |
Example response:
{
"id": 1,
"name": "*-stable",
"push_access_levels": [
{
"id": 1,
"access_level": 30,
"user_id": null,
"group_id": null,
"access_level_description": "Developers + Maintainers"
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 30,
"user_id": null,
"group_id": null,
"access_level_description": "Developers + Maintainers"
}
],
"unprotect_access_levels": [
{
"id": 1,
"access_level": 40,
"user_id": null,
"group_id": null,
"access_level_description": "Maintainers"
}
],
"allow_force_push":false,
"code_owner_approval_required": false
}Example with access levels
Use access levels to configure group protected branches:
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/groups/5/protected_branches"Example response:
{
"id": 5,
"name": "main",
"push_access_levels": [
{
"id": 1,
"access_level": 30,
"access_level_description": "Developers + Maintainers",
"user_id": null,
"group_id": null
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 30,
"access_level_description": "Developers + Maintainers",
"user_id": null,
"group_id": null
},
{
"id": 2,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null
}
],
"unprotect_access_levels": [
{
"id": 1,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null
}
],
"allow_force_push":false,
"code_owner_approval_required": false
}Example with a custom role
- Tier: Ultimate
Use member_role_id to allow a custom role to merge to a group protected branch.
The custom role must belong to the same top-level group as the protected branch.
curl --request POST \
--header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-Type: application/json" \
--data '{
"name": "main",
"allowed_to_merge": [{"member_role_id": 42}],
"push_access_level": 40
}' \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches"Example response:
{
"id": 5,
"name": "main",
"push_access_levels": [
{
"id": 1,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null
}
],
"merge_access_levels": [
{
"id": 1,
"access_level": 30,
"access_level_description": "Lead Developer",
"user_id": null,
"group_id": null,
"member_role_id": 42,
"member_role_name": "Lead Developer"
}
],
"allow_force_push":false,
"code_owner_approval_required": false
}Unprotect repository branches
Unprotects the given protected branch or wildcard protected branch.
DELETE /groups/:id/protected_branches/:namecurl --request DELETE \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches/*-stable"| Attribute | Type | Required | Description |
|---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the group. |
name |
string | yes | The name of the branch. |
Example response:
{
"name": "main",
"push_access_levels": [
{
"id": 12,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null
}
]
}Update a protected branch
Version history
member_role_idparameter introduced in GitLab 19.2 with a feature flag namedcustom_roles_for_protected_branches. Disabled by default.- Generally available in GitLab 19.5. Feature flag
custom_roles_for_protected_branchesremoved.
Updates a protected branch.
PATCH /groups/:id/protected_branches/:namecurl --request PATCH \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches/feature-branch?allow_force_push=true&code_owner_approval_required=true"| Attribute | Type | Required | Description |
|---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the group. |
name |
string | yes | The name of the branch. |
allow_force_push |
boolean | no | When enabled, members who can push to this branch can also force push. |
allowed_to_push |
array | no | Array of push access levels, with each described by a hash. |
allowed_to_merge |
array | no | Array of merge access levels, with each described by a hash. |
allowed_to_unprotect |
array | no | Array of unprotect access levels, with each described by a hash. |
code_owner_approval_required |
boolean | no | Prevent pushes to this branch if it matches an item in the CODEOWNERS file. Default: false. |
Elements in the allowed_to_push, allowed_to_merge, and allowed_to_unprotect arrays should take the form
{access_level: integer} or, on GitLab Ultimate, {member_role_id: integer} to allow a
custom role.
Each access level must be a valid value from the valid access levels.
The custom role referenced by member_role_id must belong to the same top-level group as the protected branch.
- To update access levels, you must also pass the
idof theaccess_levelin the respective hash. - To delete access levels, you must pass
_destroyset totrue. See the following examples.
Example: create a push_access_level record
curl --request PATCH \
--header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-Type: application/json" \
--data '{"allowed_to_push": [{"access_level": 40}]}' \
--url "https://gitlab.example.com/api/v4/groups/5/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 a push_access_level record
curl --request PATCH \
--header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-Type: application/json" \
--data '{"allowed_to_push": [{"id": 12, "access_level": 0}]}' \
--url "https://gitlab.example.com/api/v4/groups/5/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 a push_access_level record
curl --request PATCH \
--header "PRIVATE-TOKEN: <your_access_token>" \
--header "Content-Type: application/json" \
--data '{"allowed_to_push": [{"id": 12, "_destroy": true}]}' \
--url "https://gitlab.example.com/api/v4/groups/5/protected_branches/main"Example response:
{
"name": "main",
"push_access_levels": []
}