GitLab 22 🦊 Webhooks, API Integration, and System Automation
The previous chapters covered the GitLab UI, the CI/CD pipeline, and the access control model. This chapter covers the mechanisms that allow external systems to interact with GitLab programmatically: webhooks, which push events to an external URL, and the REST/GraphQL API, which allows an external system to query and modify GitLab resources. The LFCS exam does not test GitLab, but the GitLab Certified Associate exam does. The critical distinction is between the push model (webhooks) and the pull model (API polling). A webhook is an HTTP POST that GitLab sends to a configured URL when an event occurs. The API is an HTTP request that an external system sends to GitLab to read or write data. The webhook is event-driven and near-real-time. The API is request-driven and requires the external system to poll or to be triggered by a webhook.
Key point: A webhook is configured in Settings → Webhooks. It has a URL, a secret token, a list of events, and an SSL verification setting. The payload is JSON. The API is accessed at https://gitlab.example.com/api/v4/. It requires a personal access token, a project access token, or a CI/CD job token. The glab CLI wraps the API for the terminal. The API is versioned (v4), and the webhook payload is not versioned; it changes with the GitLab release.
Why webhooks and the API require explicit configuration
The notification problem. A project is updated, a pipeline fails, a merge request is opened. The external system (a chat server, a deployment orchestrator, a ticketing system) needs to know about the event. The webhook is the mechanism. GitLab sends an HTTP POST to the configured URL with a JSON payload that describes the event. The external system receives the payload and acts on it. The webhook is push-based; the external system does not need to poll .
The automation problem. A pipeline needs to create an issue, add a comment, or merge a merge request. The CI/CD pipeline can use the API with the CI_JOB_TOKEN to perform these actions. The CI_JOB_TOKEN is a short-lived token that is valid only for the job’s duration. For actions that require more permissions, a project access token or a personal access token is the correct mechanism.
The security problem. A webhook URL is a public endpoint. An attacker who discovers the URL can send fake payloads to the external system. The secret token is the defense. GitLab signs the payload with the token using HMAC-SHA256, and the external system verifies the signature before acting on the payload. The X-Gitlab-Token header contains the token, and the X-Gitlab-Event header contains the event type .
The API versioning problem. The webhook payload is not versioned. A GitLab upgrade can change the fields, and the external system may break. The API is versioned (/api/v4/), and the breaking changes are announced in advance. The API is the more stable integration mechanism for long-term automation. The webhook is the more efficient mechanism for event-driven notifications.
The trade-off. A webhook is near-real-time and efficient: one HTTP request per event. The API polling is inefficient: one HTTP request per poll, regardless of whether an event occurred. The webhook requires the external system to expose a public endpoint and to verify the signature. The API requires the external system to hold a token and to handle the pagination. The exam tests both mechanisms and the distinction between them.
a. Configuring a webhook
A webhook is configured in the project settings under Settings → Webhooks. The form requires a URL, an optional secret token, and a list of events.
Settings → Webhooks → Add new webhook
URL: https://example.com/gitlab-hook
Secret token: <random-string>
Trigger: Push events, Merge request events, Pipeline events
SSL verification: Enabled
Add webhook
The URL is the endpoint that receives the POST. The secret token is the HMAC key. The trigger events determine which events are sent. The SSL verification ensures that the endpoint’s certificate is valid. The Test button sends a test payload to the URL, which is useful for verifying the endpoint before relying on it.
The webhook can also be configured at the group level, which applies to every project in the group. The group-level webhook is the mechanism for a centralized integration that does not need per-project configuration.
The webhook payload is a JSON object. The X-Gitlab-Event header identifies the event type: Push Hook, Merge Request Hook, Pipeline Hook, Issue Hook, and others. The X-Gitlab-Token header contains the secret token, which the receiver verifies before processing the payload .
The X-Gitlab-Event-UUID header is a unique identifier for the event, which is useful for idempotency. The payload includes the project ID, the user who triggered the event, and the event-specific data.
b. The REST API and the CI_JOB_TOKEN
The REST API is accessed at https://gitlab.example.com/api/v4/. The endpoints are organized by resource: /projects, /issues, /merge_requests, /pipelines, /jobs, and others.
# List projects (with a personal access token)
curl --header "PRIVATE-TOKEN: <token>" \
"https://gitlab.example.com/api/v4/projects?membership=true"
# List merge requests for a project
curl --header "PRIVATE-TOKEN: <token>" \
"https://gitlab.example.com/api/v4/projects/<project-id>/merge_requests?state=opened"
The PRIVATE-TOKEN header is the authentication for a personal access token. The JOB-TOKEN header is the authentication for a CI/CD job token. The Authorization: Bearer <token> header is the authentication for an OAuth token.
The CI_JOB_TOKEN is the mechanism for a pipeline to call the API. It is valid only for the duration of the job and has limited permissions.
job:
script:
- curl --header "JOB-TOKEN: $CI_JOB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/issues"
The CI_JOB_TOKEN can be used to clone other repositories, pull container images, and access the API. It cannot be used to create issues or merge requests in projects other than the one that triggered the pipeline, unless the other project has explicitly added the job token to its allowlist.
c. The glab CLI and the GraphQL API
The glab CLI is the official command-line tool for GitLab. It wraps the REST API and provides a terminal interface for the common operations.
# Authenticate
glab auth login
# List merge requests
glab mr list
# Create a merge request
glab mr create --fill
# View an issue
glab issue view 42
# Trigger a pipeline
glab ci run
The glab api command sends an arbitrary API request, which is the escape hatch for endpoints that the CLI does not wrap directly.
# Get the current user
glab api user
# List project members
glab api projects/42/members
# Create an issue
glab api projects/42/issues -X POST -f title="Bug" -f description="..."
The GraphQL API is an alternative to the REST API. It allows the client to request exactly the fields it needs, which reduces the number of requests. The endpoint is https://gitlab.example.com/api/graphql. The authentication is the same as the REST API.
# GraphQL query
curl --header "Authorization: Bearer <token>" \
--header "Content-Type: application/json" \
--data '{"query": "{ currentUser { username } }"}' \
"https://gitlab.example.com/api/graphql"
The GraphQL API is not a replacement for the REST API. It is an alternative that is better suited for complex queries that span multiple resources. The REST API remains the standard for simple operations and for compatibility with existing integrations.
Complete Example Session
# ============================================
# PART 1: CONFIGURE A WEBHOOK
# ============================================
# Settings → Webhooks → Add new webhook
# URL: https://example.com/gitlab-hook
# Secret token: <random>
# Trigger: Push events, Merge request events
# Save
# ============================================
# PART 2: TEST THE WEBHOOK
# ============================================
# Click "Test" → "Push events"
# The endpoint receives a test payload.
# ============================================
# PART 3: VERIFY THE SIGNATURE
# ============================================
# The receiver checks the X-Gitlab-Token header.
# If the value matches the configured secret, the payload is trusted.
# ============================================
# PART 4: API CALL WITH PERSONAL TOKEN
# ============================================
curl --header "PRIVATE-TOKEN: <token>" \
"https://gitlab.example.com/api/v4/projects?membership=true"
# Returns the list of projects.
# ============================================
# PART 5: API CALL WITH CI_JOB_TOKEN
# ============================================
job:
script:
- curl --header "JOB-TOKEN: $CI_JOB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/issues"
# ============================================
# PART 6: GLAB CLI
# ============================================
glab auth login
glab mr list
glab mr create --fill
# ============================================
# PART 7: GLAB API
# ============================================
glab api projects/42/issues -X POST -f title="New issue"
# ============================================
# PART 8: GRAPHQL QUERY
# ============================================
curl --header "Authorization: Bearer <token>" \
--header "Content-Type: application/json" \
--data '{"query": "{ project(fullPath: \"group/project\") { name } }"}' \
"https://gitlab.example.com/api/graphql"
# ============================================
# PART 9: PROJECT ACCESS TOKEN
# ============================================
# Settings → Access tokens → Add token
# Name: automation
# Scopes: api, read_repository, write_repository
# Expiration: 30 days
# Create token
The nine parts trace the integration workflow: webhook configuration, testing, signature verification, API call with a personal token, API call with the CI job token, glab CLI, glab api, GraphQL query, and project access token.
Quick Reference
Webhook Headers
| Header | Purpose |
|---|---|
X-Gitlab-Event | Event type |
X-Gitlab-Token | Secret token |
X-Gitlab-Event-UUID | Unique event ID |
X-Gitlab-Hook-ID | Webhook ID |
API Authentication
| Header | Token Type |
|---|---|
PRIVATE-TOKEN | Personal access token |
JOB-TOKEN | CI/CD job token |
Authorization: Bearer | OAuth token |
API Endpoints
| Endpoint | Purpose |
|---|---|
/api/v4/projects | Projects |
/api/v4/projects/:id/issues | Issues |
/api/v4/projects/:id/merge_requests | Merge requests |
/api/v4/projects/:id/pipelines | Pipelines |
/api/graphql | GraphQL API |
glab Commands
| Command | Purpose |
|---|---|
glab auth login | Authenticate |
glab mr list | List merge requests |
glab mr create --fill | Create MR from commits |
glab issue view <id> | View issue |
glab ci run | Trigger pipeline |
glab api <path> | Raw API call |
Best Practices
✅ Do This:
curl --header "PRIVATE-TOKEN: <token>" ...
<!-- Use explicit token in API calls --> # ✅
# Use a secret token for webhooks
<!-- Verify the X-Gitlab-Token header --> # ✅
- curl --header "JOB-TOKEN: $CI_JOB_TOKEN" ...
<!-- Use the job token in CI/CD --> # ✅
❌ Don’t Do This:
# Use a personal access token in a pipeline
<!-- It has broader permissions than needed --> # ❌
# Ignore the webhook signature
<!-- Fake payloads can trigger the endpoint --> # ❌
# Rely on webhook payloads for long-term integration
<!-- The payload format is not versioned --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Webhook not received | URL not reachable or SSL failure | Test the endpoint |
| Signature invalid | Wrong secret token | Verify the token |
| API 401 Unauthorized | Wrong token type | Use the correct header |
| API 403 Forbidden | Token scope too narrow | Expand the scope |
| Webhook payload changed | GitLab upgrade | Switch to API polling |
Real-World Examples
1. Add Webhook
Settings → Webhooks → Add new webhook
2. Test Webhook
Webhook → Test → Push events
3. Verify Token
X-Gitlab-Token: <secret>
4. API with PAT
curl --header "PRIVATE-TOKEN: <token>" /api/v4/user
5. API with JOB-TOKEN
- curl --header "JOB-TOKEN: $CI_JOB_TOKEN" /api/v4/projects/$CI_PROJECT_ID
6. glab Auth
glab auth login
7. glab MR Create
glab mr create --fill
8. glab api
glab api projects/42/issues
9. GraphQL Query
curl --header "Authorization: Bearer <token>" /api/graphql -d '{"query":"{ currentUser { username } }"}'
10. Project Access Token
Settings → Access tokens → Add token
Visual
Webhook Flow
┌──────────────────────────────────────────────┐
│ Event in GitLab (push, MR, pipeline) │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ POST https://example.com/hook │
│ Headers: X-Gitlab-Event, X-Gitlab-Token │
│ Body: JSON payload │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ External endpoint │
│ Verify token → Parse JSON → Act │
└──────────────────────────────────────────────┘
API Authentication
┌──────────────────────────────────────────────┐
│ Personal Access Token │
│ Header: PRIVATE-TOKEN │
│ Scope: user-defined │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ CI/CD Job Token │
│ Header: JOB-TOKEN │
│ Scope: limited to project │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ OAuth Token │
│ Header: Authorization: Bearer │
│ Scope: OAuth application │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Webhook URL | Configured in Settings → Webhooks |
| Webhook secret | HMAC key for signature |
| Webhook headers | X-Gitlab-Event, X-Gitlab-Token |
| API base URL | https://gitlab.example.com/api/v4/ |
| API auth (PAT) | PRIVATE-TOKEN header |
| API auth (CI job) | JOB-TOKEN header |
| API auth (OAuth) | Authorization: Bearer |
| CLI tool | glab |
| GraphQL endpoint | /api/graphql |
Key takeaways:
- Webhooks are push-based. GitLab sends a POST to the configured URL when an event occurs. The payload is JSON. The
X-Gitlab-Tokenheader is the signature for verification. - The webhook payload is not versioned. A GitLab upgrade can change the fields, and the external system may break. The API is the more stable mechanism for long-term integrations.
- The REST API is the standard automation interface. It is versioned (
/api/v4/), authenticated with a token, and organized by resource. TheglabCLI wraps the common operations. - The
CI_JOB_TOKENis the pipeline-scoped token. It is valid only for the job’s duration and has limited permissions. It is the correct token for a pipeline that calls the API. - The GraphQL API is the alternative. It allows the client to request exactly the fields it needs. It is better suited for complex queries that span multiple resources.
Remember: The webhook is the event notification. The API is the data access. The glab CLI is the terminal interface. The token is the credential. The secret is the signature. The exam tests the distinction between push (webhook) and pull (API), and the authentication headers for each.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!