Skip to content
Shareglow

Webhooks

Webhooks

Shareglow POSTs a signed JSON body to your URL when something happens in your workspace. Works with the webhook trigger nodes of Zapier, Make and n8n. Add endpoints and copy the signing secret under Developer > Webhooks.

01

Events

link.created

A short link is created.

data
{ "id": "...", "slug": "...", "shortUrl": "...", "destination": "...", "imageUrl": "..." }

image.rendered

An OG image is rendered.

data
{ "url": "...", "templateId": "..." }

monitor.alert

A monitor raises an alert.

data
{ "monitorId": "...", "monitorName": "...", "events": [{ "event": "...", "severity": "...", "url": "...", "message": "..." }] }

The dashboard's "Send test" button sends type webhook.test, which you cannot subscribe to.

02

Request

POST body
{
  "id": "<delivery id, uuid>",
  "type": "link.created",
  "createdAt": "<ISO 8601>",
  "workspaceId": "...",
  "data": { }
}
Headers
content-type: application/json
user-agent: Shareglow-Webhooks/1.0
x-shareglow-event: link.created
x-shareglow-delivery: <delivery id>
x-shareglow-signature: t=<unix seconds>,v1=<hex>

03

Verify the signature

v1 is the hex HMAC-SHA256 of <t>.<raw body> keyed with your workspace secret (it starts with whsec_). Use the raw body, not re-serialized JSON, and reject timestamps more than 5 minutes old.

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

// header = request.headers["x-shareglow-signature"], body = the raw request body string
export function verify(secret: string, body: string, header: string, toleranceSec = 300): boolean {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!Number.isFinite(t) || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = Buffer.from(createHmac("sha256", secret).update(`${t}.${body}`).digest("hex"), "hex");
  const given = Buffer.from(parts.v1, "hex");
  return expected.length === given.length && timingSafeEqual(expected, given);
}

04

Delivery, retries and limits

  • Answer with a 2xx status. Redirects are never followed, so a 3xx counts as a failure.
  • Timeouts, 5xx and 429 are retried three times after 1, 4 and 16 seconds. Other failures are not retried.
  • Your URL must resolve to a public address. Responses are read up to 64 KB with a 5 second timeout.
  • Every attempt is logged in the dashboard so a failed delivery can be debugged.
  • Endpoints per workspace and deliveries per UTC day: Free 1 and 100 · Starter 3 and 5,000 · Growth 10 and 25,000 · Scale 25 and 150,000. Over the daily cap a delivery is logged as failed, not sent.