Webhooks

On Business, receive every change as a signed JSON request at your own address. The payload, the signature and retries.

Updated October 17, 2026

A Custom Webhook destination sends every change its rules let through as a POST with a JSON body to your address. Custom webhooks are part of Business. They are sent from our proxy network, so your endpoint never sees our servers' address.

The request

http
POST /your/endpoint
Content-Type: application/json
X-LabelSentry-Event: change.confirmed
X-LabelSentry-Signature: t=1792050000,v1=5d41402abc4b2a76b9719d911017c592...
json
{
  "event": "change.confirmed",
  "change_id": "c0a8...",
  "artist_id": "5f1e...",
  "artist": "Kova Lane",
  "artist_spotify_id": "2aBcDeFgHiJkLmNoPqRsTu",
  "entity_type": "release",
  "entity_id": "1hX0...",
  "entity_name": "Aftercare",
  "severity": "critical",
  "change_type": "release_removed",
  "summary": "\"Aftercare\" is no longer available",
  "detected_at": 1792049520
}

Check the signature

When you set a secret on the destination, every request is signed. The signature is an HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with your secret.

js
import crypto from "node:crypto";

function verify(secret, header, rawBody) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Reject requests older than five minutes, so a captured request cannot be sent again later.

Answer and retries

Answer with any 2xx status within 10 seconds. Anything else counts as failed and is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. After 20 failures in a row the destination is switched off.

Read next

Still stuck? Write to [email protected].