GitLab issues as Keepr tasks, tracked time as /spend
What the integration does
One integration connects a GitLab project to one Keepr project. Inbound, GitLab tells Keepr about its issues: a new issue becomes a task in the Keepr project, closing it archives the task, and reopening it restores the task. A merge request that closes exactly one issue — with “Closes #12” or a link GitLab counts as closing — is tracked on that issue's task, and the browser extension starts its timer there too; any other merge request gets a task of its own, archived when it is merged or closed. Keepr asks GitLab which issues a merge request closes, so this needs the token from step 4.
Outbound, Keepr reports tracked time back. When a time entry on a task linked to a GitLab issue, merge request or work item is saved, edited or deleted, Keepr adds a note with /spend to that item, so GitLab's own time tracking shows it. Only time from the integration's Keepr project is sent, to the second, never rounded.
Before you start
In Keepr you need to be an owner or admin of the organization, or a manager of the workspace that holds the project. Every plan includes integrations: Free runs one at a time, Team three, and Ultimate — trial included — as many as you need.
In GitLab you need the Maintainer or Owner role on the project, to add a webhook and to create a project access token. A self-managed GitLab works the same way, as long as it can reach over HTTPS and Keepr can reach it back over the public internet.
1. Create the integration in Keepr
Open Organization settings → Integrations and press “New integration”. Choose GitLab, the Keepr project its tasks go into, and the name it acts under: Keepr shows its actions as, for example, “Archived by GitLab · Website”.
Keepr then shows the webhook signing secret, once. Copy it before closing the dialog. Afterwards only its first characters are shown; if it is lost, rotate it on the integration's page and paste the new one into GitLab.
2. Add the webhook in GitLab
In the GitLab project, open Settings → Webhooks, at , and press “Add new webhook”. Paste the endpoint from the integration's Inbound webhooks section into URL, and the signing secret into “Secret token”.
Under Trigger, tick “Issues events” and “Merge request events”, keep SSL verification on, and save. “Test → Issues events” sends a sample, which appears in the integration's recent deliveries in Keepr with what it did.
Confidential issues are ignored on purpose: their titles would reach everyone who can see the Keepr project.
3. Choose who the time is reported as
GitLab attributes every note and every /spend to the owner of the token Keepr uses, so in GitLab's time reports that account appears to have spent all of the team's time. The note itself names the Keepr member who tracked it. Reporting under each member's own GitLab account is planned; until then, use a token that belongs to no person.
Best is a project access token, for which GitLab creates a bot user of its own. Create it at with the Reporter role or higher and the api scope. A group access token, at , covers every project in the group the same way. On GitLab.com both need the Premium or Ultimate tier; self-managed GitLab has them on every tier.
Where your plan has service accounts, a service account and its token work as well; GitLab.com lists them under the top-level group's Settings → Service accounts, at . Failing all of these, create a separate GitLab account for Keepr, give it the Reporter role on the project, and sign in as it to create a personal access token at User settings → Personal access tokens with the api scope.
Use a classic token, not a fine-grained one: fine-grained tokens cannot run /spend yet. Set an expiry date you will notice; once the token expires, reporting stops with “Credential rejected” until a new one is set.
4. Give Keepr the address and the token
On the integration's page, open Credentials and press “Set…” next to the GitLab credential. The GitLab address is , or the root address of your self-managed instance, not a project page; a pasted gitlab.com page is saved as . Paste the token and save.
Keepr stores the token encrypted and afterwards shows only its first characters. It uses it to write time back and to read which issues a merge or pull request closes; nothing else. Without it, issues still come in, but a merge or pull request is left alone and shows “Couldn't read its issues” in the recent deliveries.
What Keepr sends
An entry is sent once it is finished — a running timer sends nothing until it stops — and only when its task is in the integration's project and links to an issue, merge request or work item under the GitLab address. Tasks the integration created link that way, and so do tasks started from the browser extension on a GitLab page.
Each send is a note on the item: a sentence naming the member, the duration and the day, then a /spend line. An edit sends only the difference, as a negative /spend when the entry got shorter; deleting the entry takes its time back the same way, and moving it to another issue moves the time. Durations go in hours, minutes and seconds, never days, because GitLab counts a day as eight working hours.
Imported time is history and is never sent. The integration's “Recently sent” list shows the latest entries, each with its item, the member, the time GitLab holds for it, and whether the last send worked; a failed row says why and what to do.
Pause, rotate or revoke
The switch on the integration's page turns it off without losing anything. While it is off, deliveries are recorded but not acted on (GitLab's “Resend request” replays one later), and no time is sent: an entry saved during the pause is sent the next time it changes. Switching it back on takes a slot under your plan's limit again.
“Rotate…” in Credentials replaces the webhook secret at once, with no grace period: deliveries fail until the new secret is in GitLab. “Remove” next to the GitLab credential stops time reporting and leaves the inbound direction alone.
Revoke is final. Keepr destroys both secrets and acts on nothing more; the integration stays in the list as history, and what it did stays attributed to it in the audit log and on archived tasks. Revoke the token in GitLab as well.
When a webhook is not possible
If you cannot add a webhook — no Maintainer role, or a GitLab that cannot reach Keepr — a scheduled pipeline can create the tasks through the REST API instead. It needs a personal API token, which is part of the Ultimate plan: create one under API tokens with the timers:write scope, and keep it in a masked CI/CD variable at . Schedule the pipeline at .
The job lists the project's issues with GitLab's API and, for each, calls with the header “Authorization: Bearer” and the token, and a JSON body of externalKey (the issue number), name (its title), url (its web address) and projectId. Keepr returns the existing task or creates it, matched exactly as a webhook-created one is, so running the job again is harmless.
To archive the task of a closed issue, take the task's id from that response and call , which needs the projects:write scope. Every field is in the API reference at api.keepr.ru/docs. Time reporting needs no webhook and works either way once the address and token are set.
If something goes wrong
Every delivery appears under the integration's recent deliveries with what it did. One that GitLab shows as failed with 401 carried the wrong secret token: paste the current one, or rotate it. “Task already there” means a task for the issue existed — for example, one started from the browser extension — and nothing was duplicated.
In “Recently sent”, “Address or credential not set” means the integration has no GitLab address or token yet. “Credential rejected” means the token was revoked, expired or lacks the api scope. “Tool refused it” usually means GitLab kept the note but dropped its /spend, because the token's role is below Reporter. “Issue not found” means the item was deleted or the token cannot see it, and “Address not allowed” that the GitLab address is not public HTTPS. “Tool unreachable, retrying” clears on its own once GitLab answers.
A send that failed for good is tried again the next time the entry changes.