Exit codes

Wire a shell job into Drumbeats with one extra line, using the exit code you already have.

One extra line at the end of a shell job:

bash
your-job
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?
your-job
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?

$? holds the previous command's exit status. Drumbeats reads it as the event.

How codes map#

Exit codeRecorded asEffect on the monitor
0successResolves any open incident, monitor flips UP
1 through 255failure, with the exact code storedFailure counter increments. Monitor flips DOWN at failure_tolerance
Anything elseRejected with 400 and code: INVALID_EXIT_CODENothing recorded

Keeping the exact code matters during triage. 137 tells you the kernel killed the process, almost always out of memory. 124 is a timeout command firing. A custom exit 42 is your own code path. All three read as "failure" in the alert, and the number is what tells them apart.

Capture $? before anything else runs#

$? is overwritten by every command, including echo and the curl itself. Save it first:

bash
your-job
EXIT=$?
# anything can happen here now
logger "job finished with $EXIT"
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$EXIT
your-job
EXIT=$?
# anything can happen here now
logger "job finished with $EXIT"
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$EXIT

Skipping this is the most common way an exit-code integration silently reports the wrong thing. The naive your-job; logger ...; curl .../$? sends the exit status of logger, which is almost always 0, so every run looks successful.

Use ; not && in crontab#

bash
0 2 * * * /usr/bin/backup.sh; \
  curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?
0 2 * * * /usr/bin/backup.sh; \
  curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?

With && the ping is skipped whenever the job fails, which is exactly when you want it. With ; the ping runs either way and carries the real code.

Compose with run_id#

Exit codes work alongside the named events. Send start at the top, finish with the code:

bash
RUN_ID="job-$(uuidgen)"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start?run_id=$RUN_ID"
python worker.py
EXIT=$?
curl -sf "$API/${EXIT}?run_id=$RUN_ID"
RUN_ID="job-$(uuidgen)"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start?run_id=$RUN_ID"
python worker.py
EXIT=$?
curl -sf "$API/${EXIT}?run_id=$RUN_ID"

Always use -sf in cron#

-s silences curl's progress meter, which cron mails to root on every single run otherwise. -f makes curl exit non-zero on an HTTP error, so a 500 from the ping API does not read as success further down your chain.

When to use something else#

Use exit codesUse named events
You are wrapping one shell commandYou need start and duration tracking
$? is already sitting thereYou want log pings between phases
Success and failure is the whole storyYou want to attach stdout or an error message

Both cost the same in beats. The exit-code form is a convenience, not a cheaper path. When you need more than a binary outcome, payloads and the named events give it to you.

What happens when it breaks#

An exit-code ping behaves exactly like the event it maps to. A non-zero code opens a FAILED incident once failure_tolerance is reached, pages every notification group on the monitor, and shows the numeric code on the incident timeline. A later 0 resolves it and sends the recovery message.

Next#

Ping API for the full event and error reference. Scheduled pings for the Cron and Heartbeat patterns most exit-code jobs use. Shell integration for production-grade scripts with retries and timeouts.