← Back to Blog Webhook integration between API servers New Feature

Real-Time Transcription Webhooks: Async Processing with HMAC Security

May 3, 2026 · 7 min read

Polling is dead weight. Every second your application spends checking "is it done yet?" is a wasted connection, a wasted thread, and a wasted opportunity to do something useful. Webhooks flip the model: VoxParse tells you when it's done.

Today we are launching webhooks and async mode for the VoxParse API. Submit a transcription request, get an immediate 202 response, and receive the full result via a signed HTTP POST to your endpoint when processing completes. Every payload is HMAC-SHA256 signed, automatically retried on failure, and works on both Lite and Pro plans.

Why webhooks matter for production pipelines

If you are processing a handful of files per day, synchronous mode works perfectly. But production call center pipelines, meeting recorders, and compliance systems often need to process hundreds or thousands of files concurrently. Holding open HTTP connections for each one creates problems:

Async mode with webhooks eliminates all of these. Fire and forget, then handle the result when it arrives.

1 Register a webhook

Create a webhook endpoint that VoxParse will POST results to. You get the signing secret exactly once — store it securely:

curl -X POST https://api.voxparse.com/v1/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/voxparse",
    "events": ["transcription.completed", "transcription.failed"],
    "description": "Production webhook"
  }'

Response includes the signing secret (shown only once):

{
  "id": "whk_a1b2c3d4e5f6...",
  "url": "https://your-app.example.com/webhooks/voxparse",
  "secret": "whsec_7f3a9b2c4d5e6f...",
  "secret_last4": "...6f8a",
  "events": ["transcription.completed", "transcription.failed"],
  "active": true
}

Lite accounts get 1 webhook. Pro accounts get 5. Both plans support the same event types and signing.

2 Submit async transcription requests

Add async=true to any transcription request. You get an immediate 202 with a job ID instead of waiting for results:

curl -X POST https://api.voxparse.com/v1/transcribe \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "[email protected]" \
  -F "async=true" \
  -F 'metadata={"ticket_id":"T-1234","agent":"jane"}'

Response (immediate, typically under 200ms):

{
  "job_id": "job_8a7b6c5d4e3f...",
  "status": "processing",
  "message": "Job accepted. Results will be delivered via webhook."
}

The metadata field (up to 4KB of JSON) is echoed back in the webhook payload. Use it to correlate results with your internal records — ticket IDs, agent names, CRM references, anything you need.

3 Verify webhook signatures

Every webhook delivery is signed with HMAC-SHA256. The signature is in the X-VoxParse-Signature header, computed over the timestamp and request body. Always verify before processing.

Node.js verification

import crypto from 'crypto';

function verifyWebhook(req, secret) {
  const timestamp = req.headers['x-voxparse-timestamp'];
  const signature = req.headers['x-voxparse-signature'];
  const body = JSON.stringify(req.body);

  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + body)
    .digest('hex');

  // Constant-time comparison prevents timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}

Python verification

import hmac, hashlib

def verify_webhook(headers, body, secret):
    timestamp = headers.get('X-VoxParse-Timestamp', '')
    signature = headers.get('X-VoxParse-Signature', '')

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{body}".encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

4 Handle the webhook payload

When transcription completes, VoxParse sends a POST with the full result. The payload structure matches what you would get from a synchronous request, wrapped in an event envelope:

{
  "api_version": "2026-05-01",
  "event": "transcription.completed",
  "event_id": "evt_a1b2c3d4...",
  "created_at": "2026-05-03T12:00:00Z",
  "data": {
    "job_id": "job_8a7b6c5d4e3f...",
    "status": "completed",
    "metadata": {"ticket_id": "T-1234", "agent": "jane"},
    "result": {
      "duration_seconds": 2760,
      "ai_analysis": { ... }
    }
  }
}

Headers included with every delivery:

HeaderDescription
X-VoxParse-SignatureHMAC-SHA256 hex signature
X-VoxParse-TimestampISO 8601 timestamp of signing
X-VoxParse-Event-Typetranscription.completed or transcription.failed
X-VoxParse-Event-IdUnique event ID (use for deduplication)
X-VoxParse-Delivery-IdUnique delivery attempt ID
X-VoxParse-AttemptRetry attempt number (1, 2, 3, 4)

Retry policy

If your endpoint returns a non-2xx status code or times out, VoxParse retries automatically with jittered exponential backoff:

AttemptDelayDescription
1ImmediateFirst delivery attempt
2~30 secondsFirst retry
3~5 minutesSecond retry
4~30 minutesFinal retry

If all 4 attempts fail, the event is marked failed_permanent. You can inspect failed deliveries via the delivery log API.

Webhooks that accumulate 10+ consecutive failures over 30 minutes are automatically disabled to prevent wasted resources. You can re-enable them via the API at any time.

Complete async pipeline example

Here is a complete Node.js Express handler that receives webhook deliveries, verifies signatures, and processes results:

import express from 'express';
import crypto from 'crypto';

const app = express();
const WEBHOOK_SECRET = process.env.VOXPARSE_WEBHOOK_SECRET;

app.post('/webhooks/voxparse', express.json(), (req, res) => {
  // Step 1: Verify signature
  const timestamp = req.headers['x-voxparse-timestamp'];
  const signature = req.headers['x-voxparse-signature'];
  const body = JSON.stringify(req.body);

  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(timestamp + '.' + body)
    .digest('hex');

  if (!crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Step 2: Process event
  const { event, data } = req.body;

  if (event === 'transcription.completed') {
    const { job_id, metadata, result } = data;
    console.log(`Job ${job_id} completed for ticket ${metadata?.ticket_id}`);
    // Store result, update your database, notify users, etc.
  }

  if (event === 'transcription.failed') {
    console.error(`Job ${data.job_id} failed: ${data.error}`);
    // Alert your team, retry with different settings, etc.
  }

  // Step 3: Acknowledge receipt
  res.status(200).json({ received: true });
});

app.listen(3000);

Start building with webhooks

Available on both Lite and Pro plans. No extra cost. Get your API key and register your first webhook in under a minute.

Get your API key →

Bottom line

Webhooks turn VoxParse from a request-response API into an event-driven platform. Submit files whenever they arrive, let VoxParse process them in the background, and handle the results when they are ready. Every delivery is signed, retried automatically, and carries your custom metadata for easy correlation.

Read the full Webhooks API documentation for endpoint management, secret rotation, and delivery log inspection. Or check the dashboard code builder to generate async request snippets in cURL, Python, and Node.js.