How to Monitor a Scheduled Task in Laravel
Laravel's scheduler will happily run a command every night for months without anyone checking whether it actually did anything useful. There's no dashboard flag for "this task has been silently failing since the last deploy," and artisan's exit codes don't mean much if nobody's watching for them. The job just runs, or doesn't, and the gap between those two states is invisible until a customer notices a missing invoice.
Laravel actually ships a built-in answer to this: scheduled tasks can ping a URL on success, no extra package required. Pair that with a heartbeat monitor and you get an alert the moment a task stops running, instead of finding out whenever someone happens to look. Here's how to set it up with the Downdar API.
What You Will Need
A Downdar API key from your account, and a Laravel application with task scheduling already configured. Authentication is via Bearer token in the Authorization header. Your API key must always be kept server-side, never commit it to version control.
Creating a Heartbeat Monitor
The /api/v1/heartbeats/create endpoint accepts a POST request with a name and returns the full heartbeat object, including the unique url your task will call on schedule. You only need to do this once per task, not on every run. For a one-off task this is arguably overkill anyway, creating the heartbeat from your Downdar dashboard and copying the URL works just as well; the API is more useful if you're provisioning heartbeats as part of a deploy script across several environments. Laravel's Http facade handles the API version cleanly:
The response looks like this:
Save the url field in your .env file, you'll reference it from your scheduler definition next.
Pinging the Heartbeat From Your Scheduled Task
Laravel's scheduler has this exact use case built in. The pingOnSuccess() method calls a URL only when the scheduled command exits successfully, which is precisely the behavior a heartbeat needs, and means you don't have to write any conditional logic yourself. In routes/console.php (Laravel 11 and later) or the schedule() method of app/Console/Kernel.php on earlier versions:
That's the entire integration. Laravel calls the URL with a quick HTTP request only when invoices:generate exits with a zero status code. If the command throws an exception or exits with an error, the ping never fires, and the heartbeat notices the missing check-in on its own schedule and opens an incident.
If you'd rather ping on both success and failure and let Downdar distinguish them, Laravel also offers pingBefore(), thenPing(), and pingOnFailure(), but for straightforward silent-failure detection, pingOnSuccess() alone is usually all you need: a missing ping is the signal, you don't need a separate failure ping to know something went wrong.
Checking Heartbeat Status Programmatically
If you want to surface a heartbeat's current state in your own admin panel, the /api/v1/heartbeats/status endpoint returns just the essentials, lighter than a full get call.
status reflects the heartbeat's current state directly, so you can show "last invoice run: healthy" on an internal dashboard without your team needing to check Downdar itself.
A Note on API Key Security
Store your API key in .env and expose it through config/services.php, Laravel's standard pattern for third-party credentials, rather than referencing env() directly outside config files.
The heartbeat URL can live safely next to your other environment config since it only accepts a check-in and exposes nothing about your account. Treat the API key differently: it's what actually needs to stay out of version control and off the client.
Get Started
Create the heartbeat, add one line to your scheduler definition, and deploy. There's nothing else to configure, Laravel and Downdar handle the rest between them.