Webhooks
Docs/Developers

Webhooks

Webhooks send an HTTP POST to your server when something happens in your project, signed so you can check it came from us. Pro+

Add an endpoint

  1. Open Webhooks under Developer and click + New webhook.
  2. Enter your HTTPS endpoint URL and pick the events.
  3. Copy the signing secret (whsec_…).
    It's shown once; you need it to verify requests.
app.cloakingurl.comWebhooks › New webhook
https://hooks.yourbrand.com/cloakingurl
✓link.created✓link.updated✓link.deleteddomain.connectedpayment.completed
Create

You can only subscribe to events about things your role can see (for example, billing events need billing access).

Events

Payload and headers

POST to your endpoint
POST /cloakingurl HTTP/1.1
Content-Type: application/json
User-Agent: CloakingURL-Webhooks/1
X-CloakingURL-Event: link.created
X-CloakingURL-Delivery: 5b1e0c7a-…
X-CloakingURL-Signature: sha256=3f7a9c…

{
  "id": "5b1e0c7a-…",
  "event": "link.created",
  "createdAt": "2026-10-01T08:30:12.000Z",
  "data": { "id": "…", "slug": "black-friday", "domain": "go.yourbrand.com" }
}

The id (also in X-CloakingURL-Delivery) is the same on every retry of a delivery; use it to ignore duplicates.

Verifying the signature

The signature is an HMAC-SHA256 of the raw request body with your signing secret, hex-encoded. Compute it yourself and compare in constant time. Parse the JSON only after it matches.

Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/cloakingurl", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.CLOAKINGURL_WEBHOOK_SECRET)
    .update(req.body)                       // the raw Buffer, not re-serialized JSON
    .digest("hex");
  const got = req.get("X-CloakingURL-Signature") ?? "";
  const ok = got.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
  if (!ok) return res.status(401).end();

  const { event, data } = JSON.parse(req.body.toString("utf8"));
  // … handle the event
  res.sendStatus(200);
});
PHP
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, getenv('CLOAKINGURL_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_CLOAKINGURL_SIGNATURE'] ?? '')) {
  http_response_code(401); exit;
}
$payload = json_decode($body, true);

Retries and the delivery log

  • Reply with any 2xx within 10 seconds. Do slow work after replying.
  • Anything else (an error code, a timeout) is retried up to 5 attempts in total, with growing gaps starting at 2 seconds.
  • Each webhook has a delivery log showing the last 100 deliveries: event, status, response code, attempts and time.
TipFor local testing, point a webhook at a request inspector (such as webhook.site) or a tunnel to your machine, then create a link to fire link.created.