Event-Driven Pings

Ping patterns for Event-driven monitors. Always send start with a unique run_id, then matching success or failure with the same run_id. Concurrency-safe.

Event-driven pings are the Ping API patterns Event-driven monitors use — queue workers, webhook handlers, API-triggered jobs, anything that runs on demand rather than on a schedule. The required shape is start with a unique run_id, followed by success or failure carrying the same run_id. Without the run_id, Drumbeats cannot tell which finish ping pairs with which start when multiple instances are running.

This page consolidates the older /event-driven-pings and /event-driven-job-pings references — both old URLs now resolve here.

Required pattern

Every execution of an event-driven job should send three pings: start, then either success or failure. All three carry the same run_id value, unique per execution.

bash
RUN_ID="job-$(date +%s)-$$"

curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/start?run_id=$RUN_ID"
python process_order.py
if [ $? -eq 0 ]; then
  curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/success?run_id=$RUN_ID"
else
  curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/failure?run_id=$RUN_ID"
fi
RUN_ID="job-$(date +%s)-$$"

curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/start?run_id=$RUN_ID"
python process_order.py
if [ $? -eq 0 ]; then
  curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/success?run_id=$RUN_ID"
else
  curl -sf "https://api.drumbeats.io/v1/ping/<monitor-id>/failure?run_id=$RUN_ID"
fi

The run_id is what lets Drumbeats correlate pings across 10 concurrent workers all hitting the same monitor. Without it, two workers' start and success events arrive interleaved and Drumbeats cannot compute per-run duration or detect a hung run.

Language examples

Each example wires the same start → success/failure flow with proper error handling. The Drumbeats integration guides expand on each pattern.

Python
import requests, uuid

API = "https://api.drumbeats.io/v1/ping/<monitor-id>"

def run_job():
    run_id = f"job-{uuid.uuid4()}"
    requests.get(f"{API}/start", params={"run_id": run_id}, timeout=5)
    try:
        do_work()
        requests.get(f"{API}/success", params={"run_id": run_id}, timeout=5)
    except Exception as e:
        requests.post(
            f"{API}/failure",
            params={"run_id": run_id},
            json={"payload": str(e)},
            timeout=5,
        )
        raise
import requests, uuid

API = "https://api.drumbeats.io/v1/ping/<monitor-id>"

def run_job():
    run_id = f"job-{uuid.uuid4()}"
    requests.get(f"{API}/start", params={"run_id": run_id}, timeout=5)
    try:
        do_work()
        requests.get(f"{API}/success", params={"run_id": run_id}, timeout=5)
    except Exception as e:
        requests.post(
            f"{API}/failure",
            params={"run_id": run_id},
            json={"payload": str(e)},
            timeout=5,
        )
        raise
Node.js / TypeScript
const API = "https://api.drumbeats.io/v1/ping/<monitor-id>";

async function runJob(jobId: string) {
  const runId = `job-${jobId}-${Date.now()}`;
  await fetch(`${API}/start?run_id=${runId}`);
  try {
    await doWork(jobId);
    await fetch(`${API}/success?run_id=${runId}`);
  } catch (err) {
    await fetch(`${API}/failure?run_id=${runId}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ payload: err instanceof Error ? err.message : String(err) }),
    });
    throw err;
  }
}
const API = "https://api.drumbeats.io/v1/ping/<monitor-id>";

async function runJob(jobId: string) {
  const runId = `job-${jobId}-${Date.now()}`;
  await fetch(`${API}/start?run_id=${runId}`);
  try {
    await doWork(jobId);
    await fetch(`${API}/success?run_id=${runId}`);
  } catch (err) {
    await fetch(`${API}/failure?run_id=${runId}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ payload: err instanceof Error ? err.message : String(err) }),
    });
    throw err;
  }
}
Go
package main

import (
    "fmt"
    "net/http"
    "time"
)

const api = "https://api.drumbeats.io/v1/ping/<monitor-id>"

func runJob(jobID string) error {
    runID := fmt.Sprintf("job-%s-%d", jobID, time.Now().UnixNano())
    http.Get(fmt.Sprintf("%s/start?run_id=%s", api, runID))

    if err := doWork(jobID); err != nil {
        http.Get(fmt.Sprintf("%s/failure?run_id=%s", api, runID))
        return err
    }
    http.Get(fmt.Sprintf("%s/success?run_id=%s", api, runID))
    return nil
}
package main

import (
    "fmt"
    "net/http"
    "time"
)

const api = "https://api.drumbeats.io/v1/ping/<monitor-id>"

func runJob(jobID string) error {
    runID := fmt.Sprintf("job-%s-%d", jobID, time.Now().UnixNano())
    http.Get(fmt.Sprintf("%s/start?run_id=%s", api, runID))

    if err := doWork(jobID); err != nil {
        http.Get(fmt.Sprintf("%s/failure?run_id=%s", api, runID))
        return err
    }
    http.Get(fmt.Sprintf("%s/success?run_id=%s", api, runID))
    return nil
}
PHP
$api = 'https://api.drumbeats.io/v1/ping/<monitor-id>';
$runId = 'job-' . uniqid('', true);

file_get_contents("$api/start?run_id=$runId");
try {
    doWork();
    file_get_contents("$api/success?run_id=$runId");
} catch (Throwable $e) {
    $ctx = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/json',
            'content' => json_encode(['payload' => $e->getMessage()]),
        ],
    ]);
    file_get_contents("$api/failure?run_id=$runId", false, $ctx);
    throw $e;
}
$api = 'https://api.drumbeats.io/v1/ping/<monitor-id>';
$runId = 'job-' . uniqid('', true);

file_get_contents("$api/start?run_id=$runId");
try {
    doWork();
    file_get_contents("$api/success?run_id=$runId");
} catch (Throwable $e) {
    $ctx = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/json',
            'content' => json_encode(['payload' => $e->getMessage()]),
        ],
    ]);
    file_get_contents("$api/failure?run_id=$runId", false, $ctx);
    throw $e;
}

With progress logs

Send log pings between phases to make hung runs easier to triage. Each log carries the run's run_id so it shows up on the right execution timeline.

Choosing a run_id strategy

Pick the run_id shape that gives you the most useful correlation for the job at hand:

StrategyExampleUse when
Timestampjob-$(date +%s)Low-concurrency scripts where collisions are unlikely.
UUIDjob-$(uuidgen)High-concurrency workers, queue consumers, anything multi-process.
Domain IDorder-${order.id}You want the dashboard to show natural identifiers (order numbers, ticket IDs).
Timestamp + randomjob-$(date +%s)-$RANDOMCheap uniqueness without uuidgen, fine for moderate concurrency.

Status rules

SituationResult
start then matching success (same run_id)Run recorded as successful with duration.
start then matching failure (same run_id)Failure counter +1; monitor flips DOWN once failure_tolerance is exceeded.
start with no finish before max_duration_secondsHung-run event, counted as a failure for incident purposes.
success without a preceding startRecorded, but no duration computed for that run.
Two start pings with the same run_idOnly the first opens the run; subsequent starts are stored on the timeline but do not double-count.

Tips

  • Event-driven monitors — when to pick this monitor type and how it interprets these pings.
  • Status signals — events vs exit-code pings.
  • Payloads — full reference for payload, run_id, and duration_ms.
  • Production hardening — retries, timeouts, and how to absorb transient ping failures without flipping the monitor.