AO Job Heartbeat

Know when a scheduled job did not run.

A cron job. A nightly backup. An agent that runs every hour. Each one hits its own ping URL when it finishes.

If the ping does not arrive in time, you get an email. On the paid plan you also get a signed webhook.

No dashboard to babysit. No SMS. No phone calls. Just the alert, when it matters.

How it works

  1. Create a check. Give it a name, how often the job runs (period_s), and how late is too late (grace_s).
  2. Ping it from the job. Put the check's ping_url at the end of your script. GET or POST, no key needed, body ignored.
  3. Get told when it is late. No ping within period + grace sends a check.late event. The next ping sends check.recovered. If the job knows it broke, it can call /ping/<id>/fail right away.

Plans

PlanPriceChecksAlertsEvent log
Free$010email30 days
Paid$7 a month, flat100email + webhooks30 days

No per-seat fee. To upgrade, call POST /v1/billing/checkout with your API key. You get a Stripe Checkout page; pay there and your plan flips to paid.

Cancel any time from the billing portal.

Quick start

Three calls. Replace the email with yours.

# 1. sign up (free). The API key is shown once. Save it.
curl -s -X POST https://beat.austinoaksapi.com/v1/signup \
     -H 'Content-Type: application/json' \
     -d '{"email":"you@example.com"}'

# 2. create a check: runs every 5 minutes, 60 seconds of grace
curl -s -X POST https://beat.austinoaksapi.com/v1/checks \
     -H "Authorization: Bearer $KEY" \
     -H 'Content-Type: application/json' \
     -d '{"name":"nightly-backup","period_s":300,"grace_s":60}'
# the reply includes "ping_url" and "fail_url"

# 3. from the job itself, at the end of each run
curl -s https://beat.austinoaksapi.com/ping/<check id>
# the job knows it broke? say so now instead of waiting for the clock
curl -s -X POST https://beat.austinoaksapi.com/ping/<check id>/fail

Paid plan: add a webhook target and every event is POSTed to it.

curl -s -X POST https://beat.austinoaksapi.com/v1/targets \
     -H "Authorization: Bearer $KEY" \
     -H 'Content-Type: application/json' \
     -d '{"kind":"webhook","url":"https://hooks.example.com/alerts","secret":"shared"}'

See what happened: GET /v1/checks/<id>/events returns the last 100 events with their delivery results. The full route list is in openapi.json.

Signed events

Every alert is a JSON event signed with an Ed25519 key. You can check that it really came from this service and was not changed.

The envelope is {"event", "sig", "kid", "ts"}. The signed bytes are the canonical JSON of {"event", "kid", "ts"}: keys sorted, no whitespace. The public key is at GET /v1/keys. Any Ed25519 library can verify it.

A signature proves this service issued the event, unchanged, at the stated time. It does not prove your job is healthy.

Webhook deliveries also carry X-AOKit-Event (the event type) and, when your target has a secret, X-AOKit-Signature: sha256=<HMAC of the body>. Deliveries are tried 3 times with 1, 2 and 4 second waits.

Limits worth knowing

More