Payloads
Attach context to any Drumbeats ping — error messages, stdout, structured data, metrics. Payload sizes, parameter precedence, and storage rules.
Payloads are the optional body attached to a Drumbeats ping. They carry error messages, stdout, structured data, or metrics — anything that helps an on-call engineer triage a failed run without leaving the dashboard. Only POST requests can carry payloads; GET pings are limited to query-string parameters (run_id, exit-code event).
Request parameters
The Ping API accepts three optional parameters on every event. All three can be sent on the same POST.
| Field | Where | Size limit | Description |
|---|---|---|---|
run_id | Query string or POST body | 255 chars | Correlates pings from one execution. Required for Event-driven monitors. |
payload | POST body | Plan dependent (see below) | Stdout, error text, or any string. The dashboard renders this on the run timeline. |
duration_ms | POST body | — | Manually reported duration. Optional; Drumbeats also computes duration from start → finish timing when both pings share a run_id. |
Payload size limits per plan:
- Free: 100 KB per ping
- Pro: 1 MB per ping
- Business: 2 MB per ping
Payloads up to 10 KB live in the primary database and load instantly in the dashboard. Larger payloads spill to object storage and may take a moment to render. Beats are metered from the first byte regardless of storage tier.
Sending a payload
The patterns below all use the recommended POST + JSON shape. The start / success / failure URL suffix is unchanged — only the request body and method differ.
curl "https://api.drumbeats.io/v1/ping/<monitor-id>/success?run_id=job-123"curl "https://api.drumbeats.io/v1/ping/<monitor-id>/success?run_id=job-123"curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: application/json" \
-d '{
"run_id": "backup-2026-05-11",
"payload": "Backed up 3 databases (2.1 GB) in 4m32s"
}'curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: application/json" \
-d '{
"run_id": "backup-2026-05-11",
"payload": "Backed up 3 databases (2.1 GB) in 4m32s"
}'START=$(date +%s%3N)
/usr/bin/backup.sh
END=$(date +%s%3N)
curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: application/json" \
-d "{\"run_id\":\"backup-$(date +%Y%m%d)\",\"duration_ms\":$((END-START))}"START=$(date +%s%3N)
/usr/bin/backup.sh
END=$(date +%s%3N)
curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: application/json" \
-d "{\"run_id\":\"backup-$(date +%Y%m%d)\",\"duration_ms\":$((END-START))}"python job.py 2>&1 | curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: text/plain" \
--data-binary @-python job.py 2>&1 | curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/success \
-H "Content-Type: text/plain" \
--data-binary @-OUTPUT=$(python job.py 2>&1)
EXIT=$?
EVENT=$([ $EXIT -eq 0 ] && echo "success" || echo "failure")
curl -X POST "https://api.drumbeats.io/v1/ping/<monitor-id>/$EVENT" \
-H "Content-Type: application/json" \
-d "{\"payload\":$(echo "$OUTPUT" | python3 -c 'import json,sys;print(json.dumps(sys.stdin.read()))'),\"run_id\":\"job-$$\"}"OUTPUT=$(python job.py 2>&1)
EXIT=$?
EVENT=$([ $EXIT -eq 0 ] && echo "success" || echo "failure")
curl -X POST "https://api.drumbeats.io/v1/ping/<monitor-id>/$EVENT" \
-H "Content-Type: application/json" \
-d "{\"payload\":$(echo "$OUTPUT" | python3 -c 'import json,sys;print(json.dumps(sys.stdin.read()))'),\"run_id\":\"job-$$\"}"Payload resolution rules
When Drumbeats receives a POST, it stores the payload using this precedence:
- Explicit
payloadfield inside a JSON body → stored as-is. - Entire raw body (when no explicit
payloadfield) → the whole body is stored as the payload. This is how the plain-text /--data-binary @-form works. - No body → no payload stored. The ping still counts as 1 Beat.
This means you can POST a flat JSON object and the dashboard will render it verbatim:
curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/log \
-H "Content-Type: application/json" \
-d '{"processed": 1240, "skipped": 3, "duration_ms": 1820}'curl -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/log \
-H "Content-Type: application/json" \
-d '{"processed": 1240, "skipped": 3, "duration_ms": 1820}'Progress logs
The log event records intermediate state without changing monitor status. Each log ping costs 1 Beat and shows up on the run timeline next to the start and finish pings (when they share a run_id).
API="https://api.drumbeats.io/v1/ping/<monitor-id>"
RUN_ID="import-$(date +%s)"
curl -sf "$API/start?run_id=$RUN_ID"
python import.py --phase=extract
curl -sf -X POST "$API/log?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Extract: 5000 rows fetched"}'
python import.py --phase=transform
curl -sf -X POST "$API/log?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Transform: 4998 rows processed, 2 skipped"}'
python import.py --phase=load
curl -sf "$API/success?run_id=$RUN_ID"API="https://api.drumbeats.io/v1/ping/<monitor-id>"
RUN_ID="import-$(date +%s)"
curl -sf "$API/start?run_id=$RUN_ID"
python import.py --phase=extract
curl -sf -X POST "$API/log?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Extract: 5000 rows fetched"}'
python import.py --phase=transform
curl -sf -X POST "$API/log?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Transform: 4998 rows processed, 2 skipped"}'
python import.py --phase=load
curl -sf "$API/success?run_id=$RUN_ID"Use log for phases whose state actually helps debugging. Routine "step finished" notes add Beats with little payoff — see Beats & usage for the accounting.
Multi-environment with slug URLs
The project-slug URL form lets the same script run against multiple Drumbeats projects (staging vs production) without code changes. The monitor's slug stays constant; only the project ID changes per environment.
# .env
DRUMBEATS_PROJECT="proj_abc123"
DRUMBEATS_SLUG="nightly-sync"
API="https://api.drumbeats.io/v1/s-ping/$DRUMBEATS_PROJECT/$DRUMBEATS_SLUG"
RUN_ID="sync-$(date +%s)"
curl -sf "$API/start?run_id=$RUN_ID"
python sync.py
curl -X POST "$API/success?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Synced 5000 records"}'# .env
DRUMBEATS_PROJECT="proj_abc123"
DRUMBEATS_SLUG="nightly-sync"
API="https://api.drumbeats.io/v1/s-ping/$DRUMBEATS_PROJECT/$DRUMBEATS_SLUG"
RUN_ID="sync-$(date +%s)"
curl -sf "$API/start?run_id=$RUN_ID"
python sync.py
curl -X POST "$API/success?run_id=$RUN_ID" \
-H "Content-Type: application/json" \
-d '{"payload":"Synced 5000 records"}'Swap DRUMBEATS_PROJECT between staging and production — same slug, different project, same script.
Tips
Related guides
- Ping API reference — events, methods, and response shapes.
- Scheduled pings and Event-driven pings — the patterns that consume payloads.
- Payloads & logs concept — what gets stored, where, and for how long.
- Beats & usage — how payload size affects Beats consumption.