Skip to main content

API Reference

Webhook Events

Receive vendor incident notifications in your own system. Configure a webhook in Settings → Alerts, set a signing secret, then send a test to verify your receiver.

HTTP headers

Webhook requests use JSON. A configured secret adds the signature header. Channels can also include custom headers.
Content-Type
application/json

All webhook payloads are JSON-encoded.

X-Checkupstream-Signature
<64 lowercase hex characters>

HMAC-SHA256 of the raw request body using your webhook secret, with no algorithm prefix. Included only when a secret is configured. Verify the signature before processing the event.

Verifying signatures

Always verify the X-Checkupstream-Signature header before processing events. The value is an HMAC-SHA256 hash of the raw request body, hex-encoded with no algorithm prefix.
TypeScript
import crypto from "node:crypto";

export function verifySignature(
  body: string,
  signature: string,
  secret: string,
): boolean {
  // Check the format before decoding untrusted header values.
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;
  const expected = crypto.createHmac("sha256", secret).update(body).digest();
  return crypto.timingSafeEqual(Buffer.from(signature, "hex"), expected);
}

Event payloads

Initial alerts, updates, record closures and tests use different flat payloads. These examples are illustrative. Dependency matches and generated enrichment do not confirm application impact.

incident.detected

Sent for qualifying incident alerts about a service your organization tracks. Your alert configuration determines which incidents trigger delivery.

predictedResolution summarises historical incident start and closure timestamps. Closure can mean a feed entry disappeared or tracking expired. These durations do not confirm vendor or application recovery, and they are not minutes remaining.

Illustrative payload
{
  "event": "incident.detected",
  "serviceId": "svc_stripe",
  "serviceName": "Stripe",
  "incidentId": "inc_abc123",
  "title": "Elevated error rates on the Charges API",
  "impact": "major_outage",
  "affectedProjects": [
    {
      "id": "prj_checkout",
      "name": "checkout-api",
      "dependencies": [
        { "name": "stripe", "version": "14.12.0", "ecosystem": "npm" }
      ]
    }
  ],
  "enrichment": {
    "aiSummary": "Card charges are failing for a subset of requests.",
    "affectedCapabilities": ["Card payments"],
    "survivingCapabilities": ["Subscription billing"],
    "mitigations": ["Queue charge retries behind an idempotency key"],
    "architectureInsight": null,
    "affectedProjects": [
      {
        "id": "prj_checkout",
        "name": "checkout-api",
        "impactScore": 82,
        "dependencies": [
          { "name": "stripe", "version": "14.12.0", "ecosystem": "npm" }
        ]
      }
    ],
    "affectedRegions": ["us-east-1"],
    "customerImpact": "degraded",
    "errorRateDelta": 0.47,
    "latencyDelta": 820,
    "recurrenceCount7d": 2,
    "communityDetectedLeadMs": 240000,
    "source": "ai-cached"
  },
  "predictedResolution": {
    "likelyMinutes": 42,
    "optimisticMinutes": 25,
    "pessimisticMinutes": 90,
    "sampleSize": 11,
    "line": "Historical duration ~42 min, based on 11 recorded Stripe incidents in the last 180 days (range 25 to 90 min). This is not a recovery forecast.",
    "compact": "~42 min historical duration (based on 11 recorded incidents; range 25 to 90 min)"
  },
  "timestamp": "2026-04-06T14:32:00Z"
}

incident.updated

Sent after a status or severity change to the original webhook URL, while its channel is enabled and the alert remains unmuted. Read impact for the current severity and updateBody for the vendor's update.

Illustrative payload
{
  "event": "incident.updated",
  "kind": "incident_updated",
  "serviceId": "svc_stripe",
  "serviceName": "Stripe",
  "incidentId": "inc_abc123",
  "title": "Elevated error rates on the Charges API",
  "status": "monitoring",
  "impact": "degraded",
  "updateBody": "We are monitoring the results of a fix.",
  "timestamp": "2026-04-06T15:02:00Z"
}

incident.resolved

Sent when a tracked record closes, to the original webhook URL while its channel is enabled and the alert remains unmuted. Read resolution before interpreting the event as vendor recovery.

vendor_declared
The vendor explicitly reported its incident resolved. Application recovery still needs your own evidence.
absent_from_feed
The incident disappeared from the vendor feed. No vendor resolution was recorded.
max_age_timeout
The record reached CheckUpstream's 30-day tracking limit. Vendor recovery is unconfirmed.
unspecified
The record closed without a recognized reason. Vendor recovery is unconfirmed.

The duration field is a human-readable interval between the record’s start and closure timestamps, such as 42m. It is not measured application downtime.

Illustrative payload
{
  "event": "incident.resolved",
  "kind": "incident_resolved",
  "serviceId": "svc_stripe",
  "serviceName": "Stripe",
  "incidentId": "inc_abc123",
  "title": "Elevated error rates on the Charges API",
  "resolution": "absent_from_feed",
  "duration": "42m",
  "timestamp": "2026-04-06T15:14:00Z"
}

alert.test

Sent when you press "Send test" on a webhook channel in Settings → Alerts. If a secret is configured, the test uses it to sign the request. Use this event to check your receiver before an incident.

Illustrative payload
{
  "event": "alert.test",
  "service": "Stripe",
  "title": "Test alert from CheckUpstream",
  "timestamp": "2026-04-06T14:32:00Z"
}

Ready to receive events

Add your endpoint and secret, then use Send test to check signature handling and delivery.