Uptime BasicsDevelopers
Signed outbound events

Trust the signature, not the network.

Uptime Basics sends versioned JSON events to a public HTTPS endpoint. Verify the raw body before processing it, respond quickly, and move slow work to your own queue.

Delivery contract

Transport

  • HTTPS on port 443 only
  • 5-second delivery timeout
  • No redirect following
  • At most 5 delivery attempts
  • 30 days of safe delivery history

Identity

  • eventId identifies the event
  • deliveryId identifies one delivery
  • Manual redelivery preserves eventId
  • Consumers should deduplicate by eventId
Version 1 envelope
{
  "schemaVersion": 1,
  "eventId": "evt_example",
  "eventType": "monitor.status_changed",
  "createdAt": "2026-08-01T18:00:00.000Z",
  "delivery": {
    "id": "delivery_example",
    "attempt": 1,
    "manualRedelivery": false,
    "redeliveryOf": null
  },
  "data": {
    "monitor": {
      "id": "monitor_example",
      "name": "Production website",
      "status": "down",
      "previousStatus": "up"
    }
  }
}
Required security control

Verify the signature

The Uptime-Basics-Signature header uses this form:

Signature header
t=1785600000,v1=HEX_DIGEST

Build the signed message from the timestamp, a period, and the exact raw request body. Calculate HMAC-SHA256 with your endpoint secret. Reject old timestamps and compare against every supplied v1 value in constant time.

Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, header, secret, tolerance = 300) {
  const parts = Object.fromEntries(
    header.split(',').map((part) => part.split('=', 2))
  );
  const timestamp = Number(parts.t);
  const signatures = header.split(',')
    .filter((part) => part.startsWith('v1='))
    .map((part) => part.slice(3));
  if (!Number.isFinite(timestamp) || Math.abs(Date.now() / 1000 - timestamp) > tolerance) return false;
  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  return signatures.some((value) => {
    if (!/^[a-f0-9]{64}$/i.test(value)) return false;
    return timingSafeEqual(Buffer.from(value, 'hex'), Buffer.from(expected, 'hex'));
  });
}
Framework body parsers can change whitespace or encoding. Capture the raw request bytes before parsing JSON.

Secret rotation without downtime

Rotate from Account > Integrations. The new secret is shown once and the previous secret remains valid for 24 hours. During this grace period the signature header contains two v1 values.

  1. Store the new secret alongside the existing secret.
  2. Accept a valid signature from either secret.
  3. Deploy the updated receiver.
  4. Remove the previous secret after the grace period.

Rotating again replaces the previous grace-period secret. Do not repeatedly rotate while a rollout is still in progress.

Retries, redelivery, and endpoint health

A 2xx response marks a delivery successful. Timeouts, transport failures, and non-2xx responses can retry up to five attempts with delays of 60, 300, 900, and 900 seconds.

  • Return a 2xx response as soon as the event is authenticated and durably queued.
  • Do not rely on events arriving exactly once or strictly in order.
  • Deduplicate using eventId.
  • Use deliveryId when troubleshooting one attempt.
  • Manual redelivery creates a new delivery ID while preserving the original event ID.
An endpoint receives a warning after 7 consecutive failures and is automatically suspended after 10. Correct the destination and reactivate it from Account > Integrations.