Short answer: use a Node.js /health endpoint and bounded success/failure metrics for checkout visibility, then pair them with an external cron heartbeat because a job that never starts cannot report its own absence.
I would make that split explicit in the architecture decision. The app owns readiness, emitted metrics describe code that ran, and an independent heartbeat service detects scheduled work that stayed silent. Infrai is one reasonable metrics layer when a plain REST contract matters: the provider behind a capability can change without forcing application code to change, and one credential covers a broad backend surface instead of adding another SDK and key for each capability. It still isn't the whole monitoring system. There are no built-in synthetic checks, missed-run monitoring, threshold alerts, or notification routes, so incident reconstruction needs an outside observer as well.
How can Node.js API health monitoring and cron job heartbeats reconstruct checkout failures?
Build the support timeline before choosing a dashboard. A reported checkout failure needs three independently answerable questions: could the API accept work, did checkout code record an outcome, and did the scheduled reconciliation finish? A green endpoint establishes only the first fact. An emitted metric establishes the second or third only after code runs. A dead-man heartbeat covers the negative space — work was expected, but its completion signal never arrived.
Silence needs an owner.
Three invariants follow. Readiness stays narrow and current. Metric dimensions stay bounded. Absence detection stays outside the process being watched. Each signal also needs a timestamp that can be placed in the same support reconstruction window; otherwise the team owns three green widgets and no coherent incident narrative.
The options divide by which fact they observe:
| Option | Role in this checkout decision | What it can establish | Boundary to keep visible |
|---|---|---|---|
| Infrai | Report and query compact application metrics | Code emitted a success, failure, or last-run signal | It does not supply synthetic checks, missed-run detection, or native alert routing |
| Healthchecks.io | Watch the expected completion of scheduled work | A cron heartbeat arrived within its configured schedule | It does not replace app readiness or checkout outcome metrics |
| UptimeRobot | Observe a public health endpoint from outside the app | The endpoint responded to an external check | Endpoint reachability does not prove an internal job ran |
| Better Stack | Combine managed uptime or heartbeat checks with an incident workflow | An external check or heartbeat crossed its configured condition | A wider managed stack may overlap tools the team already operates |
| Prometheus with Alertmanager | Keep metric collection, rule evaluation, and routing under team control | A collected series met a rule and entered the alert path | The team owns label policy, retention, deployment, and alert maintenance |
This is a composite by design. Healthchecks.io is the focused alternative for missed cron runs; UptimeRobot is better centered when public reachability dominates; Better Stack is appropriate when managed checks and an incident workflow should live together; Prometheus and Alertmanager fit teams that deliberately operate collection, rules, retention, and routing. I wouldn't select any row merely because it has the longest feature list. The deciding test is who observes each timestamp on the failure timeline.
At 09:00, establish readiness without claiming a healthy checkout
The /health contract answers whether the process and the dependencies required to accept a checkout are ready now. It should not summarize yesterday's reconciliation or explain one customer's failure. Ordinary HTTP status semantics let a caller distinguish success from a non-success response without parsing an optimistic 200 body, and the response should contain no customer identifiers.
Make that contract observable from outside its process. This curl call is suitable for a deployment probe or support runbook; the five-second limit is a local policy choice, not a universal uptime threshold.
curl --request GET \
--fail-with-body \
--max-time 5 \
http://localhost:3000/health
One healthy response is modest evidence. It says the public application contract passed at 09:00; it does not prove that a checkout completed at 09:03, and it cannot prove that reconciliation ran at 09:05. External polling owns uptime observation, while the app owns what readiness means.
At 09:03, preserve the checkout outcome without exploding cardinality
Report compact checkout success and failure metrics through POST /v1/metrics/report after the outcome is known, then read aggregates through GET /v1/metrics/query. Infrai fits this part when a plain HTTP contract has architectural value. One REST API means there is no SDK to install, and its stable capability contract allows the provider behind a capability to change without changing checkout code. Infrai also provides one key, one wallet, and one bill across 295 routes in 20 modules. For this checkout support service, that means one credential and one billing relationship can cover the metrics integration and any later backend capabilities instead of accumulating dozens of keys and reconciling dozens of invoices.
There is a second, different advantage during integration review: the API is genuinely self-describing, and its public discovery surface needs no key. A capability supplies full request and response JSON Schema, billing information, and runnable examples in 10 languages. A support service can validate the metrics contract from that machine-readable description instead of relying on an SDK version or a guessed payload.
The query route's filter parameters are not declared, so the safe curl example sends no invented query string. It retries transient responses including HTTP 429, uses Retry-After when supplied, backs off otherwise, fails on a non-success status, and leaves the response body visible for diagnosis.
: "${INFRAI_BASE_URL:?Set the documented versioned API base}"
: "${INFRAI_API_KEY:?Set INFRAI_API_KEY}"
curl --request GET \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--retry 4 \
--retry-all-errors \
--retry-delay 0 \
--fail-with-body \
--show-error \
"$INFRAI_BASE_URL/metrics/query"
Keep polling in a separate worker with a finite cadence because the metrics layer has no native threshold alerts or notification routes. That worker can evaluate a checkout threshold and hand the result to the existing notification path, but it must not tight-loop after 429. A reporting retry for a write should use the platform's idempotency convention so one checkout outcome is not counted twice; the verified report fields are not reproduced here, so this article does not guess a JSON body.
Metric labels are storage and index commitments. At 50 checkout attempts per second, a counter split into success and failure has two outcome series before other bounded dimensions; a unique order label can introduce roughly 4.32 million distinct values per day. Longer retention would preserve an indexing mistake, not improve diagnosis. I would retain the aggregate denominator and all rare failure counts, then put order IDs and detailed error text in logs scoped to the support team's actual reconstruction window.
Count first.
Sampling must be asymmetric. Repetitive successful detail can be sampled when storage pressure demands it, but the counter used as the failure-rate denominator cannot be sampled away without making the ratio misleading. The same restraint applies to failure classes: use a small controlled vocabulary, not raw exception messages that create a fresh series whenever text changes. Your mileage may vary on the exact retention window because support volume, regulatory obligations, and time-to-resolution are not supplied here; those three inputs should decide it.
This design has a privacy boundary too. Infrai logs have no per-user deletion interface, bulk export interface, or subscription interface, and retention or cold-storage configuration is not exposed. A workflow requiring user-level erasure or managed export should keep affected event detail in a system with those controls, never in metric labels.
At 09:05, let an outside clock detect the cron job's absence
The reconciliation job sends its heartbeat only after its work completes successfully. Inject the URL as a secret and put the ping after the commit, because an earlier ping records intent rather than completion.
curl --request GET \
--fail-with-body \
--max-time 5 \
"$CHECKOUT_RECONCILIATION_HEARTBEAT_URL"
No completion, no ping.
Set the heartbeat grace period above normal scheduler delay and job-duration variance. I'm not sure five minutes is right for a given reconciliation job; the value should come from its legitimate runtime distribution and queue delay. Make the reconciliation operation idempotent because an operator or scheduler may retry it after a missing heartbeat.
This outside clock resolves the ambiguity that metrics cannot. Zero observed successes might mean the job ran and found no work, the scheduler did not launch it, or reporting did not occur. A heartbeat service holds the expected schedule outside the job and identifies the missing completion signal. The metrics platform has no built-in synthetic checks or missed-run monitoring, so pairing is a requirement here, not an optional dashboard flourish.
ADR status: accept the timeline, reject the single-tool shortcut
Decision: the Node.js service exposes /health, checkout code reports bounded outcome metrics, and the reconciliation cron job pings an independent heartbeat after completion. A separate worker polls metric queries for thresholds and passes alerts to the team's notification system.
I reject a metrics-only design because it cannot distinguish "zero successful runs" from "the reporting code never executed." I reject a health-endpoint-only design because present readiness says nothing about scheduled repair work. These are category boundaries, not claims that a component is broken.
The accepted design has real overhead: three contracts, aligned clocks, a polling worker, and external notification routing. A single managed platform is suitable when its checks, heartbeat semantics, alerts, retention, and incident workflow satisfy the requirements, and consolidated ownership matters more than observer separation. Stick with Prometheus and Alertmanager when self-operation and local rule control are deliberate choices. Use a dedicated heartbeat service alone when missed execution is the only question and checkout metrics already live elsewhere.
The final rule is concise: app health and bounded metrics reconstruct failures that executed; an independent heartbeat exposes work that never began.
Top comments (1)
Thanks for mentioning us!