Skip to main content

Webhooks

Webhooks let Yadulink push an event to your tool when something happens: a lead is created, a lead's status changes, a job finishes.

They are useful to:

  • trigger an n8n workflow;
  • update a CRM;
  • receive a job result without polling;
  • audit Yadulink events in an external system.

Available events

EventWhen it is sent
lead.createdA lead is created
lead.status_changedA lead's status changes
job.completedAn API job succeeds
job.failedAn API job fails for good
job.canceledAn API job is canceled
testTest event

Create a webhook

curl https://app.yadulink.com/api/v1/webhooks \
-X POST \
-H "Authorization: Bearer yd_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/yadulink/webhook",
"events": ["job.completed", "job.failed", "job.canceled"],
"description": "Production n8n webhook"
}'

Typical response:

{
"success": true,
"data": {
"id": 456,
"url": "https://example.com/yadulink/webhook",
"events": ["job.completed", "job.failed", "job.canceled"],
"active": true,
"secret": "whsec_xxx"
},
"meta": {}
}
warning

Treat the signing secret as a secret. Never expose it in a browser or a log.

The payload you receive

{
"id": "evt_xxx",
"event": "job.completed",
"api_version": "v1",
"schema_version": "2026-05-11",
"created_at": "2026-05-11T10:30:00Z",
"timestamp": "2026-05-11T10:30:00Z",
"delivery": {
"id": 123,
"attempt": 1,
"max_attempts": 5,
"webhook_id": 456
},
"account": {
"user_id": 789,
"email": "user@example.com"
},
"data": {
"job_id": "job_123",
"operation": "send_message",
"status": "completed"
}
}

The identifiers that matter:

FieldUse
idStable identifier of the business event
delivery.idIdentifier of the delivery attempt
delivery.attemptAttempt number
api_versionAPI version that produced the event
schema_versionPayload schema version

When Yadulink retries a delivery, id stays the same. Use it to deduplicate on your side.

Headers sent

Yadulink adds useful headers:

X-Yadulink-Event: job.completed
X-Yadulink-Event-Id: evt_xxx
X-Yadulink-Webhook-Id: 456
X-Yadulink-Delivery-Id: 123
X-Yadulink-Delivery-Attempt: 1
X-Yadulink-Api-Version: v1
X-Yadulink-Schema-Version: 2026-05-11
X-Yadulink-Signature: t=...,v1=...

Verify the signature

Yadulink signs the exact request body with HMAC-SHA256.

Header:

X-Yadulink-Signature: t=timestamp,v1=signature

Signed payload:

timestamp.raw_json_payload

Node.js example:

import crypto from "node:crypto";

function verifyYadulinkSignature({ rawBody, signatureHeader, secret }) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => part.split("="))
);

const timestamp = parts.t;
const signature = parts.v1;

if (!timestamp || !signature) {
return false;
}

const signedPayload = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");

if (signature.length !== expected.length) {
return false;
}

return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}
warning

Verify the signature against the raw body, before JSON parsing. If your framework rewrites the body first, the signature will no longer match.

Retries

Yadulink automatically retries temporary failures:

  • timeout;
  • network error;
  • HTTP 408;
  • HTTP 409;
  • HTTP 425;
  • HTTP 429;
  • HTTP 5xx.

Yadulink does not retry errors it treats as unrecoverable:

  • HTTP 400;
  • HTTP 401;
  • HTTP 403;
  • HTTP 404;
  • HTTP 410.

The retry policy uses exponential backoff, up to five attempts.

Webhook health

Read the state:

curl https://app.yadulink.com/api/v1/webhooks/health \
-H "Authorization: Bearer yd_your_api_key"

The payload lists the endpoints to look at, the failure counters and the latest failed deliveries.

If an endpoint fails too often, Yadulink can disable it automatically to stop a failure loop.

To bring it back:

curl https://app.yadulink.com/api/v1/webhooks/456/activate \
-X POST \
-H "Authorization: Bearer yd_your_api_key"

Test a webhook

curl https://app.yadulink.com/api/v1/webhooks/456/test \
-X POST \
-H "Authorization: Bearer yd_your_api_key"

Yadulink sends a test event.

Replay a delivery

  1. List the deliveries:
curl https://app.yadulink.com/api/v1/webhooks/456/deliveries \
-H "Authorization: Bearer yd_your_api_key"
  1. Replay a specific attempt:
curl https://app.yadulink.com/api/v1/webhooks/456/deliveries/123/replay \
-X POST \
-H "Authorization: Bearer yd_your_api_key"

A replay keeps the same business event id, so your deduplication still holds.

n8n

With the Yadulink community node, prefer Yadulink Trigger. It creates and removes the webhook as the workflow is activated or deactivated, and it verifies the HMAC signature for you.