Github connections

The GitHub integration lets recipes listen for repository events through the Neurocad GitHub App, call the GitHub REST API to read files and pull-request data, and route events to the right recipe by repository. Two things have to be in place before a GitHub-driven recipe will run end-to-end:

  1. The GitHub account is connected in /connect.
  2. The Neurocad GitHub App is installed on the repos you want to listen to or read from — and the install grants access to those repos.

There is no per-user link step the way Slack has — GitHub events aren't user-invoked, so the bot/user identity gating that drives /neurocad link doesn't apply here.

The rest of this doc walks through each of those, then covers the GitHub trigger and GitHub action nodes.

1. Connect a GitHub account or organisation

In Neurocad, go to /connect → GitHubAdd connection. Unlike a typical OAuth app, this kicks off a GitHub App install flow:

  • Neurocad redirects to the public https://github.com/apps/<neurocad-app-slug>/installations/new install page on github.com.
  • Choose which user or organisation to install on.
  • Choose All repositories or Only select repositories, and pick the repos. This is the access scope — Neurocad can only see and act on repos covered by this install.
  • Authorize. GitHub redirects back to /connect/provider/githubApi, the install record is fetched, and the new connection appears in your Connect list keyed by the GitHub account login.

What's stored on the connection: the GitHub App installationId, the current installation access token (a short-lived bearer; see below), the token's expiry timestamp, the stable account id, the account login, and the account type (User / Organization). The stable account id is what every trigger registration and inbound webhook routes on — install ids rotate when an install is removed and re-added, but account ids don't.

The token refreshes itself

GitHub App installation tokens are valid for one hour. You don't need to do anything to keep them fresh — before each request, the credential checks the stored expiry, and if it's missing, unparseable, or within 5 minutes of expiring, it mints a new one through the platform's signed App JWT and updates the stored token. Subsequent calls in the same execution use the refreshed token.

If the platform can't mint a token (the GITHUB_APP_ID or GITHUB_APP_PRIVATE_KEY_B64 environment variables are missing on the host), executions fail with a clear error: "GitHub installation token is stale but this execution host has no GitHub App token minter configured. Ensure GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY_B64 are set on the host." That's an operator config issue, not a user-fixable one.

Connection states

Like Slack, the connection can drift away from a healthy state via lifecycle events GitHub delivers to the App:

  • uninstalled — Someone removed the App from the account/org (installation.deleted). The connection is marked uninstalled and has to be added again before recipes referencing it can deploy or run.

There's no needs_reauth state for GitHub — the App's install is either present or not. Tokens are refreshed transparently as long as the install exists.

Removing a connection from Neurocad

If you delete the connection from /connect, Neurocad calls DELETE /app/installations/{installationId} against GitHub using the App-level JWT (an installation token can't manage its own install). This actually removes the App from the GitHub account/org — not just from Neurocad. To put it back, run the install flow again from /connect.

2. Install the App on the right repositories

This is the GitHub equivalent of "invite the bot to the channel." The App can only see repos that are explicitly covered by the install. You manage that on github.com, not in Neurocad:

  • On github.com, go to your account or org Settings → Integrations → Applications → Installed GitHub Apps, click the Neurocad App, and click Configure.
  • Under Repository access, switch to All repositories, or pick Only select repositories and add/remove the specific repos.
  • Permissions are pinned by the App definition — you don't tweak them per install. Adding new permission scopes to the App later requires each install owner to accept the new permissions on github.com before the new scopes become available.

If a recipe references a repository the App isn't installed on, the GitHub trigger never fires for it (events for that repo are simply not delivered to Neurocad) and the GitHub action node gets 404s when it tries to read files. Fix is always: install or extend the App's repository access on github.com.

The GitHub trigger node

GitHub trigger starts a recipe when a configured webhook event arrives for a specific repository. The credential is githubApi. With the GitHub App delivery model, webhooks arrive automatically for every repo the App is installed on — there's a single Request URL configured on the App itself (/api/github/webhooks), so activation is purely about writing a routing entry. There's no per-repo POST /repos/{owner}/{repo}/hooks call.

Repository

A repository field with two modes:

  • From list — Searches repositories the connected install can see. The picker calls back to GitHub through the installation token.
  • By full name — Paste owner/repo directly, e.g. octocat/hello-world. Useful when you know exactly which repo you want without browsing.

The chosen repository is stored as owner/repo and lowercased on activation. That string is the trigger's routing key.

Events

Multi-select of GitHub webhook event types this trigger fires on. The list is the repository-routable subset of GitHub's event taxonomy — events whose payloads reliably carry a top-level repository. Pick Any event (the leading *) to receive every supported event for the chosen repository.

  • Any event — Wildcard. Every repository event GitHub delivers for the selected repo.
  • Push — A push to a branch or tag.
  • Pull request — PR opened, edited, closed, reopened, labeled, synchronized, marked ready, locked/unlocked, or review requested.
  • Pull request review / Pull request review comment — Review submitted/edited/dismissed; comment on a diff created/edited/deleted.
  • Issues / Issue comment — Issue lifecycle and comment lifecycle.
  • Release — Release published, unpublished, created, edited, deleted, or pre-released.
  • Create / Delete — Branch or tag created / deleted. (Also: repo created.)
  • Check run / Check suite / Status — CI status updates.
  • Deployment / Deployment status — Deployment lifecycle.
  • Workflow run — A GitHub Actions workflow run completes.
  • Repository / Public / Fork / Star / Watch — Repo-level lifecycle and engagement events.
  • Member / Team add / Label / Milestone — Collaboration-surface events.
  • Page build / Repository import / Security alert / Wiki page (gollum) / Deploy key / Commit comment — Various repo subsystems.

Account-level, organization-level, and App-lifecycle events (e.g. installation, installation_repositories, installation_target) are deliberately not in this list — they require an account-scoped trigger shape, which is not the contract this node implements. Those events are still consumed internally by the platform for credential lifecycle (e.g. flipping a connection to uninstalled), they just aren't routed to user recipes.

Listen / Capture

The property panel's Listen button works the same way as for Slack: click Listen, then push a commit / open a PR / etc. on the watched repo, and Neurocad captures one real GitHub event so the rest of the graph has a real payload to wire against.

Routing internals (informational)

Under the hood, activation registers an entry keyed by:

  • Provider type: githubApi
  • External org id: the credential's account id (the stable GitHub account id the App is installed on)
  • Routing key: owner/repo lowercased
  • Routing config: { owner, repo, events }

When a webhook arrives at /api/github/webhooks, the platform:

  1. Verifies the HMAC SHA-256 signature using the App's webhook secret (x-hub-signature-256).
  2. Dedupes by x-github-delivery.
  3. Derives the routing key from repository.full_name in the payload.
  4. Matches against registered entries by (accountId, owner, repo, events). Wildcard subscriptions (* in events) match any event for that repo; explicit subscriptions match by x-github-event header.

You don't configure any of that — it's a contract the trigger node satisfies on your behalf — but knowing the shape helps when something doesn't fire.

The GitHub action node

GitHub is the outbound action node. Like Slack, it exposes a Resource + Operation selector. The credential is githubApi, and every request goes out through the installation token using GitHub's pinned API version (X-GitHub-Api-Version: 2022-11-28).

Resources and operations

  • Compare — List changed files
  • File — Get
  • Pull request — List files

Compare → List changed files

Compare two refs and return one workflow item per changed file. GitHub includes up to 300 changed files per compare — beyond that, you'll need a different shape.

Inputs:

  • Repository — Pick from list, or by full name.
  • Before — Earlier commit / branch / tag in the compare range. The default placeholder is ={{ $json.before }}, which maps directly from a push event's before field, so wiring this from a GitHub trigger is one click.
  • After — Later commit / branch / tag, with placeholder ={{ $json.after }}.

This is the heart of the GitHub file change to Slack preset — push a commit, fan out one item per changed file, send each to a downstream node.

File → Get

Fetch a single file's contents at a chosen ref.

Inputs:

  • Repository — Pick from list, or by full name.
  • File path — Two modes:
    • Browse tree — File picker that walks the repository tree at the default branch. Depends on Repository.
    • By path — Paste a path directly, e.g. images/logo.png.
  • Ref — Branch, tag, or commit. Leave empty to use the repo's default branch.

The response is requested as application/vnd.github.raw and exposed as a binary on the workflow item under field name data. Downstream nodes (Library, Schematic, Create share link) can wire the binary directly without needing a base64 decode step.

Pull request → List files

List the files changed by a pull request, paginated. GitHub's per_page is capped at 100; this operation paginates through all pages automatically.

Inputs:

  • Repository — Pick from list, or by full name.
  • Pull request number — The PR number (e.g. 42).

What happens on 404 / 401 / installation not found

Most GitHub action errors come down to App access: the installation can't see the repo you asked about. The fix is on github.com — extend the install's repository access — not in Neurocad. If the install was uninstalled entirely, reconnect from /connect.

Common GitHub-driven recipe shapes

The built-in GitHub file change to Slack preset is the canonical example:

GitHub trigger (push to a repo)
→ GitHub (compare → List changed files, before/after wired from $json)
→ GitHub (file → Get, fetches the file at the new ref)
→ Library
→ Create share link
→ Slack (message → Send the share URL to a chosen channel)

The preset's first canvas note spells out the same checklist: "This recipe requires your GitHub connection. Navigate to /connect and add it there first. Then install the Neurocad GitHub app on the repository you want to watch. Select GitHub trigger and choose the repository, branch, and file path to watch."

A simpler PR-driven shape:

GitHub trigger (pull_request)
→ GitHub (pull request → List files)
→ GitHub (file → Get for the changed file)
→ Library
→ Create share link
→ Slack (message → Send to #ops)

You can also use a GitHub trigger purely as a manual ping mechanism — fire on release or workflow_run to kick off downstream regeneration whenever a release or CI run completes.

Deploying a GitHub-driven recipe

GitHub recipes are deployable — they start from a server-side trigger and only use Connect / EDA / MDA nodes. Once the recipe is shaped:

  1. Click the recipe title menu → Deploy. The deployment lands in /automate as inactive.
  2. In /automate, toggle the recipe on. Activation registers the trigger keyed on the connection's stable account id and the chosen owner/repo + events.
  3. From that point, GitHub deliveries for the watched repo flow through /api/github/webhooks, get matched against your registration, and start the recipe.

Two activation-time errors are worth knowing:

  • "GitHub credential is missing the account id — cannot register the trigger. Reconnect the GitHub App so the account id is captured." — You have an older credential from before stable-account-id capture, or the install partially populated. Reconnect from /connect to refresh it.
  • "Pick at least one GitHub event for the trigger to listen to, or pick 'Any event' to receive every event for the repository." — The Events field is empty. Add at least one event or pick *.

There is no command-word collision check the way Slack has, because GitHub doesn't use slash commands here — two recipes can subscribe to the same (repo, event) and both will fire on the same delivery.

Differences from Slack at a glance

If you've read the Slack doc, here's the quick translation:

  • Connect flowSlack: OAuth v2 workspace install (Slack-issued bot token). GitHub: GitHub App install (App JWT mints installation tokens).
  • Token lifetimeSlack: Bot token doesn't expire. GitHub: Installation token expires hourly, refreshed transparently.
  • Routing keySlack: team_id (workspace). GitHub: accountId (stable user/org id).
  • Per-resource accessSlack: Bot must be invited to channels (/invite @Neurocad). GitHub: App must be installed on repos (manage on github.com).
  • Per-user identitySlack: /neurocad link binds Slack user → Neurocad user. GitHub: None — events aren't user-invoked here.
  • Per-user gatingSlack: Who can invoke setting (org-members / anyone-linked) on user-invoked triggers. GitHub: None.
  • Webhook signatureSlack: HMAC SHA-256 v0 with timestamp skew (x-slack-signature + x-slack-request-timestamp). GitHub: HMAC SHA-256 (x-hub-signature-256).
  • Replay keySlack: event_id (events) / trigger_id (commands). GitHub: x-github-delivery.
  • User-invoked routing configSlack: Command word (must be unique per workspace). GitHub: n/a.
  • Recipe-side trigger configSlack: Channel + events + command word + exposure. GitHub: Repo + events.
  • Action node resourcesSlack: Channel / Message / Reaction / File / User. GitHub: Compare / File / Pull request.

Quick checklist when something doesn't fire

If a GitHub-driven recipe isn't firing, walk this list — almost every "it's not working" report is one of these:

  • The GitHub connection exists in /connect and isn't uninstalled.
  • The Neurocad GitHub App is installed on the specific repository the recipe watches (check Configure on github.com → Installed GitHub AppsNeurocad).
  • The recipe's deployment is active in /automate.
  • The trigger has at least one event selected (or the * wildcard).
  • The repository spelling in the trigger matches owner/repo exactly (the picker avoids typos; By full name is where typos creep in).
  • For action-node 404s: the install can see the repo and the path / ref / PR number actually exist. Try Browse tree on the file picker as a quick "can the install see this repo?" check.
  • If executions are 401-ing despite the connection looking fine, the host's GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY_B64 are likely missing — operator config issue, not user-fixable.

If all of those pass and it's still silent, capture a real event with the GitHub trigger's Listen button. That's the cheapest test for "is GitHub actually reaching this workflow," separate from whether downstream nodes are succeeding.

Last updated May 7, 2026