Service accounts API
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Use this API to interact with service accounts.
The number of service accounts you can create depends on your subscription and offering:
- On GitLab Premium and Ultimate, you can create an unlimited number of service accounts for all offerings.
- On GitLab Free, limits vary by offering:
- For GitLab.com, you can create up to 100 service accounts for each top-level group. This includes service accounts created in subgroups or projects.
- For GitLab Self-Managed, you can create up to 100 service accounts per instance. This includes all service accounts regardless of how they are provisioned (instance, group, or project level).
You can also interact with service accounts through the users API.
Instance service accounts
- Offering: GitLab Self-Managed, GitLab Dedicated
Instance service accounts are available to an entire GitLab instance, but must still be added to groups and projects like a human user.
To manage personal access tokens for instance service accounts, use the personal access tokens API.
Prerequisites:
- You must have administrator access to the instance.
List all instance service accounts
Lists all instance service accounts.
Use the page and per_page pagination parameters to filter the results.
GET /service_accountsSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
order_by | string | no | Attribute to order results by. Possible values: id or username. Default value: id. |
sort | string | no | Direction to sort results by. Possible values: desc or asc. Default value: desc. |
Example request:
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/service_accounts"Example response:
[
{
"id": 114,
"username": "service_account_33",
"name": "Service account user"
},
{
"id": 137,
"username": "service_account_34",
"name": "john doe"
}
]Create an instance service account
Creates an instance service account.
POST /service_accounts
POST /service_accounts?email=custom_email@gitlab.example.comSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
name | string | no | Name of the user. If not set, uses Service account user. |
username | string | no | Username of the user account. If undefined, generates a name prepended with service_account_. |
email | string | no | Email of the user account. If undefined, generates a no-reply email address. Custom email addresses require confirmation, unless the email confirmation settings are turned off. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/service_accounts"Example response:
{
"id": 57,
"username": "service_account_6018816a18e515214e0c34c2b33523fc",
"name": "Service account user",
"email": "service_account_6018816a18e515214e0c34c2b33523fc@noreply.gitlab.example.com"
}If the email address defined by the email attribute is already in use by another user,
returns a 400 Bad request error.
Update an instance service account
Updates a specified instance service account.
PATCH /service_accounts/:idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer | yes | ID of the service account. |
name | string | no | Name of the user. |
username | string | no | Username of the user account. |
email | string | no | Email of the user account. Custom email addresses require confirmation, unless the email confirmation settings are turned off. |
Example request:
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/service_accounts/57" --data "name=Updated Service Account&email=updated_email@example.com"Example response:
{
"id": 57,
"username": "service_account_6018816a18e515214e0c34c2b33523fc",
"name": "Updated Service Account",
"email": "service_account_<random_hash>@noreply.gitlab.example.com",
"unconfirmed_email": "custom_email@example.com"
}Group service accounts
Group service accounts are owned by a specific group and can be invited to the group where they were created or to any descendant subgroups or projects. They cannot be invited to ancestor groups.
Prerequisites:
- On GitLab.com, you must have the Owner role for the group.
- On GitLab Self-Managed or GitLab Dedicated, you must either:
- Be an administrator for the instance.
- Have the Owner role in a group and be allowed to create service accounts.
List all group service accounts
Lists all service accounts in a specified group.
Use the page and per_page pagination parameters to filter the results.
GET /groups/:id/service_accountsParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target group. |
order_by | string | no | Orders list of users by username or id. Default is id. |
sort | string | no | Specifies sorting by asc or desc. Default is desc. |
Example request:
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/345/service_accounts"Example response:
[
{
"id": 57,
"username": "service_account_group_345_<random_hash>",
"name": "Service account user",
"email": "service_account_group_345_<random_hash>@noreply.gitlab.example.com"
},
{
"id": 58,
"username": "service_account_group_345_<random_hash>",
"name": "Service account user",
"email": "service_account_group_345_<random_hash>@noreply.gitlab.example.com",
"unconfirmed_email": "custom_email@example.com"
}
]Create a group service account
Creates a service account in a specified group.
POST /groups/:id/service_accountsSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a group. |
name | string | no | User account name. If not specified, uses Service account user. |
username | string | no | User account username. If not specified, generates a name prepended with service_account_group_. |
email | string | no | Email of the user account. If not specified, generates an email prepended with service_account_group_. Custom email addresses require confirmation, unless the group has a matching verified domain or email confirmation settings are turned off. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/345/service_accounts" --data "email=custom_email@example.com"Example response:
{
"id": 57,
"username": "service_account_group_345_6018816a18e515214e0c34c2b33523fc",
"name": "Service account user",
"email": "custom_email@example.com"
}Update a group service account
Updates a service account in a specified group.
- You cannot update the username of a service account associated with a composite identity.
PATCH /groups/:id/service_accounts/:user_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target group. |
user_id | integer | yes | The ID of the service account. |
name | string | no | Name of the user. |
username | string | no | Username of the user. |
email | string | no | Email of the user account. Custom email addresses require confirmation, unless the group has a matching verified domain or email confirmation settings are turned off. |
Example request:
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/345/service_accounts/57" --data "name=Updated Service Account&email=updated_email@example.com"Example response:
{
"id": 57,
"username": "service_account_group_345_6018816a18e515214e0c34c2b33523fc",
"name": "Updated Service Account",
"email": "service_account_group_345_<random_hash>@noreply.gitlab.example.com",
"unconfirmed_email": "custom_email@example.com"
}Delete a group service account
Deletes a service account from a specified group.
DELETE /groups/:id/service_accounts/:user_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target group. |
user_id | integer | yes | The ID of a service account. |
hard_delete | boolean | no | If true, contributions that would usually be moved to a ghost user are instead deleted, as well as groups owned solely by this service account. |
Example request:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/345/service_accounts/181"List all personal access tokens for a group service account
Lists all personal access tokens for a service account in a specified group.
GET /groups/:id/service_accounts/:user_id/personal_access_tokensSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a group. |
user_id | integer | yes | ID of service account. |
created_after | datetime (ISO 8601) | no | If defined, returns tokens created after the specified time. |
created_before | datetime (ISO 8601) | no | If defined, returns tokens created before the specified time. |
expires_after | date (ISO 8601) | no | If defined, returns tokens that expire after the specified time. |
expires_before | date (ISO 8601) | no | If defined, returns tokens that expire before the specified time. |
last_used_after | datetime (ISO 8601) | no | If defined, returns tokens last used after the specified time. |
last_used_before | datetime (ISO 8601) | no | If defined, returns tokens last used before the specified time. |
revoked | boolean | no | If true, only returns revoked tokens. |
search | string | no | If defined, returns tokens that include the specified value in the name. |
sort | string | no | If defined, sorts the results by the specified value. Possible values: created_asc, created_desc, expires_asc, expires_desc, last_used_asc, last_used_desc, name_asc, name_desc. |
state | string | no | If defined, returns tokens with the specified state. Possible values: active and inactive. |
Example request:
curl --request GET \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/groups/187/service_accounts/195/personal_access_tokens?sort=id_desc&search=token2b&created_before=2025-03-27"Example response:
[
{
"id": 187,
"name": "service_accounts_token2b",
"revoked": false,
"created_at": "2025-03-26T14:42:51.084Z",
"description": null,
"scopes": [
"api"
],
"user_id": 195,
"last_used_at": null,
"active": true,
"expires_at": null
}
]Example of unsuccessful responses:
401: Unauthorized404 Group Not Found
Create a personal access token for a group service account
Creates a personal access token for an existing service account in a specified group.
POST /groups/:id/service_accounts/:user_id/personal_access_tokensParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a group. |
user_id | integer | yes | ID of service account. |
name | string | yes | Name of personal access token. |
description | string | no | Description of personal access token. |
scopes | array | yes | Array of approved scopes. For a list of possible values, see Personal access token scopes. |
expires_at | date | no | Expiration date of the access token in ISO format (YYYY-MM-DD). If not specified, the date is set to the maximum allowable lifetime limit. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/35/service_accounts/71/personal_access_tokens" --data "scopes[]=api,read_user,read_repository" --data "name=service_accounts_token"Example response:
{
"id":6,
"name":"service_accounts_token",
"revoked":false,
"created_at":"2023-06-13T07:47:13.900Z",
"scopes":["api"],
"user_id":71,
"last_used_at":null,
"active":true,
"expires_at":"2024-06-12",
"token":"<token_value>"
}Revoke a personal access token for a group service account
Revokes a specified personal access token for an existing service account in a group.
DELETE /groups/:id/service_accounts/:user_id/personal_access_tokens/:token_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target group. |
user_id | integer | yes | The ID of the service account. |
token_id | integer | yes | The ID of the token. |
Example request:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/35/service_accounts/71/personal_access_tokens/6"If successful, returns 204: No Content.
Other possible responses:
400: Bad Requestif not revoked successfully.401: Unauthorizedif the request is not authorized.403: Forbiddenif the request is not allowed.404: Not Foundif the access token does not exist.
Rotate a personal access token for a group service account
Rotates a specified personal access token for an existing service account in a specified group. This revokes the existing token and creates a new token with the same name, description, and scopes.
POST /groups/:id/service_accounts/:user_id/personal_access_tokens/:token_id/rotateParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target group. |
user_id | integer | yes | The ID of the service account. |
token_id | integer | yes | The ID of the token. |
expires_at | date | no | Expiration date of the access token in ISO format (YYYY-MM-DD). Introduced in GitLab 17.9. If the token requires an expiration date, defaults to one week. If not required, defaults to the maximum allowable lifetime limit. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/35/service_accounts/71/personal_access_tokens/6/rotate"Example response:
{
"id":7,
"name":"service_accounts_token",
"revoked":false,
"created_at":"2023-06-13T07:54:49.962Z",
"scopes":["api"],
"user_id":71,
"last_used_at":null,
"active":true,
"expires_at":"2023-06-20",
"token":"<token_value>"
}Project service accounts
Project service accounts are owned by a specific project and are available only to their associated project.
Prerequisites:
- On GitLab.com, you must have the Owner or Maintainer role for the project.
- On GitLab Self-Managed or GitLab Dedicated, you must either:
- Be an administrator for the instance.
- Have the Owner or Maintainer role in a project.
List all project service accounts
Lists all service accounts in a specified project.
Use the page and per_page pagination parameters to filter the results.
GET /projects/:id/service_accountsParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target project. |
order_by | string | no | Orders list of users by username or id. Default is id. |
sort | string | no | Specifies sorting by asc or desc. Default is desc. |
Example request:
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/345/service_accounts"Example response:
[
{
"id": 57,
"username": "service_account_project_345_<random_hash>",
"name": "Service account user",
"email": "service_account_project_345_<random_hash>@noreply.gitlab.example.com"
},
{
"id": 58,
"username": "service_account_project_345_<random_hash>",
"name": "Service account user",
"email": "service_account_project_345_<random_hash>@noreply.gitlab.example.com",
"unconfirmed_email": "custom_email@example.com"
}
]Create a project service account
Creates a service account in a specified project.
POST /projects/:id/service_accountsSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a project |
name | string | no | User account name. If not specified, uses Service account user. |
username | string | no | User account username. If not specified, generates a name prepended with service_account_project_. |
email | string | no | Email of the user account. If not specified, generates an email prepended with service_account_project_. Custom email addresses require confirmation, unless the group has a matching verified domain or email confirmation settings are turned off. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/345/service_accounts" --data "email=custom_email@example.com"Example response:
{
"id": 57,
"username": "service_account_project_345_6018816a18e515214e0c34c2b33523fc",
"name": "Service account user",
"email": "custom_email@example.com"
}Update a project service account
Updates a service account in a specified project.
PATCH /projects/:id/service_accounts/:user_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target project. |
user_id | integer | yes | The ID of the service account. |
name | string | no | Name of the user. |
username | string | no | Username of the user. |
email | string | no | Email of the user account. Custom email addresses require confirmation, unless the group has a matching verified domain or email confirmation settings are turned off. |
Example request:
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/345/service_accounts/57" --data "name=Updated Service Account&email=updated_email@example.com"Example response:
{
"id": 57,
"username": "service_account_project_345_6018816a18e515214e0c34c2b33523fc",
"name": "Updated Service Account",
"email": "service_account_project_345_<random_hash>@noreply.gitlab.example.com",
"unconfirmed_email": "custom_email@example.com"
}Delete a project service account
Deletes a service account from a specified project.
DELETE /projects/:id/service_accounts/:user_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target project. |
user_id | integer | yes | The ID of a service account. |
hard_delete | boolean | no | If true, contributions that would usually be moved to a ghost user are instead deleted, as well as groups owned solely by this service account. |
Example request:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/345/service_accounts/181"List all personal access tokens for a project service account
Lists all personal access tokens for a service account in a project.
GET /projects/:id/service_accounts/:user_id/personal_access_tokensSupported attributes:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a project. |
user_id | integer | yes | ID of service account. |
created_after | datetime (ISO 8601) | no | If defined, returns tokens created after the specified time. |
created_before | datetime (ISO 8601) | no | If defined, returns tokens created before the specified time. |
expires_after | date (ISO 8601) | no | If defined, returns tokens that expire after the specified time. |
expires_before | date (ISO 8601) | no | If defined, returns tokens that expire before the specified time. |
last_used_after | datetime (ISO 8601) | no | If defined, returns tokens last used after the specified time. |
last_used_before | datetime (ISO 8601) | no | If defined, returns tokens last used before the specified time. |
revoked | boolean | no | If true, only returns revoked tokens. |
search | string | no | If defined, returns tokens that include the specified value in the name. |
sort | string | no | If defined, sorts the results by the specified value. Possible values: created_asc, created_desc, expires_asc, expires_desc, last_used_asc, last_used_desc, name_asc, name_desc. |
state | string | no | If defined, returns tokens with the specified state. Possible values: active and inactive. |
Example request:
curl --request GET \
--header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/187/service_accounts/195/personal_access_tokens?sort=id_desc&search=token2b&created_before=2025-03-27"Example response:
[
{
"id": 187,
"name": "service_accounts_token2b",
"revoked": false,
"created_at": "2025-03-26T14:42:51.084Z",
"description": null,
"scopes": [
"api"
],
"user_id": 195,
"last_used_at": null,
"active": true,
"expires_at": null
}
]Example of unsuccessful responses:
401: Unauthorized404 Project Not Found
Create a personal access token for a project service account
Creates a personal access token for an existing service account in a specified project.
POST /projects/:id/service_accounts/:user_id/personal_access_tokensParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | ID or URL-encoded path of a project. |
user_id | integer | yes | ID of service account. |
name | string | yes | Name of personal access token. |
description | string | no | Description of personal access token. |
scopes | array | yes | Array of approved scopes. For a list of possible values, see Personal access token scopes. |
expires_at | date | no | Expiration date of the access token in ISO format (YYYY-MM-DD). If not specified, the date is set to the maximum allowable lifetime limit. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/35/service_accounts/71/personal_access_tokens" --data "scopes[]=api,read_user,read_repository" --data "name=service_accounts_token"Example response:
{
"id":6,
"name":"service_accounts_token",
"revoked":false,
"created_at":"2023-06-13T07:47:13.900Z",
"scopes":["api"],
"user_id":71,
"last_used_at":null,
"active":true,
"expires_at":"2024-06-12",
"token":"<token_value>"
}Revoke a personal access token for a project service account
Revokes a personal access token for an existing service account in a specified project.
DELETE /projects/:id/service_accounts/:user_id/personal_access_tokens/:token_idParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target project. |
user_id | integer | yes | The ID of the service account. |
token_id | integer | yes | The ID of the token. |
Example request:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/35/service_accounts/71/personal_access_tokens/6"If successful, returns 204: No Content.
Other possible responses:
400: Bad Requestif not revoked successfully.401: Unauthorizedif the request is not authorized.403: Forbiddenif the request is not allowed.404: Not Foundif the access token does not exist.
Rotate a personal access token for a project service account
Rotates a personal access token for an existing service account in a specified project. This creates a new token valid for one week and revokes any existing tokens.
POST /projects/:id/service_accounts/:user_id/personal_access_tokens/:token_id/rotateParameters:
| Attribute | Type | Required | Description |
|---|---|---|---|
id | integer or string | yes | The ID or URL-encoded path of the target project. |
user_id | integer | yes | The ID of the service account. |
token_id | integer | yes | The ID of the token. |
expires_at | date | no | Expiration date of the access token in ISO format (YYYY-MM-DD). Introduced in GitLab 17.9. If the token requires an expiration date, defaults to one week. If not required, defaults to the maximum allowable lifetime limit. |
Example request:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/35/service_accounts/71/personal_access_tokens/6/rotate"Example response:
{
"id":7,
"name":"service_accounts_token",
"revoked":false,
"created_at":"2023-06-13T07:54:49.962Z",
"scopes":["api"],
"user_id":71,
"last_used_at":null,
"active":true,
"expires_at":"2023-06-20",
"token":"<token_value>"
}