Deploy hooks

A deploy hook is a secret URL that deploys your app when your CI calls it. Use one when a release should wait for your own checks — tests, a manual approval, a scheduled window — instead of going out on every push.

When to use one#

Connecting a repository deploys on every push to the production branch. That is the right default, but it deploys before your CI has run. A deploy hook moves the decision into your pipeline: production updates when the step that calls the hook runs, and not before.

Hooks are also how to deploy when nothing was pushed:

  • after content changes in a headless CMS, from its publish webhook
  • on a schedule, to rebuild a site that renders data at build time
  • after changing an environment variable that is read during the build

A hook always deploys the app’s production branch — the branch set in the app’s Settings under Source — at its latest commit when the build starts. It cannot choose a different branch or commit.

Make your CI the only way to production#

While Deploy on every push is on, a push to the production branch deploys straight away, and the hook then deploys a second time once your tests pass. To have your pipeline decide when production updates, turn it off:

  1. Open Settings → Deploy hooks and clear Deploy on every push to your production branch.
  2. Create a hook and call it from the step that runs after your tests.

From then on, pushes to the production branch build nothing until your CI calls the hook, or someone presses Deploy in the dashboard. Pushes to other branches still create preview deployments as before.

Create a hook#

  1. Open the app in the dashboard and go to Settings → Deploy hooks.
  2. Give the hook a name that says where it is used, such as GitHub Actions.
  3. Copy the URL. It is shown once: we store only a hash of it, so it cannot be displayed again. If you lose it, revoke the hook and create a new one.
  4. Save it in your CI as a secret, for example AHURA_DEPLOY_HOOK.

Only team owners and admins can create or revoke hooks. Members can see the list. An app can have up to 10 active hooks; the dashboard shows each one’s name, the last four characters of its URL and when it was last used.

Call it#

POSThttps://ahurasense.com/api/v2/hooks/deploy/{token}

Send a POST with no body and no headers. The token in the path is the authentication; it starts with dh_. A GET is refused, so that a link preview or a crawler fetching the URL can never start a deploy.

curl -fsS -X POST "$AHURA_DEPLOY_HOOK"

Response#

A successful call returns 202 Accepted as soon as the deployment is queued. It does not wait for the build to finish.

202 Accepted
{
  "deployment": { "ref": "dpl-8e76876577a2" },
  "status": "queued"
}
statusMeaning
queuedA new deployment was created and will build when the builder reaches it.
already_queuedA deployment of your production branch was already waiting to build, so your call joined it instead of creating a second one. ref is that deployment.

Either way, the build behind ref starts after your call and checks out your production branch as it is at that moment, so it includes every commit pushed before you called. A deployment that records one particular commit is never joined, and your call queues its own. Pushes record the commit pushed, and Deploy in the dashboard records the branch’s latest commit at the moment it was pressed.

The deployment then moves through the states described in Deployments. Follow it in the dashboard; it is listed with the trigger deploy hook.

Errors#

Every error uses the same envelope:

error
{
  "error": {
    "code": "rate_limited",
    "message": "This deploy hook has been called too often. Try again later.",
    "retry_after": 1680
  }
}
StatuscodeCauseWhat to do
404not_foundThe URL is wrong, the hook was revoked, or its app was deleted. These look the same on purpose, so the endpoint never confirms which hooks exist.Check the secret in your CI. If the hook was revoked, create a new one.
405invalid_requestThe request was a GET.Use POST.
409conflictThe app has no production environment to deploy to.Contact support with the app name.
410goneThe person who created the hook has left the team, so the hook was revoked. Later calls return 404.Have a current owner or admin create a new hook.
429rate_limitedToo many calls. retry_after and the Retry-After header give the wait in seconds.Wait and retry. Check for a loop in your pipeline.
500internalSomething failed on our side. No deployment was created.Retry. If it keeps happening, contact support.

Limits#

LimitValue
Calls per hook, including ones that return already_queued30 per hour
Calls from one IP address, to any hook60 per minute
Active hooks per app10

Security#

Anyone who has the URL can deploy your app, so treat it like a password: keep it in your CI’s secret store, never in your repository or in a log.

What someone holding a hook URL can and cannot do:

  • They can start a deployment of your production branch, within the limits above.
  • They cannot read anything — not your code, settings, logs or environment variables.
  • They cannot change settings, choose a branch or commit, or deploy a different app.

Revoke a hook

In Settings → Deploy hooks, choose Revoke on the hook. It stops working on its very next call; there is no cache to wait out. A revoked hook stays in the list, struck through, as a record of who could deploy and until when. Revoking cannot be undone — create a new hook instead.

A hook is also revoked automatically when the person who created it leaves the team, or when their account is deleted. If a pipeline must outlive a particular person, have the hook created by an account that will stay on the team.

Something missing or wrong on this page? Tell us, and quote the page title.