logbook/Docs

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:

  1. 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 the link_repo MCP tool.
  2. 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:

FieldValue
Payload URLhttps://logbook.gvy.ai/api/webhooks/github
Content typeapplication/json
Secretthe project's webhook secret (see below)
EventsLet 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:

  1. Finds every linked item. It searches each item's links for one that references <owner>/<repo>/pull/<number> — a pr link you added. Any stored URL variant matches.
  2. Crosses matched items off — status becomes done, unless the item is already done (see the rule below).
  3. 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.
  4. 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).
  5. Records the changed files as item_files rows on each matched item (capped at 300 paths per PR). These power the history_for_files MCP tool and the changed_files field on pull_item.
  6. 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 the owner/name/pull/number path).
  • 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 done when the merge arrives, that step is skipped — no auto-resolution, no file summary. (The merge note and item_files are 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:

  1. In the repo on GitHub go to Settings → Webhooks and click the webhook.
  2. Open Recent Deliveries. GitHub sends an initial ping when the hook is created — logbook answers it 200 { ok: true }.
  3. Merge a linked PR and check the delivery — expect 200 with 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).
  • 401 Bad 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.
  • 400 Invalid JSON — the content type is not application/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.