# Netlify deploy notifications on your phone

> Get a push notification when a Netlify deploy succeeds or fails, louder when a production deploy breaks.

- Company: Netlify (https://www.justpush.io/recipes/netlify)
- Event: `deploy` (Deploy)
- Tags: Developer tools, Releases
- Install: https://studio.justpush.io/recipes/netlify/deploy
- Web page: https://www.justpush.io/recipes/netlify/deploy

## Setup

1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
2. Follow the setup notes below: Netlify has separate notification screens for deploys and forms.
3. Add an HTTP POST request notification with your webhook URL (shown in Studio after install) as the URL.
4. Save.
5. In Netlify, open the site's **Project configuration → Notifications → Deploy notifications → Add notification → HTTP POST request**. Add one notification for **Deploy succeeded** and one for **Deploy failed**, both with the URL above. Other deploy events (started, locked, …) are ignored.
6. Netlify doesn't say which event fired, so the recipe goes by the deploy's `state` (`ready` or `error`).
7. Netlify can sign notifications with a JWS secret (`X-Webhook-Signature`), but JustPush doesn't check it yet, so keep your endpoint URL private.

## Code

Studio calls `handleRequest(request)` with the incoming webhook (`{ method, headers, body, query, raw }`) and sends the message object it returns. Returning `null` sends nothing.

```js
// Netlify → deploy succeeded or failed
// Fires when a Netlify "HTTP POST request" deploy notification sends the deploy object.
// Netlify doesn't say which event fired, so the recipe goes by the deploy's state.
// Docs: https://docs.netlify.com/deploy/deploy-notifications/

// Deploy contexts, as people say them.
const CONTEXTS = { production: "production", "deploy-preview": "deploy preview", "branch-deploy": "branch deploy" }

// Only link to real http(s) URLs.
function link(url, cta) {
    return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
}

function handleRequest(request) {
    const deploy = request.body || {}

    // Guard: only finished deploys. Building/enqueued states are skipped (one push per deploy).
    if (!deploy.site_id || !["ready", "error"].includes(deploy.state)) {
        console.log(`Skipping Netlify deploy state "${deploy.state ?? "unknown"}"`)
        return null
    }

    const failed = deploy.state === "error"
    const name = deploy.name ?? "Your site"
    const context = Object.prototype.hasOwnProperty.call(CONTEXTS, deploy.context) ? CONTEXTS[deploy.context] : deploy.context
    const preview = deploy.review_id ? ` #${deploy.review_id}` : ""
    const commit = (deploy.title ?? deploy.commit_message ?? "").split("\n")[0]
    const details = failed
        ? [deploy.error_message, deploy.branch].filter(Boolean)
        : [deploy.branch, commit, deploy.committer ? `@${deploy.committer}` : null].filter(Boolean)
    const log = deploy.admin_url && deploy.id ? `${deploy.admin_url.replace(/\/$/, "")}/deploys/${deploy.id}` : null

    return {
        title: `${failed ? "❌" : "✅"} ${name} ${failed ? "deploy failed" : "deployed"}${context ? ` (${context}${preview})` : ""}`,
        message: details.join(" · ") || (failed ? "The deploy failed" : "Deploy is live"),
        topic: "Netlify",
        // A failed production deploy needs you; previews and successes can wait.
        priority: failed ? (deploy.context === "production" ? 1 : 0) : -1,
        buttons: [...link(log, failed ? "View deploy log" : "View deploy"), ...(failed ? [] : link(deploy.deploy_ssl_url ?? deploy.ssl_url, "Open site"))],
    }
}
```

## Netlify deploy webhook payload (sample)

```json
{
  "id": "65268a46257790246",
  "url": "http://acme-site.netlify.app",
  "name": "acme-site",
  "state": "error",
  "title": "Update Layout.js",
  "branch": "main",
  "context": "production",
  "site_id": "b46da3f0-0ca7-4f81-9365-1a2b3c4d5e6f",
  "ssl_url": "https://acme-site.netlify.app",
  "build_id": "65268a461697790246",
  "admin_url": "https://app.netlify.com/sites/acme-site",
  "committer": "janedoe",
  "review_id": null,
  "commit_ref": "21cad587a3c5dbef9e0f548d",
  "commit_url": "https://github.com/acme/site/commit/21cad587a3c5dbef9e0f548d",
  "deploy_url": "http://main--acme-site.netlify.app",
  "deploy_time": 42,
  "published_at": null,
  "error_message": "Build script returned non-zero exit code: 2",
  "manual_deploy": false,
  "commit_message": null,
  "deploy_ssl_url": "https://main--acme-site.netlify.app"
}
```

## FAQ

### Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

### Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into Netlify and you are done. The code is there if you want to change the text, the sound or the buttons.

### Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

### What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.
