Monitoring

Heartbeat Checks

Passive monitoring for cron jobs, background workers, and scheduled tasks. Instead of Beacon checking your endpoint, your service pings Beacon. If no ping arrives within the expected interval, it is treated as a failure.

What heartbeats do

HTTP monitors are active -- Beacon reaches out to your service. Heartbeat monitors are passive -- your service reaches out to Beacon. This reversal makes heartbeats ideal for tasks that do not expose an HTTP endpoint:

  • Cron jobs and scheduled tasks
  • Background workers and queue consumers
  • Data pipeline stages
  • Deployment scripts
  • Database backup routines
  • Certificate renewal processes

Each heartbeat monitor expects a ping at a regular interval. If the ping does not arrive before the deadline, Beacon treats it the same way as a failed HTTP check -- the consecutive failure counter increments, and once the threshold is exceeded, an auto-incident is created.

Creating a heartbeat monitor

Navigate to your status page, open the Monitors tab, and click Add Monitor. Select the Heartbeat type and fill in:

Field Description Default
Name A descriptive label, e.g. Nightly Backup --
Interval How often you expect a ping (in minutes) 5
Failure Threshold Missed pings before an incident is created 3

After creation, Beacon generates a unique ping URL for the monitor.

The ping URL

Each heartbeat monitor receives a unique URL in the following format:

POST https://usebeacon.pro/api/v1/heartbeat/{token}

The token is a unique, unguessable identifier generated when the monitor is created. No authentication headers are required -- the token itself serves as the credential. This makes it easy to use from minimal environments like cron or shell scripts.

Treat the heartbeat token like a secret. Anyone with the URL can send pings to your monitor. If a token is compromised, delete the monitor and create a new one.

Sending heartbeats

Send a POST request to the ping URL after your task completes. The response body is empty with a 200 status on success.

curl

curl -X POST https://usebeacon.pro/api/v1/heartbeat/abc123

Cron job

Append the curl command to the end of your cron entry so the ping only fires after the job completes:

# Run nightly backup at 2 AM, then ping Beacon
0 2 * * * /usr/local/bin/backup.sh && curl -fsS -X POST https://usebeacon.pro/api/v1/heartbeat/abc123

PHP / Laravel

// In your Laravel scheduler (app/Console/Kernel.php)
$schedule->command('reports:generate')
    ->daily()
    ->after(function () {
        Http::post('https://usebeacon.pro/api/v1/heartbeat/abc123');
    });

Node.js

// After your job completes
await fetch('https://usebeacon.pro/api/v1/heartbeat/abc123', {
  method: 'POST',
});

Python

import requests

# After your task completes
requests.post('https://usebeacon.pro/api/v1/heartbeat/abc123')

Ruby

require 'net/http'

# After your task completes
Net::HTTP.post(
  URI('https://usebeacon.pro/api/v1/heartbeat/abc123'),
  ''
)

How failure works

Beacon tracks a next_check_at timestamp for each heartbeat monitor. This timestamp advances by the configured interval each time a ping is received.

The scheduled monitors:check command runs every minute. When it finds a heartbeat monitor whose next_check_at has passed without a new ping, it records a failure. The same threshold and auto-incident logic used by HTTP monitors applies:

  • Consecutive missed pings are counted against the failure threshold
  • Once the threshold is exceeded, an incident is created
  • The linked component (if any) is set to major_outage
  • Subscribers are notified

Recovery

Sending a ping to a heartbeat monitor that is in the down state immediately resolves the active auto-incident. The monitor returns to up, the linked component is restored to operational, and recovery notifications are sent to subscribers.

Tip: If your cron job failed and you have fixed the underlying issue, you can manually trigger a ping with curl to resolve the incident immediately rather than waiting for the next scheduled run.

Practical examples

Laravel Scheduler with heartbeat

Monitor that your daily invoice job actually runs:

// app/Console/Kernel.php
$schedule->command('invoices:send')
    ->dailyAt('08:00')
    ->after(fn () => Http::post(
        'https://usebeacon.pro/api/v1/heartbeat/inv-job-token'
    ));

Sidekiq worker

Confirm a recurring Sidekiq job completes:

class DataSyncWorker
  include Sidekiq::Worker

  def perform
    sync_data_from_warehouse

    # Ping Beacon on success
    Net::HTTP.post(
      URI('https://usebeacon.pro/api/v1/heartbeat/sync-token'),
      ''
    )
  end
end

Node.js scheduled task

Verify a Node.js cleanup task runs on schedule:

import cron from 'node-cron';

cron.schedule('0 */6 * * *', async () => {
  await cleanupExpiredSessions();

  // Ping Beacon on success
  await fetch('https://usebeacon.pro/api/v1/heartbeat/cleanup-token', {
    method: 'POST',
  });
});