GitLab
The GitLab integration lets Helios agents search and call the GitLab REST API on GitLab.com or a self-managed instance, and run agents automatically when something happens in your projects or groups, such as a push, a new merge request, a failed pipeline, or a comment.
What your agents can do
GitLab exposes a REST API (v4). Agents can call GitLab REST API endpoints with the connected user's permissions. The credential is attached server-side.
- Open merge requests, read their diffs, and leave review comments
- Create and label issues, and reply in their threads
- Check pipeline and job status, read a failed job's log, and retry jobs
- Read files, branches, tags, and commits, and download job artifacts
- Create releases, milestones, and wiki pages
- Upload files, such as an image to show in an issue
The integration works with GitLab.com and GitLab Self-Managed. Agents can only call the GitLab instance the connection points to. Helios doesn't index GitLab's GraphQL API, so agents won't find it when they search. They can still send a query to /api/graphql on the same instance, for features that only GraphQL offers, such as parts of work items.
Authentication
| Method | Type | Description |
|---|---|---|
| GitLab Access Token | API Key | A personal, group, or project access token with the api scope |
| Your own GitLab OAuth app | OAuth 2.0 | An OAuth application you register in GitLab, with the api and mcp scopes |
Both methods work with GitLab Self-Managed. Open Advanced Configuration in the connect form and set Base URL to your instance's address, such as https://gitlab.example.com, before you save. Leave it empty for GitLab.com.
Both methods also have an optional Group setting for events that only group webhooks send. See Group events.
See Integration authentication for how credentials are stored and injected.
GitLab Access Token
You need the Maintainer or Owner role in the projects whose events should start runs. Group and project access tokens need GitLab Premium or Ultimate on GitLab.com. On GitLab Self-Managed they work with any license.
- Create the token in GitLab:
- Personal access token: select your avatar, then Edit profile → Access → Personal access tokens. Select Generate token, then Legacy token.
- Group or project access token: open the group or project, then Settings → Access tokens, and select Add new token. Give the token the Maintainer role, or Owner for a group token that should add a group webhook.
- Enter a name and an expiration date, select the
apiscope, and create the token. - In Helios, choose GitLab Access Token and paste the token into GitLab Access Token.
- Optional: enter a group's full path, such as
acme/platform, in Group. - For GitLab Self-Managed, set Base URL under Advanced Configuration.
- Save.
The api scope gives full read and write access to the REST API, which agents need for merge requests, issues, pipelines, and job logs. Helios also uses it to add the webhooks for triggers.
On GitLab Self-Managed, an administrator's personal access token can also have admin_mode, for admin endpoints when Admin Mode is on, and sudo, for calls made as another user. Access tokens can't have the mcp scope, so agents connected with a token can't call GitLab's MCP server at /api/v4/mcp. Access to other endpoints depends on the token's permissions and the instance's settings.
An access token expires on its expiration date, at most 365 days after it's created by default. Before it expires, create a new one and enter it through Manage.
GitLab records every change made with the token as the token's user. A group or project access token, or a personal token of a separate GitLab user, keeps the agent's changes apart from yours and limits the projects it can reach.
Your own GitLab OAuth app
Any GitLab user can register an application for their own account. A group Owner can register one for the group.
-
In GitLab, select your avatar, then Edit profile → Access → Applications, and select Add new application. For a group application, open the group's Settings → Applications instead.
-
Enter a Name and set Redirect URI to:
https://api.heliosapp.com/integrations/gitlab/oauth_callback -
Select the
apiandmcpscopes. If you will turn on Admin access in Helios, also selectadmin_modeandsudo. If the form shows Confidential, leave it selected. Then select Save application. -
In Helios, choose Your own GitLab OAuth app and enter the Application ID and Secret.
-
Optional: enter a group's full path in Group, and select Admin access to ask for the admin scopes.
-
For GitLab Self-Managed, set Base URL under Advanced Configuration. The application must be registered on that instance.
-
Save, then approve access in GitLab.
Helios asks for these scopes. GitLab rejects the sign-in when the application lacks one of them, so select all of them in step 3.
| Scope | What it's for |
|---|---|
api | Full read and write access to the REST API, and the webhooks for triggers |
mcp | GitLab's MCP server at /api/v4/mcp, which api doesn't cover |
Admin access adds these scopes:
| Scope | What it's for |
|---|---|
admin_mode | Admin endpoints when Admin Mode is on |
sudo | Calls made as another user |
The admin scopes only work for GitLab administrators, so they're useful on GitLab Self-Managed. GitLab grants them to other users too, but they don't allow anything more.
Helios doesn't ask for GitLab's other scopes. Through the REST API, api already allows what they allow, except k8s_proxy. That scope works only with the Kubernetes proxy of the GitLab agent for Kubernetes, which agents can't reach.
Agents act as the user who approved access. GitLab access tokens expire after two hours, and Helios renews them with the refresh token. If you renew the application's secret in GitLab, enter the new one through Manage, then click Re-connect. To change the scopes, edit the application in GitLab, then click Re-connect.
Triggers work the same way as with an access token, for the projects where the approving user has at least the Maintainer role.
Triggers
GitLab can start a Workflow when something happens in your projects or groups. When you save the connection, Helios adds a webhook named Helios to each project where the credential has at least the Maintainer role. It processes projects most recently active first and skips archived projects. Helios checks again once a day, so new projects get the webhook too. There is nothing to paste into GitLab. Deleting the connection removes the webhooks.
Each webhook subscribes to every event below that it can send, including events on confidential issues and comments.
Triggers need GitLab 19.0 or later, because Helios signs each webhook with a signing token. On older self-managed instances, agents can still call the API, but no webhooks are added. To leave a project out, use a credential that doesn't have the Maintainer role in that project.
Group events
Member, project, and subgroup events, and events for epics, come only from a group webhook. Group webhooks need GitLab Premium or Ultimate and the Owner role in the group. On GitLab.com Free, GitLab saves a group webhook but never sends its events, so Helios doesn't add one there and keeps the project webhooks.
To receive them, enter the group's full path, such as acme or acme/platform, in Group on the connection. Helios then adds a Helios webhook to the group with the supported events turned on. A group webhook also sends the events of every project in the group and its subgroups, except deploy token events. So in those projects, Helios keeps its project webhook for deploy token events only. This avoids subscribing both webhooks to the same project events.
Without Group, or when GitLab refuses the group webhook, the trigger picker shows the member, project, and subgroup events as unavailable on the connection. When you change or clear Group, Helios removes its webhook from the other groups where you have the Owner role.
Events
| Event | Description | Filters |
|---|---|---|
branch.pushed | Commits were pushed to a branch, or a branch was created or deleted | project, branch (pattern) |
tag.created | A tag was created | project, tag (pattern) |
tag.deleted | A tag was deleted | project, tag (pattern) |
merge_request.opened | A merge request was opened | project, target branch (pattern), label (pattern) |
merge_request.updated | A merge request was edited, or commits were added to its source branch | project, target branch (pattern), label (pattern), changed |
merge_request.approval_added | A user approved a merge request | project, target branch (pattern), label (pattern) |
merge_request.approval_removed | A user removed their approval from a merge request | project, target branch (pattern), label (pattern) |
merge_request.approved | A merge request was approved by all required approvers | project, target branch (pattern), label (pattern) |
merge_request.unapproved | A merge request lost its approved status | project, target branch (pattern), label (pattern) |
merge_request.merged | A merge request was merged | project, target branch (pattern), label (pattern) |
merge_request.closed | A merge request was closed | project, target branch (pattern), label (pattern) |
merge_request.reopened | A merge request was reopened | project, target branch (pattern), label (pattern) |
issue.opened | An issue was opened | project, label (pattern) |
issue.updated | An issue was edited | project, label (pattern), changed |
issue.closed | An issue was closed | project, label (pattern) |
issue.reopened | An issue was reopened | project, label (pattern) |
work_item.opened | A task, incident, epic or other work item that is not an issue was created | project, type, label (pattern) |
work_item.updated | A work item that is not an issue was edited | project, type, label (pattern), changed |
work_item.closed | A work item that is not an issue was closed | project, type, label (pattern) |
work_item.reopened | A work item that is not an issue was reopened | project, type, label (pattern) |
comment.created | A comment was added to a merge request, issue, commit or snippet | project, comment on |
comment.updated | A comment on a merge request, issue, commit or snippet was edited | project, comment on |
wiki_page.created | A wiki page was created | project |
wiki_page.updated | A wiki page was edited | project |
wiki_page.deleted | A wiki page was deleted | project |
pipeline.updated | A pipeline's status changed, for example to failed | project, status (required), branch (pattern) |
job.updated | A job's status changed, for example to failed | project, status (required), branch (pattern), job name (pattern) |
deployment.started | A deployment started | project, environment (pattern) |
deployment.succeeded | A deployment succeeded | project, environment (pattern) |
deployment.failed | A deployment failed | project, environment (pattern) |
deployment.canceled | A deployment was canceled | project, environment (pattern) |
deployment.blocked | A deployment is waiting for an approval or a manual action | project, environment (pattern) |
deployment.approved | An approver approved a deployment | project, environment (pattern) |
deployment.rejected | An approver rejected a deployment | project, environment (pattern) |
release.created | A release was created | project, tag (pattern) |
release.updated | A release was edited | project, tag (pattern) |
release.deleted | A release was deleted | project, tag (pattern) |
milestone.created | A milestone was created | project |
milestone.closed | A milestone was closed | project |
milestone.reopened | A milestone was reopened | project |
milestone.deleted | A milestone was deleted | project |
feature_flag.enabled | A feature flag was turned on | project |
feature_flag.disabled | A feature flag was turned off | project |
emoji.awarded | An emoji reaction was added to an issue, merge request, snippet, wiki page or comment | project, emoji (pattern) |
emoji.revoked | An emoji reaction was removed | project, emoji (pattern) |
access_token.expiring | A project or group access token expires in 7 days, or in 30 or 60 days when the project or group turns those notices on | project |
deploy_token.expiring | A project deploy token expires in 7, 30 or 60 days | project |
vulnerability.updated | A vulnerability was created, its status changed, or an issue was linked to it | project, severity, state |
member.added | A user was added to the group | None |
member.updated | A group member's role or access expiration date changed | None |
member.removed | A user was removed from the group | None |
member.access_requested | A user requested access to the group | None |
member.access_request_denied | A request for access to the group was denied | None |
project.created | A project was created in the group | None |
project.deleted | A project was deleted from the group | None |
subgroup.created | A subgroup was created in the group | None |
subgroup.removed | A subgroup was removed from the group | None |
Every filter is optional except Status. Patterns are case-sensitive.
- Project lists the projects where the credential has at least the Maintainer role. Choose one or more. Events without a project, such as epics and group access token notices, don't match when Project is set.
- Branch matches the branch that was pushed, or the branch a pipeline or job ran for, such as
mainorrelease/*. Pipelines and jobs for tags never match it. - Tag matches the tag that was created or deleted, or the release's tag, such as
v*. - Target branch matches the branch a merge request merges into.
- Label matches when any label on the merge request, issue, or work item matches the pattern, such as
bugorpriority::*. - Changed matches updates that changed one of the chosen fields, such as the labels or the assignees. For merge requests, New commits matches updates that added commits to the source branch.
- Type limits work items to types such as task, incident, or epic.
- Comment on limits comments to merge requests, issues, commits, or snippets.
- Status is required on
pipeline.updatedandjob.updated. A pipeline or job changes status several times as it runs, for example from pending to running to failed, so choose the statuses that should start a run, such as Failed. - Job name matches the job's name, such as
test:*. - Environment matches the deployment's environment, such as
production. - Emoji matches the reaction's name, such as
thumbsuporeyes. - Severity and State match the vulnerability's severity and its status, such as Needs triage or Confirmed.
Some events depend on the GitLab plan or settings:
- Member, project, and subgroup events, and epics, need a group webhook. See Group events.
merge_request.approval_added,merge_request.approval_removed,deployment.approvedanddeployment.rejectedneed GitLab Premium or Ultimate. So doesaccess_token.expiringon GitLab.com, because project and group access tokens need a paid plan there.deployment.blockedis available on Free for deployments waiting on a manual action. On Free, GitLab sendsmerge_request.approvedandmerge_request.unapprovedinstead of the approval events.vulnerability.updatedneeds GitLab Ultimate.- When every project of the connection is on a plan below the one an event needs, the trigger picker shows that event as unavailable. GitLab shows a namespace's plan only to its Owners. When Helios can't read the plan, the events stay available.
deploy_token.expiringis behind a GitLab feature flag that is off by default, so GitLab may not send it.
On GitLab.com Free, GitLab can ask the user who starts a pipeline to verify their identity before jobs run on GitLab-hosted runners. Until then, the pipeline fails without running a job. pipeline.updated still fires with the Failed status, but there is no job log for the agent to read.
Helios checks the webhook-signature header on every delivery, and rejects deliveries more than five minutes old. Helios ignores a delivery it has already received.
GitLab turns off a webhook after four failed deliveries in a row, and turns it back on after a wait. After 40 failures in a row, it stays off until a test request succeeds. Send one from the webhook's settings in GitLab. On GitLab Self-Managed, this applies to project webhooks only when an administrator turns it on.
An agent's own actions can start new runs. A comment from the agent starts comment.created, and editing an issue or merge request starts issue.updated or merge_request.updated. An agent that pushes commits starts branch.pushed, a new pipeline, and its job events. A job it retries starts job.updated and pipeline.updated again. Say in the prompt which events to ignore, such as comments the agent wrote. See Guarding against events you don't want.
Check that it works
- The connection: after you save, the connection's status shows Connected. Error means GitLab rejected the credential. Click Test Connection to check again.
- The webhook: in GitLab, open a project's Settings → Webhooks, or the group's when you set Group. A webhook named Helios should be listed. To see its deliveries, select Edit, then Recent events.
- A trigger:
- Add
issue.openedto a workflow with no filters. - Open a test issue in a project that has the Helios webhook, or in a project of the group.
- Check that a run starts. Then open the connection in Helios and click Webhook events to see the event.
- Add
Common errors
| What you see | Cause | Fix |
|---|---|---|
| The connection's status shows Error | The token expired or was revoked, lacks the api scope, or Base URL points to the wrong instance | Create a new token with the api scope, and check Base URL |
Access token not found. Complete the OAuth flow first. | Access wasn't approved in GitLab | Open the connection and click Re-connect |
| GitLab says the redirect URI is not valid | The application's Redirect URI doesn't match the Helios callback URL | Set it to https://api.heliosapp.com/integrations/gitlab/oauth_callback |
GitLab says The requested scope is invalid, unknown, or malformed. | The application doesn't have the mcp scope, or Admin access is on and it lacks admin_mode or sudo | Select the missing scopes on the application in GitLab, then click Re-connect |
| GitLab says administrators are not permitted to connect applications with these scopes | The instance's disable_admin_oauth_scopes setting stops administrators from approving applications with the api scope | Approve access with a user who isn't an administrator, or ask an administrator to change the setting |
| No Helios webhook in any project | The credential's user isn't a Maintainer in any project, or the instance runs a GitLab version older than 19.0 | Use a credential with the Maintainer role, or upgrade GitLab, then save the connection again |
| No Helios webhook in one project | The credential isn't a Maintainer there, the project is archived, or the project is in the group from Group, whose webhook covers it | Give the credential the Maintainer role in that project, then save the connection again |
| Member, project, and subgroup events show as unavailable | Group is empty, the group path is wrong, the credential isn't an Owner of the group, or the group isn't on GitLab Premium or Ultimate | Fix the group or the role, then save the connection again |
| No run starts, but the webhook is listed | The filters don't match, GitLab turned the webhook off, or the namespace reached GitLab's webhook rate limit | Check Recent events on the webhook. Remove the filters, check that a run starts, then add them back |
pipeline.updated runs find no failed job | The pipeline failed before any job ran, often because of identity verification on GitLab.com | Select Verify my account in GitLab's banner, or use your own runners |
Limits
GitLab.com allows 2,000 authenticated API requests per minute for each user, with lower limits on some endpoints, such as creating comments. Requests over a limit get 429 Too Many Requests with a Retry-After header. See GitLab's rate limits.
On GitLab.com Free, the webhooks of one top-level namespace can be called 500 times a minute. Premium and Ultimate allow more. The Helios webhooks count toward this limit, and job events add many calls, because GitLab sends one each time a job changes status. Past the limit, GitLab pauses all of that namespace's webhooks, including those of other tools, until the next minute. See GitLab's webhook limits. A project can have up to 100 webhooks and a group up to 50.
On GitLab Self-Managed, the administrator sets these limits.
Last updated on
Vercel
The Vercel integration lets Helios agents call the Vercel REST API with your access token, and run agents automatically on Vercel webhook events such as failed deployments, promotions, rollbacks, domain and certificate changes, environment variable changes, feature flag changes, alerts, and firewall attacks.
Bitbucket
Connect agents to the Bitbucket Cloud REST API and start workflows on supported pull request, comment, push, build status, repository, and Pipelines trace events.