Email API

Error and Uptime Alert Emails from Cron Jobs and Monitoring Scripts

Send error and uptime alert emails from cron jobs and monitoring scripts without alert fatigue: what to alert on, deduplication, recovery mails, and curl.

Email is still the most reliable way to find out that a nightly job failed or a site went down, because it reaches you on every device and needs no app. The failure mode is the opposite of silence: a flapping check that emails every minute until you stop reading. This guide sets up alert emails that fire once, say what to do, and tell you when the problem is gone.

Quick answer

Send an alert email on the transition from healthy to failing, not on every failing check; send a recovery email when it transitions back; deduplicate on a fingerprint of the alert; and put the essential facts (what, where, since when, what to look at) in the first two lines. Send through an HTTPS email API from the monitoring host so a broken local mail setup on the server cannot swallow the alert.

What deserves an email

  • A cron job that did not complete (exit status non-zero) or did not start at all. The second needs a dead-man switch: a check that expects a "done" ping and alerts when it is missing.
  • A health-check URL returning non-200 or timing out on 3 consecutive checks (one failure is noise).
  • Disk above 90%, certificate expiring within 14 days, backup older than 26 hours, queue depth growing for 15 minutes.
  • Not: a single slow request, a 4xx from a bot, or anything that resolves itself within one check interval.

State, not events

Keep a small state file or table per check: status (ok or failing), since, last_alerted_at, and the fingerprint of the last alert. Emails are sent only when status changes. For flapping services add hysteresis: require 3 consecutive failures to enter failing and 2 consecutive successes to leave it. If the status stays failing for a long time, send at most one reminder every 6 hours with "still down since 02:14". The recovery email should include the total downtime; it is the number people ask about.

The send request

A shell-based monitor can send with curl directly. Put the facts in the subject so the notification is enough to act on:

curl -X POST https://api.oqumail.com/api/v1/emails \
  -H "Authorization: Bearer $OQUMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"alerts@yourdomain.com","to":"ops@yourdomain.com","subject":"[DOWN] api.example.com health check failing since 02:14 UTC","html":"<p><strong>api.example.com</strong> returned HTTP 503 on 3 consecutive checks.</p><p>Since: 2026-08-02 02:14 UTC<br>Last body: upstream connect error</p><p>Runbook: https://wiki.yourdomain.com/runbooks/api-down</p>","text":"api.example.com returned HTTP 503 on 3 consecutive checks since 2026-08-02 02:14 UTC. Runbook: https://wiki.yourdomain.com/runbooks/api-down"}'

Wrapping a cron job

  1. Run the job under a wrapper script that captures exit status, duration, and the last 30 lines of output.
  2. On non-zero exit, send an alert with the job name, host, exit code, and the captured output in a pre block (escape angle brackets).
  3. On success after a failure, send "recovered" with the duration.
  4. Keep the previous exit status in a file such as /var/tmp/job-name.status so the wrapper knows whether this is a transition.
  5. Ping a dead-man endpoint or write a "last ran" timestamp that a separate check inspects, so a job that never starts is caught.

Why an API and not local sendmail

Alerts from a server usually go through the local MTA, which means an unauthenticated message from an IP with no reverse DNS, arriving from a domain with no SPF record for that server. Those messages get filtered exactly when you most need them. Posting to an email API over HTTPS uses a Bearer key, sends from your verified domain with DKIM, and gives you a delivery log per alert. On OquMail that is one curl call with the key in an environment variable, and if an alert seems missing you can check the delivery log for the receiving server's SMTP response. Keep the key on the monitoring host in a file readable only by the monitoring user.

Avoiding alert fatigue

  • One alert per incident, one recovery. Not one per check.
  • Subject prefixes [DOWN], [RECOVERED], [WARN] so mail rules can route and colour them.
  • Group by host: if the host is unreachable, do not also alert for every service on it.
  • Never alert on things nobody will act on at 3 a.m. Put those into a morning summary.
  • Route to a shared ops@ mailbox rather than one person; OquMail lets you create it on the same domain and read it from any mail app.

Common questions

What about using a monitoring service instead?

Hosted uptime services are excellent for external checks and also send email. Cron-job success, backup age and disk space are internal facts they cannot see, which is where your own alert emails fit.

Should the alert include logs?

Include the last 20 to 30 lines, escaped, in the body. Enough to decide whether to get out of bed, not the full log; link to the full one.

How do I make sure the alert path itself works?

Send a heartbeat email once a day from the monitoring host ("monitoring is alive") to a mailbox with a rule that alerts you if it does not arrive. Test the alert path when you set it up, not during an outage.

Free business email on your own domain

OquMail gives you up to 15 mailboxes on your domain — free — with guided SPF/DKIM/DMARC, webmail, IMAP/SMTP for any mail app, and a send API. Most teams are live in under fifteen minutes. Start at oqumail.com.

Get started free

Ready for business email on your domain?

Up to 15 free mailboxes, guided DNS, webmail, and a transactional API — start in minutes.

Create your free workspace