Protected branches API
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Use this API to manage branch protections for your repositories.
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
The ProtectedRefAccess.allowed_access_levels
method defines the following access levels:
-
0
: No access -
30
: Developer role -
40
: Maintainer role -
60
: Administrator
List protected branches
Version history
- Deploy key information introduced in GitLab 16.0.
Gets a list of protected branches from a project as they are defined in the UI. If a wildcard is set, it is returned instead of the exact name of the branches that match that wildcard.
GET /projects/:id/protected_branches
Attribute | Type | Required | Description |
---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the project |
search |
string | no | Name or part of the name of protected branches to be searched for |
In the following example, the project ID is 5
.
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
100
and101
. -
push_access_levels
with IDs1001
,1002
, and1003
. -
merge_access_levels
with IDs2001
and2002
.
[
{
"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 see
the user_id
, group_id
and inherited
parameters. If the inherited
parameter
exists, means the setting was inherited from the project's group.
The following example response includes:
- One protected branch with ID
100
. -
push_access_levels
with IDs1001
and1002
. -
merge_access_levels
with 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
Gets a single protected branch or wildcard protected branch.
GET /projects/:id/protected_branches/:name
Attribute | Type | Required | Description |
---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the project |
name |
string | yes | The name of the branch or wildcard |
In the following example, the project ID is 5
and branch name is main
:
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 see
the user_id
and group_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
Version history
-
deploy_key_id
configuration introduced in GitLab 17.5.
Protects a single repository branch or several project repository branches using a wildcard protected branch.
POST /projects/:id/protected_branches
Attribute | Type | Required | Description |
---|---|---|---|
id |
integer or string | yes | The ID or URL-encoded path of the project. |
name |
string | yes | The name of the branch or wildcard. |
allow_force_push |
boolean | no | When enabled, members who can push to this branch can also force push. (default: false ) |
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} . The access level No access is not available for this field. Premium and Ultimate only. |
code_owner_approval_required |
boolean | no | Prevent pushes to this branch if it matches an item in the CODEOWNERS file. (defaults: false) Premium and Ultimate only. |
merge_access_level |
integer | no | Access levels allowed to merge. (defaults: 40 , Maintainer role). |
push_access_level |
integer | no | Access levels allowed to push. (defaults: 40 , Maintainer role) |
unprotect_access_level |
integer | no | Access levels allowed to unprotect. (defaults: 40 , Maintainer role) |
In the following example, the project ID is 5
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_levels
with ID1001
. -
merge_access_levels
with ID2001
. -
unprotect_access_levels
with ID3001
.
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```0
Users on GitLab Premium or Ultimate also see
the `user_id` and `group_id` parameters:
The following example response includes:
- A protected branch with ID `101`.
- `push_access_levels` with ID `1001`.
- `merge_access_levels` with ID `2001`.
- `unprotect_access_levels` with ID `3001`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```1
### Example with user push access and group merge access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Elements in the `allowed_to_push` / `allowed_to_merge` / `allowed_to_unprotect` array should take the
form `{user_id: integer}`, `{group_id: integer}`, or `{access_level: integer}`.
Each user must have access to the project and each group must [have this project shared](../user/project/members/sharing_projects_groups.md).
These access levels allow more granular control over protected branch access.
For more information, see [configure group permissions](../user/project/repository/branches/protected.md#with-group-permissions).
The following example request creates a protected branch with user push access and group merge access.
The `user_id` is `2` and the `group_id` is `3`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```2
The following example response includes:
- A protected branch with ID `101`.
- `push_access_levels` with ID `1001`.
- `merge_access_levels` with ID `2001`.
- `unprotect_access_levels` with ID `3001`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```3
### Example with deploy key access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
#### Version history
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166598) in GitLab 17.5.
Elements in the `allowed_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, see [Allow deploy keys to push to a protected branch](../user/project/repository/branches/protected.md#enable-deploy-key-access).
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```4
The following example response includes:
- An protected branch with ID `101`.
- `push_access_levels` with ID `1001`.
- `merge_access_levels` with ID `2001`.
- `unprotect_access_levels` with ID `3001`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```5
### Example with allow to push and allow to merge access
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
#### Version history
- Moved to GitLab Premium in 13.9.
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```6
The following example response includes:
- A protected branch with ID `105`.
- `push_access_levels` with ID `1001`.
- `merge_access_levels` with IDs `2001` and `2002`.
- `unprotect_access_levels` with ID `3001`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```7
## Unprotect repository branches
Unprotects the given protected branch or wildcard protected branch.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```8
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths) |
| `name` | string | yes | The name of the branch |
In the following example, the project ID is `5` and branch name is `*-stable`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/5/protected_branches"
```9
## Update a protected branch
### Version history
- `deploy_key_id` configuration [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166598) in GitLab 17.5.
Updates a protected branch.
```json
[
{
"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
},
...
]
```0
| Attribute | Type | Required | Description |
| -------------------------------------------- | ---- | -------- | ----------- |
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/_index.md#namespaced-paths). |
| `name` | string | yes | The name of the branch or wildcard. |
| `allow_force_push` | boolean | no | When enabled, 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. The access level `No access` is not available for this field. Premium and Ultimate only. |
| `code_owner_approval_required` | boolean | no | Prevent pushes to this branch if it matches an item in the [`CODEOWNERS` file](../user/project/codeowners/_index.md). Premium and Ultimate only. |
In the following example, the project ID is `5` and branch name is `feature-branch`.
```json
[
{
"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
},
...
]
```1
Elements in the `allowed_to_push`, `allowed_to_merge` and `allowed_to_unprotect` arrays should be one of `user_id`, `group_id` or
`access_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 the
`id` of the `access_level` in the respective hash.
- `group_id`: Ensure the updated group [has this project shared](../user/project/members/sharing_projects_groups.md).
You must also pass the `id` of the `access_level` in 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 to `true`. See the following examples.
### Example: create a `push_access_level` record
```json
[
{
"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
},
...
]
```2
Example response:
```json
[
{
"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
},
...
]
```3
### Example: update a `push_access_level` record
```json
[
{
"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
},
...
]
```4
Example response:
```json
[
{
"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
},
...
]
```5
### Example: delete a `push_access_level` record
```json
[
{
"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
},
...
]
```6
Example response:
```json
[
{
"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
},
...
]
```7
## Related topics
- [Protected branches](../user/project/repository/branches/protected.md)
- [Branches](../user/project/repository/branches/_index.md)
- [Branches API](branches.md)