coldos

Reference

Webhooks

Push events to your own endpoint. Cold OS signs every payload with HMAC-SHA256 so you can verify it came from us.

Overview

Configure a destination URL from Settings → Webhooks. Pick the events you care about; Cold OS POSTs each event as a single JSON body to your endpoint. Delivery is at-least-once, retried with exponential backoff on any non-2xx response.

Event catalog

  • contact.createdA new contact was inserted.
  • contact.updatedA field on a contact changed.
  • contact.stage_changedThe contact's `internal_stage` was set.
  • email.sentAn outbound email left the building (Gmail accepted).
  • email.openedFirst open per (contact, day).
  • email.clickedFirst click per (contact, day, link).
  • email.repliedAn inbound reply was attributed to a sent email.
  • email.bouncedHard bounce detected via Gmail event stream.
  • email.unsubscribedRFC 8058 unsubscribe header or in-body opt-out.
  • call.completedA call's status webhook arrived from Twilio.
  • meeting.scheduledA Cold OS calendar event was created.
  • meeting.completedA scheduled meeting elapsed.
  • task.createdA task was created (manual or flow-spawned).
  • task.completedA task was marked complete.
  • flow.enrolledA contact was enrolled in a flow.
  • flow.exitedAn enrollment terminated (any reason). Detail in payload.
  • flow.goal_reachedAn enrollment reached a Goal node.

Delivery

Headers on every delivery:

  • Content-Type: application/json
  • X-ColdOS-Event — the event name (e.g. contact.created).
  • X-ColdOS-Delivery — unique delivery id (use for idempotency).
  • X-ColdOS-Timestamp — Unix seconds when we signed the payload.
  • X-ColdOS-Signature — v1=<hex> HMAC over {timestamp}.{body}.
Example payload
{
  "id": "evt_01HZSDD4E5F6G7H8J9K0L1M2N3",
  "event": "contact.stage_changed",
  "occurred_at": "2026-05-01T14:30:00.000+05:30",
  "data": {
    "contact_id": "c2a8a93a-2f1c-4c5b-8f3d-9c1e3a8b5d12",
    "from_stage": "engaged",
    "to_stage": "meeting_booked",
    "actor_user_id": "b1f2c3d4-5678-90ab-cdef-1234567890ab"
  }
}

Verifying signatures

Algorithm

Compute HMAC_SHA256(signing_secret, "{X-ColdOS-Timestamp}.{raw_body}") and compare hex-encoded with the v1= portion of X-ColdOS-Signature. Use a constant-time comparison.

import crypto from "node:crypto";

export function verify(req, signingSecret) {
  const ts = req.headers["x-coldos-timestamp"];
  const sig = req.headers["x-coldos-signature"];
  if (!ts || !sig) return false;

  const ageSec = Math.abs(Date.now() / 1000 - Number(ts));
  if (ageSec > 300) return false;

  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${ts}.${req.rawBody}`)
    .digest("hex");

  const presented = sig.replace(/^v1=/, "");
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(presented, "hex"),
  );
}

Retries

Cold OS retries any non-2xx response on the schedule below. After the final attempt the delivery row stays in the audit log — retry it manually from the dashboard at any time.

  • 1m → 5m → 30m → 2h → 8h → 1d (six attempts total)

Related

Was this page helpful?

Your vote stays in this browser. We'll use it to prioritise rewrites.