Deploy hook

Also known as: deploy webhook, deployment hook, deployment webhook, build hook, post-deploy hook, pre-deploy hook

Updated 2026-09-224 questions

A deploy hook is a URL that triggers or reports on a deployment. Incoming hooks accept an HTTP POST that starts a build or release, so external systems can kick off deploys without direct CI access. Outgoing hooks fire after a deploy finishes so cache purgers, monitors, chat channels and dependent pipelines can react without polling.

What is a deploy hook and how does it work?

A deploy hook is a URL involved in a deployment, used in one of two directions. An incoming deploy hook is a URL your CI/CD system exposes; anyone (or anything) with the URL can POST to it and start a build or release. An outgoing deploy hook is a URL your pipeline calls after a deploy step finishes so a downstream system can react.

The mechanics are simple HTTP:

  • The CI system registers a unique URL for a pipeline, usually with a per-hook secret baked into the path or checked via a signature header. Example: https://ci.example.com/hooks/abcd1234.
  • Someone (a CMS, a cron job, a chatops bot, an operator) sends POST to that URL. The CI system authenticates the call, records who fired it, and enqueues a pipeline run.
  • On the outgoing side, the pipeline's last actions POST to third-party URLs (a Slack incoming webhook, a Cloudflare purge endpoint, a monitoring "deploy marker" API) with the commit SHA, the environment and the run status as the payload.

The payload matters. Most tools accept an optional JSON body on incoming hooks and forward its fields as pipeline variables (ref, env, image_tag), so a single hook URL can drive many deploy targets by varying the POST body. Outgoing hooks typically send a structured JSON body containing the commit, the pipeline URL, the duration and whether the deploy succeeded.

Two things separate a well-run deploy hook from a stray URL sitting on a wiki. First, authentication beyond the secret path: an HMAC signature verified against a shared key, or an IP allowlist, or a short-lived token. Second, idempotency: if the same POST arrives twice (the caller retried, the network hiccuped), the pipeline either de-duplicates by request ID or produces the same end state so the double fire is harmless.

Incoming vs outgoing deploy hooks

The two are often conflated because the vendor UI often just calls both "deploy hooks", so it helps to keep them separate in your head.

Incoming Outgoing
Direction External system to your CI Your CI to external system
HTTP role Your CI hosts the URL Your CI calls the URL
Typical caller CMS, cron, chatbot, another CI CI action at end of pipeline
Auth model Secret path + HMAC/IP allowlist Bearer token / signed request
Common use Trigger a rebuild when non-code content changes Purge cache, notify chat, mark a deploy in APM
Fails how Extra run, or an unauthorized call ignored Missed notification; deploy already succeeded

An incoming hook is a trigger; an outgoing hook is a notifier. A modern delivery pipeline usually has one of the former and several of the latter.

Why do deploy hooks matter?

Deploy hooks are the small piece of glue that keeps a delivery system open. Without them, everything that wants to start a deploy has to either commit to the repo (noisy, wrong for content changes) or hold CI credentials (risky, hard to revoke), and everything that wants to know a deploy happened has to poll the CI API.

Three concrete jobs deploy hooks do better than the alternatives:

  • Decouple content from code. A marketing team editing pages in a headless CMS shouldn't need a git commit to publish. The CMS fires an incoming deploy hook when an editor clicks "publish", the pipeline rebuilds the static site with the new content, and the code repo stays clean. This is the whole reason static-site platforms invented "build hooks" in the first place.
  • Fan out post-deploy work. After a production deploy, a surprising number of systems need to know: the CDN needs a purge, the APM needs a deploy marker for regression detection, Slack needs a message, the status page needs an entry, downstream services need a rebuild against the new API. Wiring each of those as an outgoing HTTP action from the pipeline keeps the fan-out visible in one place (the pipeline definition) instead of scattered across cron jobs.
  • Bridge tools that don't share a repo. A monorepo deploy might need to trigger a downstream repo's pipeline, or a partner's CI. An outgoing deploy hook is the lowest-common-denominator API between two CI systems that don't otherwise integrate.

Trade-offs to know before scattering hooks everywhere. Every incoming hook is an unauthenticated write endpoint until you add auth, which means every URL that leaks is a free deploy. Every outgoing hook adds latency and a failure mode to the tail of the pipeline (a slow Slack API can add seconds to every run), so mark non-critical hooks as ignore_errors or run them in parallel with the primary path.

How do popular CI/CD tools handle deploy hooks?

Every major CI/CD platform supports the pattern; what differs is how ergonomic each direction is out of the box.

  • Netlify invented the modern "build hook" shape for JAMstack. A build hook is a per-site URL that starts a new build when POSTed to, with optional payload fields for branch and title. It is the reference implementation - if you run a static site behind a headless CMS, Netlify's build hooks are the cleanest fit and the honest recommendation is to use them directly.
  • Vercel offers "deploy hooks" with essentially the same shape as Netlify's, scoped to a project and branch. Same use case, same ergonomics.
  • GitHub Actions covers the incoming side with repository_dispatch (a POST to the GitHub API with an event type that a workflow can listen for) and workflow_dispatch (a manual trigger). Combined with GitHub's built-in webhook auth, this is a solid pattern if your code already lives on GitHub. Outgoing hooks are just an HTTP step in a job.
  • GitLab CI exposes "pipeline triggers" - tokenised URLs that start a pipeline when POSTed to, with variables passed in the payload. The token model is stricter than Netlify's opaque URL and less friction than GitHub's dispatch flow.
  • Jenkins has the Generic Webhook Trigger plugin and per-job tokens. Powerful, hostile UI, and you own the auth and the payload parsing yourself.
  • Argo CD listens for webhooks from GitHub/GitLab/Bitbucket so it can reconcile the moment main changes instead of polling on its 3-minute default. If you're already running GitOps on Kubernetes, that specific integration is the better fit for the "trigger a reconcile" job than a generic deploy hook.
  • Buddy is one option we recommend when a pipeline needs both directions in the same file. An events: - type: WEBHOOK entry gives the pipeline an incoming URL with optional TOTP auth, and an HTTP action with notification_url is the outgoing call to Slack, a CDN or a downstream service. Because both live in the same pipeline YAML as the build and deploy steps, the trigger URL, the deploy, and the post-deploy fan-out are one artifact instead of three - useful when the audit question later is "what actually happened when that hook fired".

To keep the concession explicit: if you run a static site on Netlify or Vercel, their built-in build hooks are already the shortest path and there is no need to reach for a general-purpose CI. Deploy hooks in a broader CI/CD tool earn their place when the same pipeline also builds a backend, runs tests, promotes across environments and pings a dozen downstream systems - not when the whole job is "rebuild a static site when a CMS entry changes".

Example

The pipeline below shows both directions in one file. It exposes an incoming webhook (so a headless CMS can trigger a rebuild when content changes) alongside a normal git-push trigger, runs the build, and finishes with three outgoing deploy hooks: a smoke check, a CDN cache purge, and a Slack notification.

# .buddy/buddy.yml - deploy hooks in both directions
- pipeline: "site-deploy"
  events:
    - type: "PUSH"
      refs:
        - "refs/heads/main"
    - type: "WEBHOOK"
      totp: true
  actions:
    - action: "Build the site"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      commands: |-
        npm ci
        npm run build

    - action: "Publish the artifact"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      commands: |-
        bdy artifact publish site:$BUDDY_EXECUTION_REVISION ./dist --create

    - action: "Route production traffic at the new version"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      commands: |-
        bdy distro route update prod-distro --domain=example.com --target=artifact=site:$BUDDY_EXECUTION_REVISION

    - action: "Outgoing hook: post-deploy smoke check"
      type: "HTTP"
      method: "GET"
      notification_url: "https://example.com/healthz"
      retry_count: 6
      retry_interval: 10

    - action: "Outgoing hook: purge the CDN edge cache"
      type: "HTTP"
      method: "POST"
      notification_url: "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/purge_cache"
      content: '{"purge_everything":true}'
      ignore_errors: true

    - action: "Outgoing hook: notify Slack"
      type: "HTTP"
      method: "POST"
      notification_url: "https://hooks.slack.com/services/$SLACK_HOOK"
      content: '{"text":"Deployed site:$BUDDY_EXECUTION_REVISION to production"}'
      ignore_errors: true
      trigger_time: "ON_EVERY_EXECUTION"

The two events entries give the same pipeline two ways in: a push to main from an engineer, and a POST to the webhook URL from a headless CMS when an editor publishes a page. The three trailing HTTP actions are outgoing deploy hooks: the smoke check gates promotion, the cache purge makes the change visible at the edge, and the Slack message tells the team the deploy landed. Marking the non-critical hooks ignore_errors: true means a slow Slack API can't fail an otherwise successful deploy, and setting the Slack notification to ON_EVERY_EXECUTION guarantees the team hears about failed deploys as well as successful ones - the deploy log stays symmetric.

Frequently asked questions

What is the difference between a deploy hook and a webhook?

A webhook is the generic pattern - one system POSTs to a URL on another system when an event happens. A deploy hook is that pattern applied specifically to deployments, in one of two directions. Incoming deploy hooks are webhooks that start a deploy (a CMS pings your build URL when content changes). Outgoing deploy hooks are webhooks your CI fires when a deploy finishes (a Slack message, a CDN cache purge, a downstream pipeline). All deploy hooks are webhooks; most webhooks are not deploy hooks.

How do you secure an incoming deploy hook URL?

Treat the URL itself as a secret and rotate it if it leaks, but never rely on secrecy alone. Add a shared HMAC signature the caller computes over the payload and your CI verifies, an IP allowlist if the caller is predictable, and TOTP or a short-lived token for humans invoking it manually. Log every call with source IP and payload hash so a leak is visible even if the URL still works.

When should I use an incoming deploy hook instead of a git push trigger?

Use an incoming hook when the trigger lives outside your git repo - content published in a headless CMS, a translation file synced from Crowdin, a nightly cron on an external scheduler, an operator button in a chatops bot. Pushes are the right trigger when the change is in git; hooks are the right trigger when the change is somewhere else and the deploy just needs to re-render or re-fetch it.

What is a good post-deploy hook to add first?

Two are worth adding on day one. A Slack or Teams message with the commit, the environment and a link to the run gives the team a passive deploy log without anyone having to check the CI. A cache-purge or CDN-invalidation call to your edge (Cloudflare, Fastly, CloudFront) makes the deploy actually visible to end users the moment it finishes, instead of after the TTL expires. Everything else - status pages, monitoring markers, DORA telemetry - is layered on later.

Missing a term? Spotted a mistake?

Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.