Webhooks

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

Overview

LogTalk supports two types of webhooks:

The same delivery mechanism is used for both changelog conversions (LogTalk) and document conversions (DocTalk) — the event type in the payload (conversion.* vs doctalk.*) tells you which product produced the event.

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

Per-Request Webhooks

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

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

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

The same webhook_url parameter works for DocTalk conversions — pass "source_type": "document" instead of "changelog", and you'll receive doctalk.completed / doctalk.failed events with the DocTalk payload shape shown below.

Event Types

EventDescription
conversion.completedLogTalk changelog conversion finished successfully with audio (or video) ready
conversion.failedLogTalk changelog conversion failed with error details
doctalk.completedDocTalk document conversion finished successfully with audio ready
doctalk.failedDocTalk document conversion 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. Every payload shares the same envelope — id, type, created_at, api_version, and data — but the shape of data depends on which product produced the conversion.

LogTalk (changelog conversions)

conversion.completed and conversion.failed are sent for changelog conversions created with "source_type": "changelog".

conversion.completed

{
"id": "evt_abc123def456",
"type": "conversion.completed",
"created_at": "2026-01-18T14:32:15.000Z",
"api_version": "2026-01-18",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"mode": "changelog",
"product_name": "Acme Widget",
"version": "2.0.0",
"tone": "professional",
"duration": "short",
"output_format": "audio",
"source": "api",
"audio_url": "https://logtalk.io/api/v1/conversions/550e8400-e29b-41d4-a716-446655440000/audio",
"video_url": null,
"audio_duration_seconds": 180,
"script_text": "Here's what's new in Acme Widget 2.0.0...",
"listen_url": "https://logtalk.io/listen/550e8400-e29b-41d4-a716-446655440000",
"embed_url": "https://logtalk.io/embed/550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-01-18T14:30:00.000Z",
"completed_at": "2026-01-18T14:32:15.000Z"
}
}

listen_url and embed_url are null unless the conversion is public. video_url is null for audio-only conversions.

conversion.failed

{
"id": "evt_abc123def456",
"type": "conversion.failed",
"created_at": "2026-01-18T14:31:00.000Z",
"api_version": "2026-01-18",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"mode": "changelog",
"product_name": "Acme Widget",
"version": "2.0.0",
"tone": "professional",
"duration": "short",
"output_format": "audio",
"source": "api",
"created_at": "2026-01-18T14:30:00.000Z",
"failed_at": "2026-01-18T14:31:00.000Z",
"error": {
"code": "GENERATION_FAILED",
"message": "Audio generation failed"
}
}
}

DocTalk (document conversions)

doctalk.completed and doctalk.failed are sent for document conversions created with "source_type": "document". The datashape is different from LogTalk's — it has no mode or version field, and instead carries source_type, verbosity, and speakers.

doctalk.completed

{
"id": "evt_def789ghi012",
"type": "doctalk.completed",
"created_at": "2026-01-18T14:32:15.000Z",
"api_version": "2026-01-18",
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "completed",
"source_type": "document",
"content_description": "Q3 product roadmap.pdf",
"tone": "casual",
"verbosity": "normal",
"speakers": 2,
"source": "api",
"audio_url": "https://doctalk.io/api/v1/conversions/7c9e6679-7425-40de-944b-e07fc1f90ae7/audio",
"audio_duration_seconds": 240,
"script_text": "Speaker 1: Let's dig into the Q3 roadmap...",
"listen_url": "https://doctalk.io/listen/7c9e6679-7425-40de-944b-e07fc1f90ae7",
"embed_url": "https://doctalk.io/embed/7c9e6679-7425-40de-944b-e07fc1f90ae7",
"created_at": "2026-01-18T14:29:00.000Z",
"completed_at": "2026-01-18T14:32:15.000Z"
}
}

speakers is 1 or 2 depending on whether the conversion was generated as a monologue or two-speaker dialogue. source is always "api" — DocTalk conversions are API-only.

doctalk.failed

{
"id": "evt_def789ghi012",
"type": "doctalk.failed",
"created_at": "2026-01-18T14:31:00.000Z",
"api_version": "2026-01-18",
"data": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "failed",
"source_type": "document",
"content_description": "Q3 product roadmap.pdf",
"tone": "casual",
"verbosity": "normal",
"speakers": 2,
"source": "api",
"audio_url": null,
"audio_duration_seconds": null,
"script_text": null,
"listen_url": null,
"embed_url": null,
"created_at": "2026-01-18T14:29:00.000Z",
"failed_at": "2026-01-18T14:31:00.000Z",
"error": {
"code": "GENERATION_FAILED",
"message": "Could not extract readable text from the uploaded document"
}
}
}

Webhook Headers

Each webhook request includes these headers:

HeaderDescription
Content-TypeAlways application/json
X-LogTalk-EventEvent type (e.g., conversion.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.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-Signatureheader 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.