# Render cron job failed notifications on your phone

> Get a High-priority push notification when a Render cron job run or one-off job fails, and optionally a quiet one when it succeeds.

- Company: Render (https://www.justpush.io/recipes/render)
- Event: `cron_job_run_ended` (Cron Job Run Ended)
- Tags: Hosting & deployment, Cron & background jobs
- Install: https://studio.justpush.io/recipes/render/cron-job
- Web page: https://www.justpush.io/recipes/render/cron-job

## Setup

1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
2. In the Render Dashboard, open your workspace's Integrations > Webhooks and click + Create Webhook (Pro plan or higher, workspace admins only).
3. Give it a name and paste your webhook URL (shown in Studio after install) as the URL.
4. Select the Cron Job Run Ended event and click Create Webhook.
5. In the Render Dashboard, open **Integrations → Webhooks → + Create Webhook** (Pro plan or higher) with the URL above, and select **Cron Job Run Ended**. Add **Job Run Ended** too if you use one-off jobs.
6. Only failed runs push by default. Add `?all=1` to the URL to also get a quiet push when a run succeeds or is canceled:
7. ```
8. your webhook URL (shown in Studio after install)?all=1
9. ```
10. On a **Pro** workspace you get a single webhook URL, so one integration receives every event you select; the recipe ignores the events it doesn't handle. Scale and Enterprise workspaces can add a webhook per recipe.
11. Render signs deliveries with Standard Webhooks headers (`webhook-signature`), but JustPush doesn't check that signature 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
// Render → cron job run ended (and one-off job run ended)
// Fires when a Render workspace webhook sends cron_job_run_ended or job_run_ended.
// Only failed runs push, unless the webhook URL has ?all=1.
// Docs: https://render.com/docs/webhooks

// The two event types and how to name what ran.
const KINDS = {
    cron_job_run_ended: "cron job",
    job_run_ended: "job",
}

// Run statuses, their labels and how loud to be.
const STATUSES = {
    // A failed run means the work didn't happen; it needs you soon.
    failed: { emoji: "❌", verb: "failed", priority: 1 },
    // Only with ?all=1: routine news.
    succeeded: { emoji: "✅", verb: "succeeded", priority: -1 },
    canceled: { emoji: "⏹️", verb: "was canceled", priority: -1 },
}

// Look a key up in one of the tables above, ignoring inherited names like "toString".
function pick(table, key, fallback) {
    return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
}

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

function handleRequest(request) {
    const body = request.body && typeof request.body === "object" ? request.body : {}
    const data = body.data && typeof body.data === "object" ? body.data : {}
    const kind = pick(KINDS, body.type, null)
    const status = pick(STATUSES, data.status, null)

    // Guard: only finished job runs with a known status.
    if (!kind || !status) {
        console.log(`Ignoring Render "${body.type ?? "unknown"}" event (status "${data.status ?? "none"}")`)
        return null
    }

    // ?all=1 on the webhook URL also sends quiet pushes for succeeded and canceled runs.
    const all = ["1", "true", "yes"].includes(String(request.query?.all ?? "").toLowerCase())
    if (data.status !== "failed" && !all) {
        console.log(`Skipping ${kind} run that ${data.status} (add ?all=1 to get these)`)
        return null
    }

    const name = typeof data.serviceName === "string" && data.serviceName ? data.serviceName : "Your"

    return {
        title: `${status.emoji} ${name} ${kind} ${status.verb}`,
        message: data.status === "failed" ? "Check the run's logs in Render" : `The ${kind} run ${data.status === "canceled" ? "was canceled" : "finished"}`,
        topic: "Render",
        priority: status.priority,
        buttons: link("https://dashboard.render.com", "Open Render"),
    }
}
```

## Render cron_job_run_ended webhook payload (sample)

```json
{
  "data": {
    "id": "evt-d3ka1qf015js70180m20",
    "status": "failed",
    "serviceId": "crn-d2kp0ahtq21c73e9t4b0",
    "serviceName": "nightly-report"
  },
  "type": "cron_job_run_ended",
  "timestamp": "2026-09-29T02:00:41.503Z"
}
```

## 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 Render 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.
