Webhooks

Receive real-time notifications when episodes complete or fail. Webhooks eliminate the need for polling and enable event-driven workflows.

Overview

LogTalk supports two types of webhooks:

Per-Request Webhooks
Pass a webhook_url in your episode request. The callback is sent only for that specific episode. Ideal for CI/CD pipelines.
Persistent Webhooks
Configure webhooks in your dashboard to receive notifications for all episodes. Useful for Slack notifications, internal dashboards, and monitoring.

Per-Request Webhooks

Include a webhook_url in your episode request to receive a callback when that specific episode completes:

curl -X POST https://api.logtalk.io/v1/episodes \
-H "Authorization: Bearer lt_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"content": "## v2.0.0\n- New feature",
"output_format": "video",
"webhook_url": "https://your-app.com/webhooks/logtalk"
}'

Note: Webhook URLs must use HTTPS. HTTP URLs will be rejected.

Event Types

EventDescription
episode.completedConversion finished successfully with media URLs
episode.failedConversion failed with error details
quota.warningApproaching quota limit (80%)
quota.exceededMonthly quota exceeded

Webhook Payload

Webhooks are sent as POST requests with a JSON body:

episode.completed

{
"id": "evt_abc123def456",
"type": "episode.completed",
"created_at": "2026-01-18T14:32:15.000Z",
"data": {
"episode": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"output_format": "video",
"mode": "changelog",
"status": "completed",
"video_url": "https://cdn.logtalk.io/video/...",
"audio_url": "https://cdn.logtalk.io/audio/...",
"duration_seconds": 180,
"version": "2.0.0",
"created_at": "2026-01-18T14:30:00.000Z",
"completed_at": "2026-01-18T14:32:15.000Z"
}
}
}

episode.failed

{
"id": "evt_abc123def456",
"type": "episode.failed",
"created_at": "2026-01-18T14:31:00.000Z",
"data": {
"episode": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"output_format": "video",
"mode": "changelog",
"status": "failed",
"error": {
"code": "GENERATION_FAILED",
"message": "Audio generation failed"
},
"created_at": "2026-01-18T14:30:00.000Z",
"failed_at": "2026-01-18T14:31:00.000Z"
}
}
}

Webhook Headers

Each webhook request includes these headers:

HeaderDescription
Content-TypeAlways application/json
X-LogTalk-EventEvent type (e.g., episode.completed)
X-LogTalk-DeliveryUnique delivery ID for debugging
X-LogTalk-SignatureHMAC-SHA256 signature (format: t=timestamp,v1=signature)
X-LogTalk-TimestampUnix timestamp when webhook was sent

Signature Verification

Always verify webhook signatures to ensure requests are from LogTalk. Signatures use HMAC-SHA256 with the format t=timestamp,v1=signature.

Signature Format
The signed payload is: ${timestamp}.${JSON.stringify(payload)}
This prevents replay attacks by binding the signature to the timestamp.
Timestamp Tolerance
Reject webhooks with timestamps older than 5 minutes to prevent replay attacks.

Verification Examples

const crypto = require('crypto');
function verifyWebhookSignature(payload, signatureHeader, secret) {
// Parse signature header: t=timestamp,v1=signature
const parts = signatureHeader.split(',');
const timestamp = parseInt(parts.find(p => p.startsWith('t=')).split('=')[1]);
const signature = parts.find(p => p.startsWith('v1=')).split('=')[1];
// Check timestamp (reject if > 5 minutes old)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) {
throw new Error('Webhook timestamp too old');
}
// Compute expected signature
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Use timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// Express.js example
app.post('/webhooks/logtalk', express.json(), (req, res) => {
const signature = req.headers['x-logtalk-signature'];
const secret = process.env.LOGTALK_WEBHOOK_SECRET;
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const { type, data } = req.body;
console.log(`Received ${type}: ${data.episode.id}`);
res.status(200).send('OK');
});

Retry Policy

If your endpoint returns a non-2xx status code or times out, LogTalk will retry with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 hours

After 6 failed attempts, the webhook is marked as failing and you'll be notified via email. Webhook timeout is 30 seconds.

Best Practices

Respond Quickly
Return a 2xx response within 30 seconds. Process webhooks asynchronously if needed - acknowledge receipt first, then handle the event.
Handle Duplicates
Webhooks may be delivered more than once. Use the event id to deduplicate events and ensure idempotent processing.
Verify Signatures
Always verify the X-LogTalk-Signature header to ensure webhooks are authentic and haven't been tampered with.
Use Queue Processing
For high-volume integrations, queue webhook events for processing rather than handling them synchronously in the HTTP handler.