Set up webhooks
Receive real-time notifications for payout lifecycle events.
Register a webhook to stay informed about payout status changes instead of polling.
Manage subscriptions
POST /webhook— subscribe a URL to events.GET /webhook— list your subscriptions.DELETE /webhook/{id}— remove one.
Webhooks live in the environment they were created in: a webhook registered via the sandbox API only fires for sandbox events, and one registered via the production API only fires for production events.
Event payload
When events occur, webhooks receive HTTP POST requests with a JSON payload that matches the payout schema returned by GET /payout/{id}.
Optional/nullable fields are omitted from the JSON payload entirely when they are not present (they are not sent as null). Check for the absence of a property rather than checking if it equals null.
Event triggers
Webhooks fire for the payout lifecycle events:
- Payout creation (status
created) - Payout approval (status
approved) - Payout request (status
requested) - Payout completion (status
completed) - Payout deletion (status
deleted) - Payout expiration (status
expired)
Delivery behavior
Method: POST, Content-Type: application/json.
Request headers:
X-Talentir-Signature: HMAC-SHA256 signature for payload verificationX-Talentir-Timestamp: Unix timestamp (seconds) when the webhook was sent
Retry behavior:
- Server errors (5xx): retried up to 10 times with exponential backoff (10s, 20s, 40s, 80s, 160s, 320s, 640s, 1280s, 2560s, 5120s). Total retry window is approximately 2.8 hours.
- Rate limiting (429): retried after a 60 second delay.
- Gone (410): the webhook subscription is automatically deleted. No retry.
- Other client errors (4xx): not retried. These indicate a permanent failure (e.g. invalid endpoint configuration).
- Success (2xx): delivery confirmed.
Verify signatures
Every webhook request includes a cryptographic signature. Always verify webhook signatures to ensure requests are genuinely from Talentir.
- Extract the signature and timestamp from the request headers.
- Construct the signed payload:
{timestamp}.{raw_request_body}. - Compute HMAC-SHA256 using your webhook's signing secret.
- Compare the computed signature with
X-Talentir-Signatureusing timing-safe comparison. - Optionally, reject requests with timestamps older than 5 minutes to prevent replay attacks.
import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
timestamp: string,
signingSecret: string
): boolean {
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = createHmac('sha256', signingSecret)
.update(signedPayload)
.digest('hex');
// Timing-safe comparison to prevent timing attacks
const sigBuffer = Buffer.from(signature);
const expectedBuffer = Buffer.from(expectedSignature);
if (sigBuffer.length !== expectedBuffer.length) return false;
return timingSafeEqual(sigBuffer, expectedBuffer);
}
// In your webhook handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-talentir-signature'];
const timestamp = req.headers['x-talentir-timestamp'];
const payload = JSON.stringify(req.body);
// Reject requests older than 5 minutes
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return res.status(400).send('Timestamp too old');
}
if (!verifyWebhookSignature(payload, signature, timestamp, YOUR_SIGNING_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook...
});