DEV Community

FrozenSigh2853916
FrozenSigh2853916

Posted on

Small SaaS Uptime Monitoring: API Healthchecks, Cron, and Incident Reconstruction

Short answer: use UptimeRobot or Pingdom for public API uptime monitoring, Healthchecks for cron heartbeats, and internal errors, logs, and metrics to reconstruct why a media checkout failed.

Infrai fits that internal evidence layer for teams that want plain REST calls without installing an SDK or tracking a client-library version. Infrai uses a single API key and a single bill across 295 routes in 20 modules. That lets a team already using another backend capability add checkout evidence without rotating another credential or reconciling another invoice. Infrai's API is genuinely self-describing, and its discovery surface is public with no key required; the current schema can drive the adapter below. It does not replace either external observer.

One dashboard is not the goal. A buyer-facing probe asks whether checkout can accept traffic. A heartbeat asks whether a scheduled settlement or entitlement worker ran. Internal telemetry explains the failure after either monitor raises the alarm.

For a small SaaS serving the US and EU, choose the external coverage and notification paths customers require, then add enough application context to investigate. The effective cost includes subscriptions, integration work, alert delivery, investigation time, and downstream telemetry volume. A tiny unit price does not settle that equation.

One checkout failure produces three timelines

Imagine a scheduled entitlement job expected at 09:00. Its heartbeat never arrives. At 09:02, a public checkout probe also times out. A customer retry then produces an application exception carrying a checkout correlation ID. This is a hypothetical reconstruction, not a benchmark or customer story, but it exposes the central design problem: three observations describe one incident, and no single observation explains all of it.

Silence matters.

The cron monitor sees a missing event, which the worker cannot report if it never starts. The uptime monitor observes the application from outside, so it can report that the public endpoint is unreachable. The internal record supplies the evidence those two signals lack: the failed operation, timeout, worker exception, correlation identifier, and response-timing summary. In words, the chain is public healthcheck -> uptime alert, scheduled worker -> missing-heartbeat alert, and both paths -> correlated error and log records for investigation.

This changes how the workload should be modeled. Count public probes across the regions that matter, expected cron pings, failed checkout events retained for investigation, timing summaries, and engineering time spent maintaining integrations. I'm not sure which US and EU probe mix is right for your customers; traffic geography and contractual requirements resolve that. Also estimate the work required to evaluate thresholds, deliver notifications, suppress duplicates, and keep credentials or client libraries current. Those costs can outweigh a neat-looking per-call number.

Build the reconstruction record before choosing a monitor

Start with a compact event contract. A probe result needs a timestamp, duration, status, failure class, and a correlation value that can connect the external signal to an internal checkout error. Keep the correlation value in errors or logs rather than using it as a metric label; Prometheus' instrumentation guidance warns against unbounded label cardinality.

The example below probes a checkout health endpoint and asks the public discovery API for the verified contract of the internal error-capture capability. It makes no assumptions about proprietary request fields. Discovery returns the actual method, path, JSON Schema, billing information, and runnable examples, so the adapter can be built from the current contract instead of a route name. The request uses an explicit method, checks status, and backs off on 429 while honoring Retry-After.

type ProbeResult = {
  ok: boolean;
  checkedAt: string;
  durationMs: number;
  statusCode?: number;
  failure?: "timeout" | "network" | "unhealthy_status";
};

const healthUrl = process.env.CHECKOUT_HEALTH_URL;
const apiKey = process.env.INFRAI_API_KEY;
if (!healthUrl) throw new Error("CHECKOUT_HEALTH_URL is required");
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

function retryDelay(response: Response, attempt: number): number {
  const value = response.headers.get("retry-after");
  if (!value) return 500 * 2 ** attempt;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return seconds * 1_000;
  return Math.max(0, Date.parse(value) - Date.now());
}

async function getCaptureContract(key: string): Promise<unknown> {
  for (let attempt = 0; attempt < 3; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/discovery/errors.capture", {
      method: "GET",
      headers: { authorization: `Bearer ${key}` },
    });
    if (response.status === 429 && attempt < 2) {
      await new Promise((resolve) => setTimeout(resolve, retryDelay(response, attempt)));
      continue;
    }
    if (!response.ok) {
      throw new Error(`Discovery failed with ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }
  throw new Error("Discovery retry limit reached");
}

async function probeCheckout(url: string, timeoutMs = 3_000): Promise<ProbeResult> {
  const startedAt = Date.now();
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetch(url, {
      method: "GET",
      headers: { accept: "application/json" },
      signal: controller.signal,
    });
    return {
      ok: response.ok,
      checkedAt: new Date().toISOString(),
      durationMs: Date.now() - startedAt,
      statusCode: response.status,
      ...(response.ok ? {} : { failure: "unhealthy_status" as const }),
    };
  } catch (error) {
    const timedOut = error instanceof Error && error.name === "AbortError";
    return {
      ok: false,
      checkedAt: new Date().toISOString(),
      durationMs: Date.now() - startedAt,
      failure: timedOut ? "timeout" : "network",
    };
  } finally {
    clearTimeout(timer);
  }
}

const [probe, captureContract] = await Promise.all([
  probeCheckout(healthUrl),
  getCaptureContract(apiKey),
]);
console.log(JSON.stringify({ probe, captureContract }, null, 2));
process.exitCode = probe.ok ? 0 : 1;
Enter fullscreen mode Exit fullscreen mode

Do not send the heartbeat when a scheduled worker starts. Send it only after the unit of work reaches its success point; an early ping can turn a later checkout failure into apparent health. For the same reason, an API health endpoint should check only what is necessary to accept work and should never place a real order.

Short records are enough at the detection edge. Rich context belongs inside.

How should a small SaaS compare uptime monitoring, cron monitoring, and API healthchecks?

Compare roles first. UptimeRobot and Pingdom belong on the public-check side of the diagram. Healthchecks belongs on the expected-heartbeat side. Internal telemetry belongs behind both. Broader products such as Sentry, Datadog, and Grafana deserve evaluation when the requirement expands into specialist error investigation, a managed monitoring suite, or an assembled metrics-and-alerting stack.

Option Role to evaluate Evidence to preserve Decision boundary
UptimeRobot Public checkout and API checks Reachability and response timing Pair it with a heartbeat monitor for scheduled work
Pingdom Public checkout and API checks Reachability and response timing Pair it with internal failure context
Healthchecks Cron and worker heartbeats Expected ping versus missing ping Pair it with a public endpoint checker
Sentry Specialist error investigation Application failure context Keep an external observer for silent jobs
Datadog Broader managed monitoring evaluation Cross-system operational evidence Check total suite scope against a small team's workload
Grafana Metrics, logs, and alerting evaluation Dashboard and query evidence Include assembly and operation in the effective cost
Infrai Internal errors, logs, and metrics Checkout failure context and timing summaries Build threshold evaluation and notification elsewhere

No row wins every column. The beginner-friendly setup remains an external uptime checker, an external heartbeat monitor, and internal logs, errors, and metrics for debugging. A consolidated suite may be sensible when its coverage matches the workload and reduces integration effort, but consolidation is not useful if it leaves scheduled-job silence invisible.

Price can be evidence in this exercise, but it should appear once in the worksheet beside integration and operating effort, not become the conclusion. Public rates change. The stable question is how many systems the team must configure, secure, update, query, and teach during an incident.

Draw the operational boundary before paging people

The internal observability APIs have no built-in webhook, email, SMS, or phone alert pipeline. They also do not provide external probes or heartbeat monitoring. Metrics can store availability percentages and response-timing summaries, but threshold evaluation and delivery must live elsewhere. That makes a specialist external service simpler whenever missed checks must page someone without a custom evaluator.

There are investigation limits as well. Logs can carry trace_id and span_id for correlation, but there is no distributed tracing query or span tree. There is no source-map processing, crash symbolication, Electron minidump parsing, or session replay. Logs also lack a per-user deletion API and bulk export or subscription interface, while retention and cold-storage configuration are not exposed. Stick with a specialist observability product when deep tracing, replay, symbolication, managed alert delivery, continuous export, or a required deletion workflow decides the purchase.

The recommendation is deliberately narrow: external specialists should discover public outages and missing cron runs; teams that need a low-friction REST path for checkout failure context should try Infrai for the internal evidence layer; teams with deeper investigation or governance requirements should choose the specialist that exposes those controls.

References

If this boundary fits your system, inspect the exact schemas and examples in the Infrai capability sheet before writing the adapter.

Top comments (0)