# GitHub webhook trigger

> Run a workflow on GitHub repository events: pushes, pull requests, and new issues, with per-repo setup and signature verification.

Source: https://cotera.co/docs/reference/webhooks/github

---

Run a workflow on repository events: a push, a pull request opened or merged, a new issue. The raw GitHub event payload becomes the workflow's input.

For behavior shared by every provider (the delivery URL, testing), see the [webhook triggers reference](https://cotera.co/docs/reference/webhooks.md).

## Prerequisites

GitHub webhooks are **auto-registered**: Cotera creates the repository webhook for you over GitHub's API, so you need GitHub access connected first — either:

* a connected GitHub account, or
* a GitHub API token (`apiKey`) with permission to manage webhooks on the target repository.

Without one, install fails with `github_credentials_missing`.

## Setup

1. Add a **From an app** trigger and pick **GitHub**.
2. Choose the event (see below) and set the target repository — **owner** and **repo**.
3. Save. Cotera registers a repository webhook for the matching GitHub event type, pointed at the trigger's unique URL and signed with its Cotera-generated secret.

Deleting the trigger removes the repository webhook from GitHub. Owner and repo are matched case-insensitively, so `Cotera/App` and `cotera/app` resolve to the same trigger.

## Events

Every event requires the repository config (`owner`, `repo`).

| Event key                       | Fires when                            | GitHub event   |
| ------------------------------- | ------------------------------------- | -------------- |
| `push`                          | Commits are pushed to the repository  | `push`         |
| `pull_request.opened`           | A pull request is opened              | `pull_request` |
| `pull_request.synchronize`      | Commits are pushed to a PR branch     | `pull_request` |
| `pull_request.reopened`         | A pull request is reopened            | `pull_request` |
| `pull_request.ready_for_review` | A draft PR is marked ready for review | `pull_request` |
| `pull_request.merged`           | A pull request is merged              | `pull_request` |
| `issues.opened`                 | An issue is opened                    | `issues`       |

GitHub has no dedicated "merged" event: a merge arrives as a `pull_request` delivery with action `closed` and `pull_request.merged: true`. Cotera narrows that to `pull_request.merged`, so a merge trigger fires only on real merges, not every close.

## Payload

The raw GitHub event payload is passed straight through as the workflow input. GitHub also sends `X-GitHub-Event` (the event type) and `X-GitHub-Delivery` (a unique delivery id used to deduplicate retries). A `pull_request.opened` delivery looks like:

```json
{
  "action": "opened",
  "number": 1,
  "pull_request": {
    "id": 1374949,
    "number": 1,
    "state": "open",
    "title": "Add a new feature",
    "body": "This pull request adds a new feature.",
    "user": { "login": "octocat", "id": 1, "type": "User" },
    "head": {
      "ref": "feature-branch",
      "sha": "a1b2c3d4…",
      "repo": { "full_name": "octocat/Hello-World" }
    },
    "base": { "ref": "main", "sha": "d4e5f6a7…", "repo": { "full_name": "octocat/Hello-World" } },
    "html_url": "https://github.com/octocat/Hello-World/pull/1",
    "merged": false
  },
  "repository": {
    "id": 1296269,
    "name": "Hello-World",
    "full_name": "octocat/Hello-World",
    "owner": { "login": "octocat", "id": 1 }
  },
  "sender": { "login": "octocat", "id": 1, "type": "User" }
}
```

`push` and `issues` deliveries carry GitHub's standard payloads for those event types. A `pull_request.merged` delivery is a `pull_request` payload with `action: "closed"`, `pull_request.merged: true`, and the `merged_at` / `merged_by` / `merge_commit_sha` fields set.

## Signature verification

GitHub signs each delivery with the trigger's secret and sends it in the `X-Hub-Signature-256` header:

```
X-Hub-Signature-256: sha256=<hex-hmac>
```

The signature is an HMAC-SHA256, in hexadecimal, of the raw request body. Cotera verifies it on every delivery — a request with a missing or invalid signature is rejected before your workflow runs.

