Receive your first webhook
Webhooks push events to your server as they happen — an enrollment completed, a course published, a quiz passed. Each delivery is a signed POST; you verify the signature, then act on it. You’ll need an API key with the webhooks:manage permission and the variables from the overview.
Register an endpoint
Point Talent at a URL on your server and name the events you care about (or ["*"] for all of them). HTTPS is required — plain http is allowed only for localhost during development.
curl -X POST "$TALENT_API/webhook-endpoints" \
-H "Authorization: Bearer $TALENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.example/talent/webhooks",
"event_types": ["enrollment.completed"]
}'The response carries the signing secret — this is the one and only time it’s shown. Store it now; you sign with it below.
{
"id": "e1d2c3b4-a5f6-4788-9a0b-1c2d3e4f5a6b",
"url": "https://your-server.example/talent/webhooks",
"event_types": ["enrollment.completed"],
"status": "active",
"secret": "whsec_Zm9vYmFyYmF6cXV4Y29ycmVjdGhvcnNl",
"…": "…"
}Know what a delivery looks like
Every delivery is a POST with three webhook-* headers and a JSON body. The body is the same envelope for every event: an id, the type, an api_version, and an event-specific data object.
webhook-id: e7a1… (also the payload id)
webhook-timestamp: 1753376696 (unix seconds)
webhook-signature: v1,Base64Signature=
content-type: application/json
{
"id": "e7a1b2c3-d4e5-4f60-8a91-b2c3d4e5f607",
"type": "enrollment.completed",
"created_at": "2026-07-24T17:11:36.204Z",
"api_version": "2026-07",
"data": {
"enrollment_id": "…",
"course_id": "…",
"user_id": "…"
}
}Verify the signature
The signature is v1, followed by the base64 HMAC-SHA256 of {id}.{timestamp}.{body}, keyed by your secret. Compute the same value and compare in constant time. Two rules matter: verify over the raw request body (parsing then re-serializing will change the bytes and break the signature), and reject timestamps older than five minutes to stop replays.
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_S = 300; // reject deliveries older than 5 minutes
/**
* Verify a Talent webhook. Pass the raw request body (a string or Buffer),
* the webhook-* headers, and the endpoint's signing secret.
*/
export function verify(secret: string, headers: Headers, rawBody: string): boolean {
const id = headers.get("webhook-id");
const timestamp = Number(headers.get("webhook-timestamp"));
const signatureHeader = headers.get("webhook-signature");
if (!id || !signatureHeader || !Number.isFinite(timestamp)) return false;
// Replay protection.
if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_S) return false;
// The HMAC key is the base64-decoded part of the secret after "whsec_".
const key = Buffer.from(secret.slice("whsec_".length), "base64");
const expected =
"v1," +
createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`).digest("base64");
// The header may carry several space-separated signatures (during secret
// rotation); any one matching in constant time is a pass.
const expectedBuf = Buffer.from(expected);
return signatureHeader.split(" ").some((candidate) => {
const buf = Buffer.from(candidate);
return buf.length === expectedBuf.length && timingSafeEqual(buf, expectedBuf);
});
}Wire it up against the raw body. In Express, that’s express.raw({ type: "application/json" }); in a Next.js route handler, await req.text() before JSON.parse.
Respond, and know the retry rules
Return any 2xx and you’re done. Anything else — or no response within ten seconds — is a failure, and Talent retries on a backoff schedule for up to eight attempts across roughly 24 hours before the delivery is marked exhausted. An endpoint that fails twenty times in a row is disabled automatically, and you get an webhook_endpoint.disabled event.
Send yourself a test delivery any time, and inspect every attempt — request, response, and signature — in the dashboard delivery log.
curl -X POST "$TALENT_API/webhook-endpoints/$ENDPOINT_ID/test" \
-H "Authorization: Bearer $TALENT_KEY"Next
Browse the full event catalog and every endpoint’s payload in the API reference, or manage your endpoints and read delivery logs under Developer → Webhooks.