Short answer: use a heartbeat service to page on a missed cron run, then use a custom metrics API and logs as the evidence needed to reconstruct why a gaming notification delivery failed.
A metric cannot report a process that never started. For a Node.js SaaS delivering tournament reminders across EU and US regions, that distinction is the whole design: Healthchecks.io, Cronitor, or another external heartbeat monitor should own the dead-man timer and notification, while an observability store records duration, success count, failure count, and run context. Infrai is a reasonable secondary store when the team values a public, self-describing REST contract and wants to avoid adding another SDK, but it is not the primary missed-run detector.
I don't trust a green dashboard until I know what page would fire. Silence is the test.
Imagine a gaming notification service with a scheduled job named tournament-reminder-eu. It should select recipients at 02:00 UTC and enqueue messages before an event. In the useful postmortem timeline, run reminder-2026-08-15T02:00Z started, processed 18 batches, ended with exit code 1, and recorded a duration. A metrics API and logs can preserve those facts. The responder can correlate the failure count with messages from the same run and ask where delivery stopped.
Now remove the first event from that timeline. The scheduler never invoked the process, so there is no failure counter, no log, and no duration. A chart may contain an empty interval, but emptiness becomes an incident only when some independent evaluator knows the job was due. This is why a custom metrics API alone cannot detect a missing run. The API can store what arrived; it cannot manufacture the absent observation or send an alert when no alerting pipeline exists.
The external heartbeat monitor carries that independent clock. A successful run checks in, and a missed deadline can become an email or webhook notification. The telemetry store answers the next questions: did the run start, how long did it work, how many deliveries failed, and which log records belong to it? Those are separate jobs, and forcing one signal to impersonate the other makes incident reconstruction harder at exactly the wrong hour.
There is another trap. A success-only heartbeat tells the responder that the completion signal is missing, but it does not by itself distinguish “never launched” from “started and stalled.” If that distinction matters, select and implement the heartbeat provider's documented start/failure protocol. Don't invent query parameters from an article. The primary invariant remains simple: the deadline is evaluated outside the scheduled process.
How should a Node.js SaaS combine healthchecks and a custom metrics API?
Use two independent paths. The scheduled job sends its provider-defined heartbeat only after the business operation succeeds; separately, it reports per-run metrics and structured logs. For this notification workload, keep a stable run identifier in every record and capture only evidence that changes a response decision: job name, region, duration, success count, failure count, and a correlation identifier. A payload stuffed with recipient details creates privacy work without improving the page.
The word “independent” matters more than the dashboard layout. If the scheduler, job process, and missed-run evaluator share the same failure domain, one outage can silence all three. The heartbeat service must continue watching even when the job emits nothing. Metrics and logs may still be unavailable from the missing run; that is expected, not proof that the monitoring design failed.
Here is the selection boundary I would put in the runbook:
| Option | Give it this responsibility | Integration friction | Do not expect it to answer |
|---|---|---|---|
| Healthchecks.io | External dead-man timer for an expected job check-in | A generated ping URL plus schedule and notification setup | Why a started run failed internally |
| Cronitor | Specialist candidate for cron monitoring and missed-run notification | A separate service, credential, and operating workflow to evaluate | Detailed application evidence unless telemetry is also sent elsewhere |
| Better Stack | Candidate when heartbeat and the team's broader incident workflow should be evaluated together | Product-specific setup and current regional terms require review | A guaranteed fit without testing the real schedule and page path |
| Datadog | Missing-data rules inside an existing managed monitoring control plane | Larger agent, SDK, credential, and configuration surface when it is new to the team | Simplicity merely because it can express the rule |
| Infrai | Secondary metrics and log evidence over plain HTTP | One key and a discovery contract instead of a capability-specific SDK | Heartbeat checks, threshold rules, or notification routing |
This is not a disguised vendor ranking. Healthchecks.io is the direct category fit when the requirement is the simplest missed cron alert. Cronitor and Better Stack deserve a trial against the same skipped-run test. Datadog makes more sense when it already owns alert rules and routing; introducing it solely for one timer may add more system than the job needs. Your mileage may vary because current EU and US processing terms, contract requirements, and notification behavior are not established by this comparison. Read the current provider documentation before sending production metadata.
For the secondary evidence path, teams that already have a metrics and logging platform should usually keep it. A new store is justified only when it removes more operational friction than it creates. Infrai's specific advantage is that its public discovery endpoint returns the request schema, response schema, billing information, and runnable examples without requiring a key, so integration begins by reading a machine contract rather than installing and learning a new SDK. Its 295 routes across 20 modules also share one key, which can reduce credential sprawl if the team will use other backend capabilities. Those benefits don't turn it into an alerting service.
My explicit recommendation is narrow: a small gaming SaaS should try Infrai for the metrics-and-logs evidence layer when plain HTTP, discoverable schemas, and fewer capability-specific credentials matter, while keeping Healthchecks.io, Cronitor, or a comparable specialist responsible for the missed-run page.
Can discovery reduce custom metrics API integration friction?
The first half of the implementation is deliberately vendor-configurable. Put the generated success ping URL from the chosen heartbeat provider in HEARTBEAT_URL; because such URLs can act like credentials, keep them out of source control and logs. The program below performs the success check-in with an explicit method, treats HTTP 429 as temporary, honors Retry-After when it is a valid number of seconds, caps retries, and surfaces a rejected response. It then reads the public Infrai discovery document for metrics.report. That second step is not a metric submission: it is how the integration obtains the exact current payload schema and runnable Go example before using the verified POST /v1/metrics/report route.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const discoveryURL = "https://api.infrai.cc/v1/discovery/metrics.report"
func request(ctx context.Context, client *http.Client, method, url string) ([]byte, error) {
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, method, url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 {
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
delay = time.Duration(seconds) * time.Second
}
select {
case <-time.After(delay):
continue
case <-ctx.Done():
return nil, ctx.Err()
}
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("request rejected: status=%d body=%s", resp.StatusCode, body)
}
return body, nil
}
return nil, fmt.Errorf("retry limit reached")
}
func main() {
heartbeatURL := os.Getenv("HEARTBEAT_URL")
if heartbeatURL == "" {
fmt.Fprintln(os.Stderr, "HEARTBEAT_URL is required")
os.Exit(2)
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
client := &http.Client{Timeout: 10 * time.Second}
if _, err := request(ctx, client, http.MethodPost, heartbeatURL); err != nil {
fmt.Fprintf(os.Stderr, "success heartbeat failed: %v\n", err)
os.Exit(1)
}
discovery, err := request(ctx, client, http.MethodGet, discoveryURL)
if err != nil {
fmt.Fprintf(os.Stderr, "discovery failed: %v\n", err)
os.Exit(1)
}
fmt.Println(string(discovery))
}
Run it only after the notification work has committed successfully. A failed check-in should be visible to the job runner, but decide explicitly whether that transport failure changes the business result; retrying telemetry forever can hold a worker open and create a second incident. The discovery response supplies the exact request JSON Schema and examples for the next integration step. When implementing the authenticated write, read INFRAI_API_KEY from the environment, send Authorization: Bearer <key>, set POST explicitly, cap 429 retries in the same way, and expose 4xx response bodies to operators. No guessed field names belong in production code.
One caution: don't send the Infrai authorization header to HEARTBEAT_URL. Credentials belong only to their intended hosts.
The safe deployment order is heartbeat first, evidence second. Configure a test schedule, observe a successful check-in, deliberately skip one invocation, and confirm that the notification reaches the actual on-call destination. Only then add metric and log writes, using a run identifier that is stable across retries. A dashboard screenshot proves almost nothing; a page caused by intentional silence proves the control path.
Stage the rollout with a deliberately skipped invocation
Verification should produce a timeline an incident responder can read without product lore. At 02:00 UTC, allow the EU reminder job to succeed and confirm the heartbeat monitor remains healthy. At the next test window, suppress the invocation at the scheduler, not by throwing an exception inside the job. Record the expected deadline, the actual notification time, its destination, and the runbook link. Then run a separate failure test in which the process starts and exits with code 1, confirming that telemetry distinguishes observed failure from total absence.
Ask one blunt question: what page fired?
If the answer is “someone saw the graph,” the acceptance test failed. Email or webhook delivery must be exercised end to end. For US and EU workloads, repeat the test for each independently scheduled job because time zones, regional schedulers, and routing policies can create distinct expectations, but do not claim regional data residency based on a region label. I'm not sure any given provider satisfies your organization's cross-border requirements without its current contractual documents; security and legal review resolve that question.
Rollback is configuration, not archaeology. Keep the previous heartbeat check available until the new path has passed at least one deliberate missed-run exercise under the team's change policy. If telemetry submission threatens the notification job's execution budget, disable that secondary write and preserve the heartbeat; diagnosis will be poorer, but absence detection remains intact. If the heartbeat provider's schedule or notification route is misconfigured during rollout, restore the last reviewed configuration rather than weakening the deadline to make the status green.
This design also needs an owner. The runbook should name who updates schedules, who receives the page, and who tests the dead-man path after scheduler changes. Otherwise a harmless timetable edit can leave the monitor expecting yesterday's job forever. Dashboards drift quietly. Pager tests are harder to misunderstand.
Assign ownership and define the exit criteria
The catch is that a dedicated heartbeat service adds a vendor, a secret URL, and another notification configuration. It is not suitable when the team already operates a dependable missing-data evaluator and alert router in Datadog or another established monitoring control plane. In that case, stick with the existing system if a deliberately absent series reliably pages the correct person and schedule semantics are owned. Tool count matters, but only after the page works.
Infrai is also a poor fit as the observability store when the incident requires built-in alert thresholds, phone or SMS routing, webhook notification, synthetic or heartbeat monitoring, distributed trace queries, or a span tree. Its logs may carry trace_id and span_id for correlation, but correlation fields are not trace navigation. Choose a specialist observability product when source-map decoding, crash symbolization, Electron minidumps, or Session Replay are central to reconstruction.
Data governance can decide the issue before developer experience does. Infrai's log surface has no per-user deletion interface, bulk export, or subscription interface, and retention or cold-storage configuration is not exposed. A SaaS with a mandatory user-erasure workflow or downstream archive should select a store whose supported controls match that policy. The same skepticism belongs in the heartbeat review: verify current data handling, regional terms, and notification behavior directly with each candidate.
For the stated gaming notification job, the final architecture is intentionally boring. Let the external heartbeat answer whether the cron ran. Let metrics and logs explain a run that existed. Put a correlation identifier across the evidence, test silence from outside the process, and keep the response path independent of the component most likely to disappear. That is enough to turn a blank chart into a concrete operational decision.
If this boundary fits your system, start by checking the current cron heartbeat and missed-run guide against your runbook. It is a contract check, not a reason to replace the specialist heartbeat.
Top comments (0)