This guide explains how to set up a GitLab webhook integration with Rocket.Chat. Once configured, you'll receive real-time notifications for GitLab events like comments, issues, merge requests, and deployments directly in a Rocket.Chat room.
Prerequisites
Before you begin, verify the following:
Your Rocket.Chat workspace is publicly accessible via a URL.
You have an existing GitLab project to integrate.
Step 1: Create a Rocket.Chat webhook integration
On your Rocket.Chat workspace, navigate to Administration > Workspace > Integrations.
Click New in the top right and select the Incoming tab.
Enable the integration and fill in the details for your webhook:
Field
Description
Name
A name to identify this integration.
Post to Channel
The Rocket.Chat room where webhook messages will be posted. Use
@username
for a user or#channel
for a channel (e.g., @test or #general).Post as
The username that webhook messages will appear from. This user must already exist in your workspace.
Alias
An optional alias displayed before the username in webhook messages.
Scripts Enabled
Toggle to enable custom scripts for this integration.
Paste the provided script into the Script textbox. This script is essential for parsing the data from GitLab's webhook and formatting it for display in Rocket.Chat.
/* eslint no-console:0, max-len:0 */ // see <https://gitlab.com/help/web_hooks/web_hooks> for full json posted by GitLab const MENTION_ALL_ALLOWED = false; // <- check that bot permission allow has mention-all before passing this to true. const NOTIF_COLOR = '#6498CC'; const IGNORE_CONFIDENTIAL = true; const refParser = (ref) => ref.replace(/^refs\/(?:tags|heads)\/(.+)$/, '$1'); const displayName = (name) => (name && name.toLowerCase().replace(/\s+/g, '.')); const atName = (user) => (user && user.name ? '@' + displayName(user.name) : ''); const makeAttachment = (author, text, color) => { return { author_name: author ? displayName(author.name) : '', author_icon: author ? author.avatar_url : '', text, color: color ?? NOTIF_COLOR }; }; const pushUniq = (array, val) => ~array.indexOf(val) || array.push(val); // eslint-disable-line class Script { // eslint-disable-line process_incoming_request({ request }) { try { let result = null; const channel = request.url.query.channel; const event = request.headers['x-gitlab-event']; switch (event) { case 'Push Hook': result = this.pushEvent(request.content); break; case 'Merge Request Hook': result = this.mergeRequestEvent(request.content); break; case 'Note Hook': result = this.commentEvent(request.content); break; case 'Confidential Issue Hook': case 'Issue Hook': result = this.issueEvent(request.content, event); break; case 'Tag Push Hook': result = this.tagEvent(request.content); break; case 'Pipeline Hook': result = this.pipelineEvent(request.content); break; case 'Build Hook': // GitLab < 9.3 result = this.buildEvent(request.content); break; case 'Job Hook': // GitLab >= 9.3.0 result = this.buildEvent(request.content); break; case 'Wiki Page Hook': result = this.wikiEvent(request.content); break; default: result = this.unknownEvent(request, event); break; } if (result && result.content && channel) { result.content.channel = '#' + channel; } return result; } catch (e) { console.log('gitlabevent error', e); return this.createErrorChatMessage(e); } } createErrorChatMessage(error) { return { content: { username: 'Rocket.Cat ErrorHandler', text: 'Error occured while parsing an incoming webhook request. Details attached.', icon_url: '', attachments: [ { text: `Error: '${error}', \n Message: '${error.message}', \n Stack: '${error.stack}'`, color: NOTIF_COLOR } ] } }; } unknownEvent(data, event) { return { content: { username: data.user ? data.user.name : (data.user_name ?? 'Unknown user'), text: `Unknown event '${event}' occured. Data attached.`, icon_url: data.user ? data.user.avatar_url : (data.user_avatar ?? ''), attachments: [ { text: `${JSON.stringify(data, null, 4)}`, color: NOTIF_COLOR } ] } }; } issueEvent(data, event) { if (event === 'Confidential Issue Hook' && IGNORE_CONFIDENTIAL) { return false; } const project = data.project ?? data.repository; const state = data.object_attributes.state; const action = data.object_attributes.action; let user_action = state; let assigned = ''; if (action === 'update') { user_action = 'updated'; } if (data.assignee) { assigned = `*Assigned to*: @${data.assignee.username}\n`; } return { content: { username: 'gitlab/' + project.name, icon_url: project.avatar_url ?? data.user.avatar_url ?? '', text: (data.assignee && data.assignee.name !== data.user.name) ? atName(data.assignee) : '', attachments: [ makeAttachment( data.user, `${user_action} an issue _${data.object_attributes.title}_ on ${project.name}. *Description:* ${data.object_attributes.description}. ${assigned} See: ${data.object_attributes.url}` ) ] } }; } commentEvent(data) { const project = data.project ?? data.repository; const comment = data.object_attributes; const user = data.user; const at = []; let text; if (data.merge_request) { const mr = data.merge_request; const lastCommitAuthor = mr.last_commit && mr.last_commit.author; if (mr.assignee && mr.assignee.name !== user.name) { at.push(atName(mr.assignee)); } if (lastCommitAuthor && lastCommitAuthor.name !== user.name) { pushUniq(at, atName(lastCommitAuthor)); } text = `commented on MR [#${mr.id} ${mr.title}](${comment.url})`; } else if (data.commit) { const commit = data.commit; const message = commit.message.replace(/\n[^\s\S]+/, '...').replace(/\n$/, ''); if (commit.author && commit.author.name !== user.name) { at.push(atName(commit.author)); } text = `commented on commit [${commit.id.slice(0, 8)} ${message}](${comment.url})`; } else if (data.issue) { const issue = data.issue; text = `commented on issue [#${issue.id} ${issue.title}](${comment.url})`; } else if (data.snippet) { const snippet = data.snippet; text = `commented on code snippet [#${snippet.id} ${snippet.title}](${comment.url})`; } return { content: { username: 'gitlab/' + project.name, icon_url: project.avatar_url ?? user.avatar_url ?? '', text: at.join(' '), attachments: [ makeAttachment(user, `${text}\n${comment.note}`) ] } }; } mergeRequestEvent(data) { const user = data.user; const mr = data.object_attributes; const assignee = mr.assignee; let at = []; if (mr.action === 'open' && assignee) { at = '\n' + atName(assignee); } else if (mr.action === 'merge') { const lastCommitAuthor = mr.last_commit && mr.last_commit.author; if (assignee && assignee.name !== user.name) { at.push(atName(assignee)); } if (lastCommitAuthor && lastCommitAuthor.name !== user.name) { pushUniq(at, atName(lastCommitAuthor)); } } return { content: { username: `gitlab/${mr.target.name}`, icon_url: mr.target.avatar_url ?? mr.source.avatar_url ?? user.avatar_url ?? '', text: at.join(' '), attachments: [ makeAttachment(user, `${mr.action} MR [#${mr.iid} ${mr.title}](${mr.url})\n${mr.source_branch} into ${mr.target_branch}`) ] } }; } pushEvent(data) { const project = data.project ?? data.repository; const web_url = project.web_url ?? project.homepage; const user = { name: data.user_name, avatar_url: data.user_avatar }; // branch removal if (data.checkout_sha === null && !data.commits.length) { return { content: { username: `gitlab/${project.name}`, icon_url: project.avatar_url ?? data.user_avatar ?? '', attachments: [ makeAttachment(user, `removed branch ${refParser(data.ref)} from [${project.name}](${web_url})`) ] } }; } // new branch if (data.before == 0) { // eslint-disable-line return { content: { username: `gitlab/${project.name}`, icon_url: project.avatar_url ?? data.user_avatar ?? '', attachments: [ makeAttachment(user, `pushed new branch [${refParser(data.ref)}](${web_url}/commits/${refParser(data.ref)}) to [${project.name}](${web_url}), which is ${data.total_commits_count} commits ahead of master`) ] } }; } return { content: { username: `gitlab/${project.name}`, icon_url: project.avatar_url ?? data.user_avatar ?? '', attachments: [ makeAttachment(user, `pushed ${data.total_commits_count} commits to branch [${refParser(data.ref)}](${web_url}/commits/${refParser(data.ref)}) in [${project.name}](${web_url})`), { text: data.commits.map((commit) => ` - ${new Date(commit.timestamp).toUTCString()} [${commit.id.slice(0, 8)}](${commit.url}) by ${commit.author.name}: ${commit.message.replace(/\s*$/, '')}`).join('\n'), color: NOTIF_COLOR } ] } }; } tagEvent(data) { const project = data.project ?? data.repository; const web_url = project.web_url ?? project.homepage; const tag = refParser(data.ref); const user = { name: data.user_name, avatar_url: data.user_avatar }; let message; if (data.checkout_sha === null) { message = `deleted tag [${tag}](${web_url}/tags/)`; } else { message = `pushed tag [${tag} ${data.checkout_sha.slice(0, 8)}](${web_url}/tags/${tag})`; } return { content: { username: `gitlab/${project.name}`, icon_url: project.avatar_url ?? data.user_avatar ?? '', text: MENTION_ALL_ALLOWED ? '@all' : '', attachments: [ makeAttachment(user, message) ] } }; } createColor(status) { switch (status) { case 'success': return '#2faa60'; case 'pending': return '#e75e40'; case 'failed': return '#d22852'; case 'canceled': return '#5c5c5c'; case 'created': return '#ffc107'; case 'running': return '#607d8b'; default: return null; } } pipelineEvent(data) { const project = data.project ?? data.repository; const commit = data.commit; const user = { name: data.user_name, avatar_url: data.user_avatar }; const pipeline = data.object_attributes; return { content: { username: `gitlab/${project.name}`, icon_url: project.avatar_url ?? data.user_avatar ?? '', attachments: [ makeAttachment(user, `pipeline returned *${pipeline.status}* for commit [${commit.id.slice(0, 8)}](${commit.url}) made by *${commit.author.name}*`, this.createColor(pipeline.status)) ] } }; } buildEvent(data) { const user = { name: data.user_name, avatar_url: data.user_avatar }; return { content: { username: `gitlab/${data.repository.name}`, icon_url: '', attachments: [ makeAttachment(user, `build named *${data.build_name}* returned *${data.build_status}* for [${data.project_name}](${data.repository.homepage})`, this.createColor(data.build_status)) ] } }; } wikiPageTitle(wiki_page) { if (wiki_page.action === 'delete') { return wiki_page.title; } return `[${wiki_page.title}](${wiki_page.url})`; } wikiEvent(data) { const user_name = data.user.name; const project = data.project; const project_path = project.path_with_namespace; const wiki_page = data.object_attributes; const wiki_page_title = this.wikiPageTitle(wiki_page); const action = wiki_page.action; let user_action = 'modified'; if (action === 'create') { user_action = 'created'; } else if (action === 'update') { user_action = 'edited'; } else if (action === 'delete') { user_action = 'deleted'; } return { content: { username: project_path, icon_url: project.avatar_url ?? data.user.avatar_url ?? '', text: `The wiki page ${wiki_page_title} was ${user_action} by ${user_name}` } }; } }
Save the settings. After saving, the Webhook URL and Token are generated and displayed in the Instructions tab. Copy these credentials, you'll need them in the next step.
Step 2: Configure GitLab webhooks
In your GitLab project, navigate to Settings > Webhooks.
Paste the URL and Secret Token you copied from Rocket.Chat into the appropriate fields.
Under "Trigger," select the events you want to be notified about.
Click Add webhook to complete the setup.
Step 3: Testing the Integration
To verify that the integration is working, you can test it directly from the GitLab webhook settings page.
Find the webhook you just created.
Click the Test dropdown and select an event to trigger a notification.
You should receive a notification in the Rocket.Chat channel you specified.

GitLab webhook test
With the webhook successfully configured, your Rocket.Chat and GitLab workspaces are now connected. From now on, any actions you've subscribed to such as comments, issue updates, or merge requests will automatically send a notification to the designated Rocket.Chat channel.