HTTP check-ins (curl)
Report runs from shell cron, CI or any language without an SDK with one curl command: start, finish, heartbeat.
On this page
Anything that can make an HTTPS request can report runs: a shell cron line, a CI step, a PHP script, a Go binary without an SDK. HTTP check-ins use the same URL format as Sentry's crons HTTP API, so existing scripts keep working with a Belvador DSN.
The check-in URL#
https://ingest.belvador.dev/api/<project-id>/cron/<monitor-slug>/<public-key>/<project-id> and <public-key> come from the project's DSN (https://<public-key>@…/<project-id>). The Monitors page shows the full URL for each monitor, already filled in. Treat it like the DSN: it can send data to your project but cannot read anything.
Report a run with a start and a finish#
Pick one id per run (32 hex characters) and send it with both requests, so Belvador can measure the duration and notice a run that started but never finished.
ID=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')curl -fsS -m 10 --retry 5 -o /dev/null "https://ingest.belvador.dev/api/<project id>/cron/nightly-invoices/<public key>/?status=in_progress&check_in_id=$ID" || trueWhen the job succeeds:
curl -fsS -m 10 --retry 5 -o /dev/null "https://ingest.belvador.dev/api/<project id>/cron/nightly-invoices/<public key>/?status=ok&check_in_id=$ID" || trueWhen it fails:
curl -fsS -m 10 --retry 5 -o /dev/null "https://ingest.belvador.dev/api/<project id>/cron/nightly-invoices/<public key>/?status=error&check_in_id=$ID" || trueA shell example that reports both cases:
if ./nightly-invoices.sh; then STATUS=ok; else STATUS=error; ficurl -fsS "$URL?status=$STATUS&check_in_id=$ID"Or: a single heartbeat#
When the work cannot report a start — a health loop, a worker that pings every few minutes — send one ok when it is done. Use an Every N minutes/hours monitor for this.
curl -fsS -m 10 --retry 5 -o /dev/null "https://ingest.belvador.dev/api/<project id>/cron/nightly-invoices/<public key>/?status=ok" || trueQuery parameters#
| Parameter | Values |
|---|---|
status | in_progress, ok, error (required) |
check_in_id | 32 hex characters; the same id on the start and finish of one run |
environment | e.g. production (default) or staging |
duration | seconds, if you measure it yourself |
release | your app's version |
outcome + reason | with status=ok: skipped (nothing to do, healthy) or unknown (ended, effect unknown — Needs review); reason is a short code such as nothing_due |
run_id, attempt, will_retry | group retries into one run: the same run_id on every attempt, attempt from 1, will_retry=true on a failed attempt your queue will repeat |
A finish without check_in_id closes the newest open start of that monitor.
A POST with a JSON body can also carry monitor_config — the same settings as withMonitor options, in Sentry's names (schedule, timezone, checkin_margin, max_runtime, failure_issue_threshold, recovery_threshold) — to create or update the monitor from the script.
Answers#
| Status | Meaning |
|---|---|
202 with {"id": "…"} | Accepted; id is the check-in id |
400 | Invalid slug, status or body |
401 | Unknown or inactive key, or the key belongs to another project |
403 | The workspace does not accept data right now |
413 | Body over 100 KiB |
429 | Too many check-ins: at most 60 per monitor and environment per minute, plus a monthly plan limit. Respect Retry-After. |
Rules#
Never call this URL from a web page: browsers' check-ins are refused, and the key would be visible to every visitor.
curl -fsSfails quietly on errors, so a Belvador outage never fails your job. Do not make your job depend on the check-in succeeding.If a check-in is refused for rate limits, the monitor shows Not observable rather than Missed.
Spotted something wrong or missing? Tell us: hello@belvador.dev