GitHub integration
Logbook can watch the repositories your project ships from. When a pull request merges, the item that PR was linked to crosses itself off, records which files changed, and gets a starter resolution — with no human in the loop. This guide covers mapping repos to a project, configuring the webhook, what happens on merge, and how agents should link items to PRs.
How it fits together
There are two independent pieces, and you need both:
- A repo mapping (
project_repos) tells logbook which project owns events from a given repository. You add it in Settings, or an agent adds it with thelink_repoMCP tool. - A GitHub webhook on that repository actually delivers the PR events to logbook, signed with the project's secret.
A repo with a mapping but no webhook is silent (no events arrive). A webhook with no mapping is ignored (logbook does not know which project the events belong to). Set up both.
Mapping a repo to a project
Open the project's Settings page and find the GitHub repos section.
Add the repository as owner/name (for example groovy-os/logbook-v2) and
save. This writes a project_repos row.
From an agent, the equivalent is the link_repo MCP tool:
link_repo { "repo": "owner/name" }
One repo maps to one project
The repo column is globally unique (project_repos.repo carries a unique
constraint). A repository can be mapped to exactly one logbook project — you
cannot route the same repo's PR events into two projects. Adding a repo that
is already mapped elsewhere fails. If you need to move a repo, remove it from
the old project first.
Configuring the webhook
Create the webhook on each repository (or org) you mapped. In the repo on GitHub go to Settings → Webhooks → Add webhook and use exactly these values:
| Field | Value |
|---|---|
| Payload URL | https://logbook.gvy.ai/api/webhooks/github |
| Content type | application/json |
| Secret | the project's webhook secret (see below) |
| Events | Let me select individual events → Pull requests only |
The secret is per-project, not per-account. Each logbook project has its own
webhook_secret, and logbook uses it to verify the signature on every
delivery. Two projects have two different secrets; use the secret belonging
to the project you mapped the repo to.
Where the secret comes from
The project's webhook secret is embedded in the one-paste setup prompt.
On the project's Settings page, use Connect Claude Code → Show setup
prompt (or create the project) to reveal it. The prompt includes a ready
gh command that creates the webhook for you with the secret already filled
in:
gh api repos/<owner>/<name>/hooks -X POST \
-f name=web -F active=true -f "events[]=pull_request" \
-f "config[url]=https://logbook.gvy.ai/api/webhooks/github" \
-f "config[content_type]=json" \
-f "config[secret]=<the project's webhook secret>"
The secret is shown only in the setup prompt; treat it like a credential. If it leaks, re-issue the setup from Settings.
What happens when a PR merges
Every pull_request delivery keeps logbook's PR registry current (title,
author, state: open / draft / closed / merged). But only a merge
crosses items off. On a merged PR, logbook:
- Finds every linked item. It searches each item's links for one that
references
<owner>/<repo>/pull/<number>— aprlink you added. Any stored URL variant matches. - Crosses matched items off — status becomes
done, unless the item is alreadydone(see the rule below). - Writes a starter resolution, but only if the item has no resolution
yet. The stub reads like
Resolved by PR #123 (title), merged by @user. <url>and never overwrites a resolution you wrote by hand. - Appends a changed-file summary to that stub — a deterministic one-line
roll-up by top-level directory, e.g.
Touched 14 files: components/ (6), lib/ (4), app/ (4). - Records the changed files as
item_filesrows on each matched item (capped at 300 paths per PR). These power thehistory_for_filesMCP tool and thechanged_filesfield onpull_item. - Adds a note to each matched item recording the merge.
Enrichment is best-effort. If the changed-files fetch fails (no
GITHUB_TOKEN, a GitHub error, bad JSON), the item still crosses off exactly
as it would have — just with an empty file summary and no item_files rows.
The webhook still returns 200.
Linking items to PRs from the agent side
The cross-off only fires for items that carry the PR as a link. Add the link
when you open the PR, using update_item with add_links:
update_item {
"number": 42,
"add_links": [
{ "kind": "branch", "value": "feature/thing" },
{ "kind": "pr", "value": "https://github.com/owner/name/pull/123" }
]
}
kind: "pr"is what the merge matcher looks for. Pass the PR URL (or theowner/name/pull/numberpath).kind: "branch"records the branch you are working on before the PR exists.
You can also attach links at creation time via create_item / create_items
with the same links shape.
Do NOT mark the item done when you open the PR
Let the merge cross the item off. Do not set status: "done" yourself at
PR-open time.
Two reasons:
- A PR is not a merge. It may be closed without merging, or sit in review for days. Marking done at PR time claims work that has not landed.
- You lose the enrichment. The webhook only writes the starter resolution
and appends the changed-file summary while crossing an item from not-done
to done. If the item is already
donewhen the merge arrives, that step is skipped — no auto-resolution, no file summary. (The merge note anditem_filesare still recorded, but the resolution stub is gone.)
So: link the PR, keep the item in_progress, and let the merge do the
cross-off. If you want a written resolution, either write one yourself with
update_item (the webhook will not overwrite it) or let the stub stand.
Verifying deliveries
After creating the webhook, confirm events are arriving:
- In the repo on GitHub go to Settings → Webhooks and click the webhook.
- Open Recent Deliveries. GitHub sends an initial
pingwhen the hook is created — logbook answers it200 { ok: true }. - Merge a linked PR and check the delivery — expect
200with a body like{ "matched": 1, "crossed_off": ["LOG-42"] }.
Reading the status codes:
200— delivered and processed.{ "ignored": ... }in the body means logbook accepted the event but had nothing to do (the repo is not mapped, or the event was not a merge).401Bad signature — the webhook secret does not match the project's secret. Re-check that you used the right project's secret. This is the most common misconfiguration.503— the project has no webhook secret set. Re-issue the setup from Settings.400Invalid JSON — the content type is notapplication/json. Fix the webhook's content type.
If deliveries are 200 { ignored: "repo not mapped to a project" }, the
webhook is reaching logbook but the repo mapping is missing — add it in
Settings or with link_repo.