AlertKite
← AlertKite

Monitor a Vercel cron job and get told on WhatsApp when it stops

Vercel Cron triggers a route on a schedule. If that route throws, Vercel records a failed invocation and moves on — there is no retry and no notification unless you have wired one up.

So the job can fail every night for a fortnight while the deployment stays green. The fix is for the job to report its own success, and for something outside Vercel to notice when that report stops arriving.

app/api/cron/digest/route.ts

export const dynamic = "force-dynamic";

export async function GET(request: Request) {
  // Vercel signs cron invocations. Without this the route is a public endpoint
  // anyone can trigger as often as they like.
  if (request.headers.get("authorization") !== \`Bearer \${process.env.CRON_SECRET}\`) {
    return new Response("unauthorized", { status: 401 });
  }

  try {
    await sendDailyDigest();
    await fetch("https://hb.alertkite.com/p/YOUR_TOKEN", { method: "POST" });
    return Response.json({ ok: true });
  } catch (error) {
    // Report the failure explicitly rather than going quiet — /fail pages now
    // instead of waiting for the grace period to lapse.
    await fetch("https://hb.alertkite.com/p/YOUR_TOKEN/fail", {
      method: "POST",
      body: String(error),
    });
    throw error;
  }
}

Setting it up

  1. Add the schedule to vercel.json under `crons`.
  2. Set CRON_SECRET in your project's environment variables and check it in the route.
  3. Create a heartbeat monitor with a period matching the schedule, plus grace.
  4. Ping on success, and call /fail in the catch so a broken run pages immediately.

Await the ping before returning

A serverless function can be frozen the moment it returns a response. A fetch that has not resolved by then may never be sent, which produces a false outage at 3am. Await it.

Hobby plans run once a day, and not at a promised minute

Cron timing on Vercel is best-effort, and Hobby is limited to daily. Set the heartbeat period generously — an hour of grace on a daily job — or you will be paging yourself over ordinary scheduling drift.

This works the same on Netlify, Render and Railway

Nothing here is Vercel-specific beyond the signature header. Any scheduler that can run a function can call a URL on success, and the monitoring side is identical.