GitHub Apps, OAuth Apps, Webhooks, Checks API, and Event-Driven Integrations: Concepts, Architecture, and Mental Model
Build the mental model for GitHub App identity, OAuth/user alternatives, short-lived installation tokens, verified webhook delivery, durable idempotence, and rich Checks feedback.
Learning objectives
- Distinguish GitHub App, OAuth App, PAT, and machine-user identity models.
- Explain App registration, installation, private-key/JWT, installation-token, and permission boundaries.
- Verify webhook HMAC and use delivery IDs for replay/idempotency control.
- Explain out-of-order/redelivery behavior and current-state reconciliation.
- Choose Checks versus commit-status feedback.
1. The problem: integrations are identities and services, not scripts with tokens
Chapter 26 treated the API as a versioned, permissioned contract. Chapter 27 adds a production concern: who is the automation, what event caused it to act, and how can the receiver prove that event really came from GitHub? A long-lived personal token copied into a daemon blurs those boundaries. A GitHub App gives automation its own identity, fine-grained permissions, installable repository scope, central webhooks, and short-lived installation access tokens.
The operating question is therefore not “how do I call the API?” It is “which identity model matches this service, what is the smallest installation/permission/event surface, how is an inbound delivery authenticated and deduplicated, and how does the service report a durable result?”
2. Mental model: event → verified delivery → scoped app identity → feedback
flowchart TD
E["GitHub event"] --> W["GitHub App webhook"]
W --> V["Verify HMAC on raw body"]
V --> D["Deduplicate X-GitHub-Delivery"]
D --> Q["Queue / state reconciliation"]
Q --> I["Installation token"]
I --> A["GitHub REST/GraphQL API"]
A --> C["Check run or other scoped feedback"]
C --> O["Logs + delivery/audit evidence"]
Every arrow is a trust boundary. GitHub emits an event to the App
webhook. The receiver verifies
X-Hub-Signature-256 against the exact raw request bytes
before parsing. It uses X-GitHub-Delivery as the
event/delivery identity so a redelivery cannot accidentally repeat a
mutation. Only after the event is accepted does the integration
obtain or reuse a short-lived installation token and call the API
within the installation's repository selection and App permissions.
A check run is one possible feedback channel; logs, delivery
records, and API request IDs provide operational evidence.
3. GitHub App, OAuth App, PAT, and machine user are different identity models
| Identity | Acts as | Access shape | Credential posture | Good fit |
|---|---|---|---|---|
| GitHub App installation | The app installation/bot | Fine-grained permissions + selected/all repositories | Installation token expires after one hour; generated from App identity | CI/security bots, integrations, services that must outlive a person |
| GitHub App user access token | App acting on behalf of a user | Intersection of app permission, installation access, and user access | Can be configured to expire; current default expiration is eight hours when expiration is enabled | User-driven product features that still need App governance |
| OAuth App token | A user | OAuth scopes plus the user's own access | User token lifecycle; broader scope model | Legacy/user-centric integrations; some enterprise-level resource cases |
| Fine-grained PAT | A user | Selected resource owner/repositories and permissions | Longer-lived user credential chosen by owner | Personal/admin automation when an App is disproportionate |
| Machine user | A normal GitHub user account used by automation | Whatever that user is granted | Password/token/2FA/user lifecycle; may consume an enterprise seat | Compatibility edge cases, not the default service-identity pattern |
GitHub's current guidance generally prefers GitHub Apps over OAuth Apps for integrations because Apps can act independently of a user, use finer permissions, permit repository selection at installation time, and use short-lived installation tokens. Authentication and authorization remain separate: a valid installation token cannot access a repository outside that installation or an endpoint outside the App's permissions.
4. App registration, installation, JWT, and installation tokens
A GitHub App registration defines the App's metadata, callback/webhook configuration, requested permissions, and subscribed events. An installation attaches that App to a user or organization account and chooses the repositories it can access. The registration is the software identity; each installation is a tenant-specific authorization boundary.
To authenticate as the App itself, a server signs a JSON Web Token
with the App private key. Current GitHub.com JWT rules require
RS256, an iat value, an iss identifying
the App, and an exp no more than ten minutes in the
future. That JWT is not the normal repository-operation token. It is
used to call App-level endpoints such as creating an installation
access token. The resulting installation token expires after one
hour and can be narrowed to repositories/permissions below the
installation's maximum.
5. Webhook deliveries: authenticity, identity, and lifecycle
A webhook is an outbound HTTP delivery from GitHub to an endpoint
after a subscribed event. Important headers include
X-GitHub-Event (event type),
X-GitHub-Delivery (globally unique delivery/event
GUID), and—when a secret is
configured—X-Hub-Signature-256. GitHub computes the
latter as HMAC-SHA256 over the request body with the webhook secret.
Verification must use the raw bytes as received and a constant-time comparison. Parsing JSON first and reserializing it can change whitespace/encoding and invalidate a correct signature. A valid signature proves possession of the configured shared secret; it does not mean the event is relevant. After verification, also validate event type, action, installation/repository identity, and any business preconditions.
GitHub currently expects a 2XX response within ten seconds on
GitHub.com. Production receivers should perform cheap
authentication/deduplication, enqueue durable work, and acknowledge
quickly. GitHub does not automatically redeliver
failed deliveries; recent deliveries can be manually/API-redelivered
for three days. A requested redelivery keeps the same
X-GitHub-Delivery, which makes delivery-ID
deduplication an important correctness control.
6. Idempotence, replay defense, ordering, and current-state reconciliation
Webhook delivery is at-least-once in an operational sense because operators can redeliver the same event, and your network/proxy can cause duplicate application processing. Store the delivery GUID before performing irreversible work. A uniqueness constraint is stronger than an in-memory set because it survives process restarts and multiple workers.
Do not infer event order from arrival order. GitHub documents that deliveries can arrive out of order and may be delayed. If correctness depends on current repository state, use the payload to identify the resource, then perform a permissioned read of the authoritative API state before the mutation. Event timestamps help reconstruct sequence, but “latest event received” is not necessarily “latest state on GitHub.”
7. Checks API versus commit statuses
A check suite groups checks for a commit. A check run is an individual analyzer/test result with lifecycle state, conclusion, output, annotations, and optional requested actions. The older commit-status API is a simpler context + state model. Checks are preferable when a GitHub App must attach rich diagnostics to exact source lines or expose re-run/requested-action UX.
Checks: write.
8. Read-only inspection before creating an integration
Before registering an App or webhook, identify the repository and current commit that the integration would observe. This proves scope without creating credentials:
gh auth status
gh repo view OWNER/atlas-c27-integration-lab --json nameWithOwner,visibility,defaultBranchRef
gh api -H "X-GitHub-Api-Version: 2026-03-10" \
repos/OWNER/atlas-c27-integration-lab/commits/HEAD \
--jq '{sha:.sha,author:.commit.author.name}'
gh api -H "X-GitHub-Api-Version: 2026-03-10" \
repos/OWNER/atlas-c27-integration-lab/commits/HEAD/check-runs \
--jq '{total_count,checks:[.check_runs[]|{name,status,conclusion,app:.app.slug}]}'
A zero check-run count is a valid hosted state. It does not imply that a check writer should immediately receive write permission.
9. DevOps connection: an integration is production control-plane software
When an integration can react to repository events and mutate checks/issues/deployments, it participates in the delivery control plane. A forged webhook, replayed delivery, stale installation token, over-broad App installation, or unbounded retry can produce organization-wide failure. Stable interfaces, least privilege, verified ingress, durable deduplication, bounded retry, and observable outcomes are therefore application requirements—not optional hardening.
Knowledge check
Why is a GitHub App installation token better suited than a developer PAT for an organization CI service?
It represents the App installation rather than one employee, is bounded by App permissions and installation repository selection, and expires after one hour. It also survives personnel changes without inheriting a human account.
What must be verified before parsing a webhook body?
Verify the HMAC-SHA256 signature over the exact raw body using the configured webhook secret and a constant-time comparison. Parsing or reserializing first can alter the bytes.
A redelivered webhook has the same delivery GUID as the original. Should the derived mutation run again?
Not automatically. Treat the GUID as an idempotency key. If the original was already committed successfully, acknowledge the duplicate and do not repeat the mutation.
Why can a valid installation token still receive 403?
Authentication succeeded, but authorization may fail because the App lacks the endpoint permission, the installation excludes that repository, an organization policy blocks access, or the requested operation is not supported for that token type.
When are commit statuses a better fit than Checks API?
When the integration only needs a simple pass/fail/pending context and rich annotations, suites, requested actions, and App-specific check UX are unnecessary.
Summary
GitHub Apps give integrations a dedicated installable identity. The secure path is: verify event authenticity on raw bytes, classify event/action/scope, deduplicate by delivery GUID, reconcile current hosted state, obtain/reuse a short-lived installation token, perform the minimum authorized mutation, and publish observable feedback. OAuth/PAT/machine-user identities remain different tools with different lifecycle and authorization tradeoffs.
Official references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.