Monitor a nightly cron job
Before you start
- A server secret ab_srv_…
- Admin or owner role in the project
A nightly job that stops running fails silently: nothing crashes, the invoices just don't go out. In this guide the job tells Abrova Trace each time it finishes (a check-in). When a check-in doesn't arrive in time, Trace marks the monitor Missed and e-mails your team. No SDK is needed: a check-in is one HTTPS request.
Add an e-mail channel#
The monitor sends its alert through an integration, so create one first. Email works inside Iran with any mailbox, including Iranian mail providers.
- In Abrova Console, open Trace → Integrations for your project.
- Click Add integration and choose Email.
- Enter a Name (for example
On-call) and the Recipients (up to 50; press Enter or a comma after each). - Click Save and send test, and check that the test e-mail arrives (look in spam too).
Managing integrations and monitors needs the admin or owner role. See Email integration.
Create the monitor#
- Open Trace → Crons and click New monitor.
- Fill in:
| Field | Value for a job that runs at 02:00 every night |
|---|---|
| Name | Nightly invoices |
| Slug | nightly-invoices (made from the name; your job sends it in every check-in) |
| Schedule | 0 2 * * * |
| Grace period | Longer than the job’s normal run time varies, for example 30 minutes |
| Environment | production (optional) |
| Notification channels | The Email integration from step 1 |
- Click Create monitor.
Trace uses the schedule to work out how long to wait between check-ins: here 24 hours. After each check-in it expects the next one within that interval plus the grace period, and it checks every monitor every 5 minutes.
Give the job the server secret#
Check-ins authenticate with your project's server secret (ab_srv_…) in the X-API-Key header. It is in Abrova Console under Project settings → Keys and is shown once, when you create or rotate it. Put it in the job's environment, never in code you commit and never in an app:
# /etc/environment, your systemd unit, or your CI secret store ABROVA_SERVER_SECRET=ab_srv_your_server_secret
Once your jobs use the server secret, turn on Server routes require the server secret under Project settings → Products → Abrova Trace. Then the public app key can no longer send a fake "ok" check-in. See Server secret & server routes.
Send a check-in when the job finishes#
Send ok when the job succeeds and error with a message when it fails. Pick the form that matches your job.
Bash
#!/bin/bash
# /usr/local/bin/nightly-invoices.sh, run by cron: 0 2 * * * /usr/local/bin/nightly-invoices.sh
URL="https://trace.abrova.ir/api/cron-monitors/check-in"
START=$(date +%s)
if /usr/local/bin/send-invoices; then # your job
STATUS="ok"
else
STATUS="error"
fi
DURATION_MS=$(( ($(date +%s) - START) * 1000 ))
curl -s -X POST "$URL" \
-H "X-API-Key: $ABROVA_SERVER_SECRET" \
-H "Content-Type: application/json" \
-d "{\"monitor_slug\": \"nightly-invoices\", \"status\": \"$STATUS\", \"duration_ms\": $DURATION_MS}"Laravel
With the Laravel SDK installed, the server secret is already in your config as ABROVATRACE_SERVER_SECRET in .env. Check in from the command itself:
<?php
// app/Console/Commands/SendInvoices.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class SendInvoices extends Command
{
protected $signature = 'invoices:send';
public function handle(): int
{
$start = microtime(true);
$checkIn = fn (array $body) => Http::withHeaders([
'X-API-Key' => config('abrovatrace.server_secret'),
])->post('https://trace.abrova.ir/api/cron-monitors/check-in', $body + [
'monitor_slug' => 'nightly-invoices',
'duration_ms' => (int) ((microtime(true) - $start) * 1000),
]);
try {
$this->sendAllInvoices(); // your job
$checkIn(['status' => 'ok']);
return self::SUCCESS;
} catch (\Throwable $e) {
$checkIn(['status' => 'error', 'message' => $e->getMessage()]);
throw $e;
}
}
}And schedule it as usual, for example in routes/console.php:
use Illuminate\Support\Facades\Schedule;
Schedule::command('invoices:send')->dailyAt('02:00');Long jobs: start and finish
For a job that runs for an hour or more, send in_progress when it starts. The answer carries an id; send it back as check_in_id when it ends, so the Console shows one run with its duration:
CHECK_IN_ID=$(curl -s -X POST https://trace.abrova.ir/api/cron-monitors/check-in \
-H "X-API-Key: $ABROVA_SERVER_SECRET" \
-H "Content-Type: application/json" \
-d '{"monitor_slug": "nightly-invoices", "status": "in_progress"}' \
| jq -r '.id')
# ... run the job ...
curl -s -X POST https://trace.abrova.ir/api/cron-monitors/check-in \
-H "X-API-Key: $ABROVA_SERVER_SECRET" \
-H "Content-Type: application/json" \
-d "{\"monitor_slug\": \"nightly-invoices\", \"status\": \"ok\", \"check_in_id\": \"$CHECK_IN_ID\"}"Test it before tonight#
- Run the script or
php artisan invoices:sendonce by hand. The check-in answers200with{"id": "…", "status": "ok", "monitor_slug": "nightly-invoices"}. - In Trace → Crons, the monitor shows OK and the check-in in its history, with the duration.
- To see an alert without waiting a day, create a second monitor (for example
alert-test) with no schedule, an expected interval of 1 minute, a grace period of 1 minute and the Email channel, and send it nothing. Within about 10 minutes it turns Missed and the e-mail arrives. Delete the test monitor afterwards, so it doesn't use one of your free monitors.
After a Missed alert, the monitor stays Missed and sends no more e-mails until the next check-in arrives. An error check-in shows the monitor as Error with your message in the Console; the e-mail alert is for runs that don't check in at all.
Troubleshooting#
The job ran but the monitor shows Missed
Check the HTTP status of the check-in: 401 means the key was refused. Check that monitor_slug matches the slug exactly, and that the server can reach trace.abrova.ir. If the job sometimes starts late or runs long, raise the grace period.
Missed, but no e-mail
Open the monitor and check that the Email integration is selected under Notification channels, and that the integration is switched on in Trace → Integrations (use Send test). Monitors that were created by a check-in have no channel until you add one.
The check-in returns 401 with server_credential_required
Server routes require the server secret is on, and the job sent the app key. Use the server secret (ab_srv_…) in X-API-Key.
The check-in returns 400
The body needs monitor_slug and a status of ok, error or in_progress, sent as JSON with Content-Type: application/json.
Laravel sends an empty key
config('abrovatrace.server_secret') reads ABROVATRACE_SERVER_SECRET through the Laravel SDK's config. Set it in .env, and run php artisan config:clear if you cache the config.
I can’t create another monitor
3 monitors are free; each extra one costs 2,000 Toman a month from your organisation’s wallet. Top up the wallet, or delete a monitor you no longer need.
- Set a grace period longer than the job’s normal run time.
- A monitor created by a check-in has no notification channel, so it turns Missed without telling anyone.
Where next: Cron monitoring