GitLab
Open a GitLab issue for each feedback item on gitlab.com or self-managed GitLab, with two-way open and closed state.
The GitLab integration works like GitHub: one issue per feedback item, created automatically or with a button, and closing either side closes the other. It uses an access token, so it works with gitlab.com and with self-managed GitLab, including instances with no public address.
Connect a project
Create a token with the api scope: a project access token (Settings › Access tokens in the GitLab
project, role Reporter or higher so it can manage issues) or a personal access token. Issues are opened by the token's
user.
Connect. As a workspace admin, open the Pinwren project's Issue trackers tab and connect GitLab:
| Field | Notes |
|---|---|
| Access token | Stored encrypted; only its last four characters are ever shown |
| Project path | group/project or group/subgroup/project |
| Self-managed GitLab URL | Optional, e.g. https://gitlab.example.com. Empty uses GITLAB_URL (https://gitlab.com). /api/v4 is added for you |
| Labels | Up to 10, comma-separated; added to every issue (default feedback) |
| Open issues for | Manually, Bugs or Everything |
Use Test connection to check that the token can see the project.
Add the webhook so closing an issue updates Pinwren. Under Two-way status the tab shows a URL
(https://feedback.example.com/api/integrations/gitlab/webhook); press Reveal webhook secret for the secret.
In the GitLab project, open
Settings › Webhooks › Add new webhook, paste the URL, put the secret in Secret token and tick Issues
events.
Creating issues
| Open issues for | What happens |
|---|---|
| Manually | Only when someone presses Send to GitLab on an item in the inbox |
| Bugs | Every new bug report opens an issue |
| Everything | Every new item opens an issue |
Issues have the same title (#142 Checkout button does nothing), quoted and escaped body, environment table and
dashboard link as on GitHub. Screenshots, logs and reporter emails are never put in an issue.
Two-way state
| You do | Pinwren does |
|---|---|
| Move an item to Resolved, Won't fix or Duplicate | Closes the issue |
| Reopen the item | Reopens the issue |
| Close the issue in GitLab | Moves an open item to Resolved |
| Reopen the issue in GitLab | Moves a closed item back to Open |
Changes from GitLab appear in the timeline as the integration and are never pushed back, so nothing loops.
Reliability and security
- Issue creation and state changes are retried background jobs; a GitLab outage never blocks a report. The last error
shows on the integration until the next success: the status code plus GitLab's JSON
message(orerror) field — never a raw response body — or a category such asCould not reach GitLab: timed out. Redirects are not followed. - The webhook is verified by comparing
X-Gitlab-Tokenwith the integration's secret in constant time. Only issue events with thecloseorreopenaction change anything; others are acknowledged and ignored. Requests over 1 MB are refused with413before they are parsed, and each sending address may make 120 deliveries a minute. - Replay protection: each verified delivery is acted on once. Pinwren remembers its
X-Gitlab-Event-UUID(orIdempotency-Key, or the body's hash) for 7 days and acknowledges a repeat with{ "ok": true, "duplicate": true }without acting on it. - When several Pinwren projects sync one GitLab project, only the integration whose secret matches acts.
- If the webhook secret leaks, rotate it with
POST /api/integrations/:id/rotate-webhook-secret(workspace admin session; the tab has no button for it) and paste the new one into GitLab.
Self-managed GitLab
Set Self-managed GitLab URL per integration, or GITLAB_URL for the whole instance. Your Pinwren server must
reach GitLab's API, and GitLab must reach Pinwren's webhook URL — on self-managed GitLab, allow requests to your
Pinwren host under Admin › Settings › Network › Outbound requests if it is on a private network.
The reverse applies on Pinwren's side. A per-integration Self-managed GitLab URL is entered by a workspace admin,
so Pinwren refuses it when it resolves to a private address (blocked: the address is private or reserved) unless
the instance operator lists the host or range in
PINWREN_OUTBOUND_ALLOWLIST, e.g.
PINWREN_OUTBOUND_ALLOWLIST=gitlab.internal. GITLAB_URL is set by the operator and may be private without the
allowlist.
| Variable | Type | Default | Description |
|---|---|---|---|
GITHUB_API_URL | url | https://api.github.com | GitHub API base URL. Each integration can override it (GitHub Enterprise Server). |
GITLAB_URL | url | https://gitlab.com | GitLab base URL. Each integration can override it (self-managed GitLab). |
Generated from packages/api/src/env.ts on every build.