New Feature
Real-Time Transcription Webhooks: Async Processing with HMAC Security
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:
- Timeouts: Load balancers, proxies, and HTTP clients all have their own timeout limits
- Resource exhaustion: Each open connection consumes memory and a thread/goroutine
- Retry complexity: If a connection drops, you need to re-submit and hope for idempotency
- Scaling limits: Connection pools have finite capacity
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:
| Header | Description |
|---|---|
X-VoxParse-Signature | HMAC-SHA256 hex signature |
X-VoxParse-Timestamp | ISO 8601 timestamp of signing |
X-VoxParse-Event-Type | transcription.completed or transcription.failed |
X-VoxParse-Event-Id | Unique event ID (use for deduplication) |
X-VoxParse-Delivery-Id | Unique delivery attempt ID |
X-VoxParse-Attempt | Retry 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:
| Attempt | Delay | Description |
|---|---|---|
| 1 | Immediate | First delivery attempt |
| 2 | ~30 seconds | First retry |
| 3 | ~5 minutes | Second retry |
| 4 | ~30 minutes | Final 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.