# Deploy on every push

> Add an app's push webhook to your git host so that each push to the app's branch builds and deploys that commit.

Source: https://docs.coritan.com/apps/push-webhook/

In the dashboard:

- /dashboard/apps/…/settings: https://www.coritan.com/dashboard/apps

Every app that builds from git has a push webhook. Add its address and secret to the repository at your git host, and each push to the app's branch starts a deployment of the pushed commit. It works with GitHub, GitLab, Gitea, Forgejo and Gogs. The **Push webhook** card on the app's **Settings** tab shows the address and makes new secrets.

## Before you begin

- The app must build from git. An app that runs an image has no push webhook.
- You need the webhook's secret. It appears once, when you create the app, in the **Push webhook secret** dialog. If you did not save it, [make a new secret](#make-a-new-secret).
- You need permission to add webhooks to the repository at your git host.
- For an organization's app, you need the owner or admin role in the organization to make a new secret.

## Add the webhook at your git host

1. Find the webhook's address. Open the app from **Apps** in the [dashboard](https://www.coritan.com/dashboard/apps), select the **Settings** tab, and copy the **Payload URL** from the **Push webhook** card. Through the API, it is the app's `webhook.url`, such as `https://api.coritan.com/api/v1/apps/hooks/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21`.
2. At your git host, open the repository's webhook settings and add a webhook with that address.
3. Where the host asks for a content type, choose `application/json`. GitHub's form encoded deliveries work too.
4. Paste the app's webhook secret into the webhook's secret field. GitLab calls it the secret token.
5. Choose push events. The webhook ignores every other event, so choosing more only adds deliveries that do nothing.
6. Save the webhook. GitHub then sends a test delivery called a ping, which the webhook answers with `200`, so GitHub shows the webhook as working.

The address takes no access token. The secret proves that a delivery comes from your git host.

The **Push webhook** card also shows what to choose at your git host: **Content type** `application/json` and **Events** `Push`. Its **Secret** row reads `Set, ends in f0a9` with the last four characters of the secret, or `Not set`. While no secret is set, the card says `Pushes are refused until the webhook has a secret`.

## What a push deploys

A delivery starts a deployment when all of these hold:

- It is a push to a branch, and that branch is the app's branch. A push to another branch, a tag or a deleted branch deploys nothing.
- It comes from the app's own repository. When the delivery names its repository, we compare the host and path with the app's repository address.
- It names the commit at the head of the push.
- The app is `active`.

The deployment builds the pushed commit and records its message and author. Its `trigger` is `push` and its `actor_type` is `webhook`, so you can tell it apart from one you started. When several pushes arrive close together, each starts a deployment, and the newest to go live cancels any older one still on its way ([Deployments](/apps/how-apps-work/#deployments)).

A delivery deploys at most once. We remember each delivery's ID for a day, so a redelivery from your git host is ignored. A push of the commit that the app's latest push deployment already has is ignored too. To run the same commit again, [redeploy](/apps/deploy-an-app/#redeploy) the app.

An app can deploy 30 pushes in 5 minutes. Past that, the webhook answers `429` until the 5 minutes have passed.

## How a delivery is checked

We check the delivery against the app's secret before we read it. The header depends on the git host:

| Git host | Header | What it holds |
| --- | --- | --- |
| GitHub | `X-Hub-Signature-256` | `sha256=` and the HMAC SHA-256 of the body, keyed with the secret |
| Gitea, Forgejo, Gogs | `X-Gitea-Signature`, `X-Forgejo-Signature` or `X-Gogs-Signature` | The HMAC SHA-256 of the body, keyed with the secret |
| GitLab | `X-Gitlab-Token` | The secret itself |

We read the delivery's ID from the host's delivery header, such as `X-GitHub-Delivery` or `X-Gitlab-Event-UUID`. A delivery can be up to 10 MB.

## Make a new secret

Make a new secret when you have lost the old one, or when someone who should not have it may have seen it. The new secret appears once, and the old one stops working at once. Paste the new secret into your git host's webhook straight away, or pushes answer `401` and deploy nothing.

1. On the app's **Settings** tab, select **Rotate secret…** on the **Push webhook** card. When the app has no secret, the button is **Create secret…**.
2. Select **Rotate secret**, or **Create secret**, to confirm.
3. The **New webhook secret** dialog shows the **Payload URL** and the **Secret**. Copy both, or select **Copy all**, then select **I have saved it**.

A message confirms `Webhook secret rotated.` or `Webhook secret created.`

The **Secret** row, and the app's `webhook.secret_hint` in the API, show the last four characters of the secret, so you can check which secret your git host holds.

## Result

Each push to the app's branch appears as a new deployment in the app's history, with the commit's ID, message and author. On the **Deployments** tab, its source reads `Push to main` and `The push webhook`, and through the API its `trigger` is `push`. Your git host's delivery log shows each answer: `201` for a push that started a deployment, `202` for a delivery the webhook ignored and why, and `200` for a ping.

## Troubleshooting

Your git host's delivery log shows the webhook's answer to each delivery. A `202` answer names what the webhook ignored in `ignored`:

`push to feature; the app deploys main`
: The push was to another branch. Push to the app's branch, or change the branch the app deploys ([Change the settings](/apps/app-settings/#change-the-settings)).

`tag push`
: Tags do not deploy on their own. GitLab's tag pushes show as `not a push: tag_push`. Deploy the tag yourself ([Choose what to deploy](/apps/deploy-an-app/#choose-what-to-deploy)).

`push from another repository`
: The delivery came from a repository other than the app's, such as a fork, or the repository moved. Correct the app's repository address.

`the app is suspended`
: We suspended the app. [Contact support](/support/conversations/) to find out why.

`this delivery was already received` or `the latest push deployment already has this commit`
: The push was deployed already. Redeploy the app to run it again.

`not a push: issues`
: The webhook sends other events. Choose push events only.

The webhook refuses a delivery with one of these, in `detail`:

`Sign the delivery with the app's webhook secret.`
: The delivery has no signature. Paste the secret into the webhook at your git host.

`The signature does not match the app's webhook secret.`
: Your git host holds another secret, often one from before you made a new one. Paste the current secret, or make a new one.

`This app has no webhook secret. Create one in its settings.`
: Select **Create secret…** on the **Push webhook** card ([Make a new secret](#make-a-new-secret)), then paste the secret into your git host.

`No git app with this id.`
: The address does not belong to an app that builds from git. The app may have been deleted. Copy the **Payload URL** from the app's **Settings** tab again.

`The delivery is not JSON.`
: Set the webhook's content type to `application/json`.

`Too many pushes to deploy. Wait a few minutes.`
: The app deployed 30 pushes in the last 5 minutes. Later pushes deploy again once the 5 minutes have passed.

`Builds are not available yet on this platform.`
: The platform cannot build apps at the moment. Try again later, or [contact support](/support/conversations/).

## Related

- [Deploy an app](/apps/deploy-an-app/)
- [Read a deployment and its build log](/apps/read-a-deployment/)
- [Change or delete an app](/apps/app-settings/)

## With the API

### Make a new secret through the API

[`POST /api/v1/client/apps/{app_uuid}/webhook/rotate`](/api/reference/client/apps/#op-post-api-v1-client-apps-app-uuid-webhook-rotate) makes a new secret. For an organization's app, send it to [`POST /api/v1/orgs/{org_slug}/apps/{app_uuid}/webhook/rotate`](/api/reference/organizations/organization-apps/#op-post-api-v1-orgs-org-slug-apps-app-uuid-webhook-rotate).

```bash
curl -X POST https://api.coritan.com/api/v1/client/apps/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21/webhook/rotate \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

```json
{
  "webhook": {
    "url": "https://api.coritan.com/api/v1/apps/hooks/3c9e2f4a-8b1d-4f6e-a2c7-5d8b9e0f1a21",
    "secret": "4f1d2c3b5a69788796a5b4c3d2e1f0a94f1d2c3b5a69788796a5b4c3d2e1f0a9"
  }
}
```

`secret` appears in this answer only. An app that runs an image answers `409` with `{"error": "not_git", "message": "Only an app that builds from git has a push webhook"}`.

### The webhook's answers

Your git host sends deliveries to `POST /api/v1/apps/hooks/{app_uuid}`, the address in `webhook.url`. It is not in the API reference, because only a git host calls it. A success answers with `ok` set to `true` and the header `Cache-Control: no-store`:

```json
{"ok": true, "deployment": "0b6f3d2e-7a41-4c59-8e1d-2f9a6b3c4d5e", "status": "queued"}
```

| Status | Body | Meaning |
| --- | --- | --- |
| `200` | `{"ok": true, "event": "ping", "app": "<app uuid>", "branch": "main"}` | A ping. Nothing deploys. |
| `201` | `{"ok": true, "deployment": "<deployment uuid>", "status": "queued"}` | The push started this deployment. |
| `202` | `{"ok": true, "ignored": "tag push"}` | The delivery deploys nothing, for the reason given. A repeated delivery also has `"duplicate": true`. |

A refusal has the error in `detail`, as in `{"detail": {"ok": false, "error": "signature_mismatch", "message": "The signature does not match the app's webhook secret."}}`:

| Status | `error` | Cause |
| --- | --- | --- |
| `400` | `invalid_payload` | The body is not JSON. |
| `401` | `signature_missing` | The delivery has no signature header. |
| `401` | `signature_mismatch` | The signature does not match the app's secret. |
| `403` | `webhook_not_configured` | The app has no secret. |
| `404` | `not_found` | No app that builds from git has this ID, or Apps is turned off (`Not found.`). |
| `409` | `refused` | The deployment was refused, and `message` says why. |
| `413` | `too_large` | The body is larger than 10 MB. |
| `422` | `repository_invalid` | The app's repository address cannot be built from, and `message` says why. |
| `429` | `rate_limited` | The app deployed 30 pushes in 5 minutes. `Retry-After` gives the wait in seconds. |
| `503` | `registry_not_configured` | The platform cannot build apps at the moment. |

To deploy from a CI job instead, send a deployment request with an access token ([Deploy through the API](/apps/deploy-an-app/#deploy-through-the-api)).

## API

- `POST /api/v1/client/apps/{app_uuid}/webhook/rotate`: A new secret for the push webhook, shown once; the old one stops verifying at once (https://docs.coritan.com/api/reference/client/apps/#op-post-api-v1-client-apps-app-uuid-webhook-rotate)
- `POST /api/v1/orgs/{org_slug}/apps/{app_uuid}/webhook/rotate`: A new secret for the push webhook, shown once; the old one stops verifying at once (https://docs.coritan.com/api/reference/organizations/organization-apps/#op-post-api-v1-orgs-org-slug-apps-app-uuid-webhook-rotate)
