HELIOS DOCS

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

MethodTypeDescription
GitLab Access TokenAPI KeyA personal, group, or project access token with the api scope
Your own GitLab OAuth appOAuth 2.0An 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.

  1. 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.
  2. Enter a name and an expiration date, select the api scope, and create the token.
  3. In Helios, choose GitLab Access Token and paste the token into GitLab Access Token.
  4. Optional: enter a group's full path, such as acme/platform, in Group.
  5. For GitLab Self-Managed, set Base URL under Advanced Configuration.
  6. 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.

  1. 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.

  2. Enter a Name and set Redirect URI to:

    https://api.heliosapp.com/integrations/gitlab/oauth_callback
  3. Select the api and mcp scopes. If you will turn on Admin access in Helios, also select admin_mode and sudo. If the form shows Confidential, leave it selected. Then select Save application.

  4. In Helios, choose Your own GitLab OAuth app and enter the Application ID and Secret.

  5. Optional: enter a group's full path in Group, and select Admin access to ask for the admin scopes.

  6. For GitLab Self-Managed, set Base URL under Advanced Configuration. The application must be registered on that instance.

  7. 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.

ScopeWhat it's for
apiFull read and write access to the REST API, and the webhooks for triggers
mcpGitLab's MCP server at /api/v4/mcp, which api doesn't cover

Admin access adds these scopes:

ScopeWhat it's for
admin_modeAdmin endpoints when Admin Mode is on
sudoCalls 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

EventDescriptionFilters
branch.pushedCommits were pushed to a branch, or a branch was created or deletedproject, branch (pattern)
tag.createdA tag was createdproject, tag (pattern)
tag.deletedA tag was deletedproject, tag (pattern)
merge_request.openedA merge request was openedproject, target branch (pattern), label (pattern)
merge_request.updatedA merge request was edited, or commits were added to its source branchproject, target branch (pattern), label (pattern), changed
merge_request.approval_addedA user approved a merge requestproject, target branch (pattern), label (pattern)
merge_request.approval_removedA user removed their approval from a merge requestproject, target branch (pattern), label (pattern)
merge_request.approvedA merge request was approved by all required approversproject, target branch (pattern), label (pattern)
merge_request.unapprovedA merge request lost its approved statusproject, target branch (pattern), label (pattern)
merge_request.mergedA merge request was mergedproject, target branch (pattern), label (pattern)
merge_request.closedA merge request was closedproject, target branch (pattern), label (pattern)
merge_request.reopenedA merge request was reopenedproject, target branch (pattern), label (pattern)
issue.openedAn issue was openedproject, label (pattern)
issue.updatedAn issue was editedproject, label (pattern), changed
issue.closedAn issue was closedproject, label (pattern)
issue.reopenedAn issue was reopenedproject, label (pattern)
work_item.openedA task, incident, epic or other work item that is not an issue was createdproject, type, label (pattern)
work_item.updatedA work item that is not an issue was editedproject, type, label (pattern), changed
work_item.closedA work item that is not an issue was closedproject, type, label (pattern)
work_item.reopenedA work item that is not an issue was reopenedproject, type, label (pattern)
comment.createdA comment was added to a merge request, issue, commit or snippetproject, comment on
comment.updatedA comment on a merge request, issue, commit or snippet was editedproject, comment on
wiki_page.createdA wiki page was createdproject
wiki_page.updatedA wiki page was editedproject
wiki_page.deletedA wiki page was deletedproject
pipeline.updatedA pipeline's status changed, for example to failedproject, status (required), branch (pattern)
job.updatedA job's status changed, for example to failedproject, status (required), branch (pattern), job name (pattern)
deployment.startedA deployment startedproject, environment (pattern)
deployment.succeededA deployment succeededproject, environment (pattern)
deployment.failedA deployment failedproject, environment (pattern)
deployment.canceledA deployment was canceledproject, environment (pattern)
deployment.blockedA deployment is waiting for an approval or a manual actionproject, environment (pattern)
deployment.approvedAn approver approved a deploymentproject, environment (pattern)
deployment.rejectedAn approver rejected a deploymentproject, environment (pattern)
release.createdA release was createdproject, tag (pattern)
release.updatedA release was editedproject, tag (pattern)
release.deletedA release was deletedproject, tag (pattern)
milestone.createdA milestone was createdproject
milestone.closedA milestone was closedproject
milestone.reopenedA milestone was reopenedproject
milestone.deletedA milestone was deletedproject
feature_flag.enabledA feature flag was turned onproject
feature_flag.disabledA feature flag was turned offproject
emoji.awardedAn emoji reaction was added to an issue, merge request, snippet, wiki page or commentproject, emoji (pattern)
emoji.revokedAn emoji reaction was removedproject, emoji (pattern)
access_token.expiringA project or group access token expires in 7 days, or in 30 or 60 days when the project or group turns those notices onproject
deploy_token.expiringA project deploy token expires in 7, 30 or 60 daysproject
vulnerability.updatedA vulnerability was created, its status changed, or an issue was linked to itproject, severity, state
member.addedA user was added to the groupNone
member.updatedA group member's role or access expiration date changedNone
member.removedA user was removed from the groupNone
member.access_requestedA user requested access to the groupNone
member.access_request_deniedA request for access to the group was deniedNone
project.createdA project was created in the groupNone
project.deletedA project was deleted from the groupNone
subgroup.createdA subgroup was created in the groupNone
subgroup.removedA subgroup was removed from the groupNone

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 main or release/*. 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 bug or priority::*.
  • 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.updated and job.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 thumbsup or eyes.
  • 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.approved and deployment.rejected need GitLab Premium or Ultimate. So does access_token.expiring on GitLab.com, because project and group access tokens need a paid plan there. deployment.blocked is available on Free for deployments waiting on a manual action. On Free, GitLab sends merge_request.approved and merge_request.unapproved instead of the approval events.
  • vulnerability.updated needs 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.expiring is 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:
    1. Add issue.opened to a workflow with no filters.
    2. Open a test issue in a project that has the Helios webhook, or in a project of the group.
    3. Check that a run starts. Then open the connection in Helios and click Webhook events to see the event.

Common errors

What you seeCauseFix
The connection's status shows ErrorThe token expired or was revoked, lacks the api scope, or Base URL points to the wrong instanceCreate 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 GitLabOpen the connection and click Re-connect
GitLab says the redirect URI is not validThe application's Redirect URI doesn't match the Helios callback URLSet 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 sudoSelect the missing scopes on the application in GitLab, then click Re-connect
GitLab says administrators are not permitted to connect applications with these scopesThe instance's disable_admin_oauth_scopes setting stops administrators from approving applications with the api scopeApprove access with a user who isn't an administrator, or ask an administrator to change the setting
No Helios webhook in any projectThe credential's user isn't a Maintainer in any project, or the instance runs a GitLab version older than 19.0Use a credential with the Maintainer role, or upgrade GitLab, then save the connection again
No Helios webhook in one projectThe credential isn't a Maintainer there, the project is archived, or the project is in the group from Group, whose webhook covers itGive the credential the Maintainer role in that project, then save the connection again
Member, project, and subgroup events show as unavailableGroup 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 UltimateFix the group or the role, then save the connection again
No run starts, but the webhook is listedThe filters don't match, GitLab turned the webhook off, or the namespace reached GitLab's webhook rate limitCheck Recent events on the webhook. Remove the filters, check that a run starts, then add them back
pipeline.updated runs find no failed jobThe pipeline failed before any job ran, often because of identity verification on GitLab.comSelect 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