Verbatik LogoVerbatik
Developer API

Webhooks

Receive completion events and verify their signatures.

Create webhook endpoints in Settings → Developer or through POST /webhooks with webhooks:write scope. Use an HTTPS endpoint your application controls. Save the signing secret when it is revealed.

Subscribe to events

{
  "url": "https://example.com/webhooks/verbatik",
  "description": "Creative job completion",
  "events": ["generation.completed", "generation.failed"]
}

Supported events are generation.completed, generation.failed, agent.review_required, agent.completed, agent.failed, voice.created, persona.ready, and persona.failed.

Verify before processing

The signature is HMAC-SHA256 over ${timestamp}.${rawBody} using the webhook signing secret. Read webhook-timestamp and webhook-signature; the signature has the form v1=<hex>.

Use the unmodified raw request body. Reject malformed timestamps and timestamps more than five minutes from the current time, and compare signatures in constant time.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, timestampHeader, signatureHeader, secret) {
  if (!timestampHeader || !/^\d+$/.test(timestampHeader)) return false;
  const timestamp = Number(timestampHeader);
  const now = Math.floor(Date.now() / 1000);
  if (!Number.isSafeInteger(timestamp) || Math.abs(now - timestamp) > 300) return false;
  const expected = Buffer.from('v1=' + createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`).digest('hex'));
  const received = Buffer.from(signatureHeader || '');
  return expected.length === received.length && timingSafeEqual(expected, received);
}

Do not substitute your API key for the signing secret. Parse and process the payload only after verification. Return a successful response after accepting the event, and make processing safe against duplicate deliveries.

Inspect delivery history

Use GET /webhooks to list endpoints and GET /webhooks/{id}/deliveries to inspect attempts with webhooks:read. PATCH /webhooks/{id} updates an endpoint; DELETE /webhooks/{id} removes it, both requiring webhooks:write.

If a delivery fails, inspect the HTTP response and endpoint logs. Polling the corresponding generation or run remains useful for reconciling your application's state.

On this page