# GitHub webhook

How Docup receives push notifications from GitHub: events, signature verification, de-duplication and responses.

This page covers how Docup receives push notifications from GitHub, including endpoint configuration, signature verification, de-duplication, and event behaviors. You do not configure this when you use the hosted Docup. This page explains what happens behind the **Updates on push** behavior, and is useful if you run your own copy of Docup. See [Local setup](/development/local-setup).

## Endpoint

```text
POST /api/webhooks/github
```

Set it as the webhook URL of your GitHub App, with the same secret as `GITHUB_WEBHOOK_SECRET`, and subscribe the app to the **push** event. The `installation` event is also handled.

## Verification

Every request must carry an `X-Hub-Signature-256` header. Docup computes an HMAC-SHA256 of the raw request body with the webhook secret and compares it with the header in constant time. A missing, malformed or wrong signature returns `401 Bad signature`.

## De-duplication

GitHub sometimes sends a delivery again. Docup stores each `X-GitHub-Delivery` id in Redis for 24 hours and answers `200 Already handled` to repeats. If handling fails, the id is removed, so **Redeliver** in the GitHub app settings works. If Redis cannot be reached, the delivery is handled anyway, because an update that runs twice finds nothing new the second time.

## Events

| Event | Behavior |
| --- | --- |
| `push` to the default branch | Starts a docs update for every project made from that repository and installation whose first run is complete. Responds `202 Updating N`. |
| `push` to any other branch, or a deleted ref, or without an installation | `200 Ignored`. |
| `installation` with action `deleted` | Forgets the installation in Docup. `200 Ok`. |
| `installation` with any other action | `200 Ok`. |
| Any other event | `200 Ignored`. |

## Responses

| Status | Meaning |
| --- | --- |
| `200` | Handled, ignored, or already handled. |
| `202` | Updates were started. |
| `401` | Bad signature. |
| `500` | Handling failed. GitHub can redeliver. |

The endpoint answers quickly, since GitHub gives up after ten seconds. The update itself runs in the background.

## What an update does with a push

The task is started with a debounce: pushes less than a minute apart become one update, at most ten minutes after the first, and one project's updates run one after another. Starting the task throws if the background service cannot be reached, so GitHub sees the delivery fail and can send it again. See [Keeping docs up to date](/guides/updates) for what the update does.

### Update execution limits

The background update task processes changed files and rewrites pages under these exact limits:

| Limit | Value | Behavior |
| --- | --- | --- |
| Daily updates | 15 updates | The maximum updates allowed per workspace per UTC day. Pushes that change no documented code are free and do not count toward this limit. |
| Parallel page writes | 3 pages | The number of pages processed in parallel during an update. |
| GitHub file comparison | 300 files | If a push contains 300 or more changed files, or if GitHub cannot provide a comparison (such as after a force push), Docup treats it as if everything changed and rewrites all pages. |
