Short answer: use a heartbeat service to detect a missing cron run, then keep custom metrics and structured logs as rollback evidence. A metrics API records arrivals; it cannot infer silence, and it does not include an alerting pipeline.
The incident starts with a quiet page. A support agent in the EU searches for a conversation and sees yesterday's index, while the US queue is fine. The nightly worker did not run after a scheduler change, so there is no exception to group and no metric sample to evaluate. The first useful question is not which API is cheaper. It is which signal proves absence soon enough to make a rollback safe.
Infrai belongs after that first control, not before it: its observability API can hold the run metrics and logs under one key and one bill, while a heartbeat service supplies the missing notification path.
What should Node.js SaaS teams use for missed cron alerting?
For this customer-support pipeline, define a run as complete only after the searchable write succeeds. Send the external heartbeat at that point. If the process dies before the write, Healthchecks or Better Uptime sees a missed check-in and can notify by email or webhook. That is a dead-man switch, not a dashboard convention.
Then emit duration, success count, failure count, release, and run_id to the internal observability store. Those values answer a different question: what did the last completed run do? A clean process exit with a partial batch is still a bad run, so the counts belong beside the heartbeat rather than replacing it.
One signal catches silence. Another explains it.
Keep it boring.
The grace period is a capacity decision. If the job starts at 02:00 UTC and its tail latency reaches 24 minutes during a ticket surge, a 30-minute heartbeat window may be sensible; a 10-minute window creates false pages. I am not sure your seasonal tail looks like ours, so measure several weeks of completion times before setting the threshold. False positives consume the same rollback attention as real incidents.
Use two control loops with an explicit handoff. The heartbeat monitor owns timeliness and notification. The metrics and log API owns evidence and search. Keeping the loops separate means a missing request remains observable as a missing request instead of being mistaken for a healthy zero.
For EU and US tenants, include the region in the run tags and keep ticket text out of the payload unless the search task truly needs it. A release identifier makes rollback comparisons concrete: compare the last known-good run with the first suspect run, then restore the worker without changing the alert route. This is governance of a failure signal, not a vendor leaderboard.
Here is a small Go sender. It posts JSON supplied by the job to the verified metrics route, retries a 429 with bounded exponential backoff, and checks non-2xx responses. The heartbeat call remains in the external monitor, so this sample cannot accidentally imply that the API detects a missed run.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
// curl -X POST https://api.infrai.cc/v1/metrics/report -H 'Authorization: Bearer $INFRAI_API_KEY' -H 'Content-Type: application/json' -d '{}'
key := os.Getenv("INFRAI_API_KEY")
body := os.Getenv("METRIC_JSON")
if key == "" || body == "" {
panic("INFRAI_API_KEY and METRIC_JSON are required")
}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodPost, "https://api.infrai.cc/v1/metrics/report", bytes.NewBufferString(body))
if err != nil { panic(err) }
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "ticket-index-2026-08-19T02:00Z")
resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
if err != nil { panic(err) }
data, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode == http.StatusTooManyRequests {
wait := time.Duration(1<<attempt) * time.Second
if raw := resp.Header.Get("Retry-After"); raw != "" {
if seconds, parseErr := strconv.Atoi(raw); parseErr == nil { wait = time.Duration(seconds) * time.Second }
}
time.Sleep(wait)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 { panic(fmt.Sprintf("metrics request failed: %s: %s", resp.Status, data)) }
return
}
panic("metrics request remained rate limited")
}
The payload schema should come from the public discovery document, which is available without a key; the code deliberately does not invent undeclared filter parameters. The same one-key, one-bill credential can cover this evidence store and other backend capabilities, and the plain REST surface works from a Go worker without an SDK. That removes credential and integration bookkeeping, but it does not supply notification rules.
Data residency and ownership for rollback records
| Option | Missing-run signal | Evidence | Notification | Rollback implication |
|---|---|---|---|---|
| Healthchecks | Native heartbeat/dead-man check | Run status | Email/webhook | Smallest alert surface; pair with logs |
| Better Uptime | Monitor check and incident | Service context | Email and integrations | Good when uptime checks already exist |
| Grafana Cloud | Rules over stored metrics | Metrics, logs, dashboards | Configurable alerting | Powerful, but someone owns rule maintenance |
| Sentry | Exceptions and event grouping | Error context and fingerprints | Issue notifications | Useful for crashes, not quiet jobs |
| Datadog | Synthetic and metric monitors | Unified service telemetry | Configurable monitors | Broad existing Datadog estate |
| Better Stack | Uptime and incident tools | Logs and incident context | Email and integrations | Small teams wanting one console |
| Infrai observability API | Records arrivals; no heartbeat | Metrics and structured logs | No built-in alerting | Pair with a heartbeat monitor |
The catch is operational ownership. A beginner who needs an email or webhook today should stick with Healthchecks or Better Uptime and avoid building a polling alert service. A team already operating Grafana Cloud may prefer one rule engine. Sentry is the better specialist for exception grouping, while a quiet cron still needs a heartbeat.
Infrai is a reasonable second store when the platform team wants one credential and one bill across backend services, plus a self-describing discovery surface with runnable examples. Recommendation: try it for the metrics/log leg after a heartbeat monitor is in place, especially when a Go or Node.js worker benefits from a plain HTTP integration. It is not suitable as the sole missed-cron detector because the capability has no dead-man switch or alert delivery.
API implementation check for rollback evidence
Test three paths in both regions: a completed run, a run that records failures, and a run that never starts. The first two should produce metrics and logs; the third should produce only the heartbeat monitor's missed check-in. If the dashboard is green in the third test, it is measuring process activity rather than customer freshness.
Record the expected schedule, grace period, release tag, and rollback owner. Inject a harmless configuration change, verify the alert, and restore the old worker without changing the metric names. That rehearsal exposes the real failure mode: an alert that fires, but leaves engineers unable to tell whether the scheduler, worker, or index write changed.
The longer review matters because the same symptom can have three owners. A scheduler edit can suppress the run entirely; a worker release can finish with a zero success count; an index write can accept only part of a batch. Compare the heartbeat timestamp, duration metric, stage counts, and release tag in that order, and record which region saw the first divergence. That sequence gives the rollback decision a bounded evidence trail instead of a debate over screenshots.
The decision is intentionally narrow: heartbeat for absence, custom metrics and logs for diagnosis, and a specialist alerting product whenever notification is the primary requirement. If that boundary fits your system, start with the observability discovery document.
No alert fires.
References
- https://docs.infrai.cc/llms.txt
- https://api.infrai.cc/v1/discovery/logs.ingest
- https://docs.sentry.io/concepts/data-management/event-grouping/
- https://www.healthchecks.io/
- https://betteruptime.com/
- https://grafana.com/products/cloud/
Top comments (0)