Voice Notes Webhooks Guide
Receive voice note transcriptions automatically in your backend. VocaFuse sends an HTTP POST request with the transcription text when processing completes. This guide covers webhook setup, HMAC signature verification, retry logic, and Python implementation examples.
Overview
VocaFuse uses webhooks to notify your application when voice notes are transcribed or when errors occur. Webhooks are HTTP POST requests sent to your configured endpoint.
Event Types
Recording Events
| Event | Description |
|---|---|
recording.transcribed | Voice note successfully transcribed |
recording.failed | Voice note processing failed |
Account Events
| Event | Description |
|---|---|
account.insufficient_balance | Account balance too low to process requests |
account.low_balance_warning | Account balance below warning threshold (Coming Soon) |
account.credits_added | Credits added to account (Coming Soon) |
Webhook Payload
recording.transcribed
{
"event": "recording.transcribed",
"timestamp": 1703001600,
"recording": {
"id": "rec_1234567890",
"identity": "user_12345",
"status": "completed",
"duration": 45.2,
"created_at": 1703001500,
"completed_at": 1703001600,
"transcription": {
"text": "This is the transcribed text",
"confidence": 0.985,
"language": "en"
}
}
}
recording.failed
{
"event": "recording.failed",
"timestamp": 1703001600,
"recording": {
"id": "rec_1234567890",
"identity": "user_12345",
"status": "failed",
"created_at": 1703001500
},
"error": {
"code": "TRANSCRIPTION_FAILED",
"message": "Audio quality too low for transcription"
}
}
account.insufficient_balance
This webhook event is planned for future release. Webhook delivery is not yet implemented.
{
"event": "account.insufficient_balance",
"timestamp": 1703001600,
"account": {
"tenant_id": "tenant_abc123",
"available_balance": 0.50,
"free_tier_remaining": 0.00,
"paid_balance": 0.50,
"currency": "USD"
},
"trigger": {
"operation": "recording_upload",
"estimated_cost": 0.013,
"shortfall": 0.00,
"breakdown": {
"api_call": 0.001,
"transcription_estimate": 0.012
}
},
"action_required": "Add credits to resume service"
}
Setup
1. Create Endpoint
Create an endpoint in your application to receive webhooks:
- Python
- Node.js
from flask import Flask, request, jsonify
from vocafuse import RequestValidator
import os
app = Flask(__name__)
validator = RequestValidator(os.environ['VOCAFUSE_WEBHOOK_SECRET'])
@app.route('/api/webhooks/vocafuse', methods=['POST'])
def handle_webhook():
# Get raw payload and signature
payload = request.get_data(as_text=True)
signature = request.headers.get('X-VocaFuse-Signature')
# Verify signature
if not validator.validate(payload, signature):
return jsonify({'error': 'Invalid signature'}), 401
# Parse event
data = request.get_json()
event = data['event']
if event == 'recording.transcribed':
handle_completed(data['recording'])
elif event == 'recording.failed':
handle_failed(data['recording'], data['error'])
return jsonify({'status': 'received'}), 200
def handle_completed(recording):
print(f"Transcription: {recording['transcription']['text']}")
# Save to database, notify user, etc.
def handle_failed(recording, error):
print(f"Failed: {error['message']}")
# Log error, notify user, etc.
const express = require('express');
const { RequestValidator } = require('vocafuse-node');
const app = express();
app.use(express.json());
const validator = new RequestValidator(process.env.VOCAFUSE_WEBHOOK_SECRET);
app.post('/api/webhooks/vocafuse', (req, res) => {
// Get payload and signature
const payload = JSON.stringify(req.body);
const signature = req.headers['x-vocafuse-signature'];
// Verify signature
if (!validator.validate(payload, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Handle event
const { event, recording } = req.body;
if (event === 'recording.transcribed') {
handleCompleted(recording);
} else if (event === 'recording.failed') {
handleFailed(recording, req.body.error);
}
res.json({ status: 'received' });
});
function handleCompleted(recording) {
console.log(`Transcription: ${recording.transcription.text}`);
// Save to database, notify user, etc.
}
function handleFailed(recording, error) {
console.log(`Failed: ${error.message}`);
// Log error, notify user, etc.
}
2. Register Webhook
Register your endpoint with VocaFuse:
- Python
- Node.js
- cURL
from vocafuse import VocaFuse
import os
client = VocaFuse(
api_key=os.environ['VOCAFUSE_API_KEY'],
api_secret=os.environ['VOCAFUSE_API_SECRET']
)
webhook = client.webhooks.create(
url='https://your-domain.com/api/webhooks/vocafuse',
events=['recording.transcribed', 'recording.failed'],
secret='your_webhook_secret_here'
)
print(f"Webhook ID: {webhook['data']['id']}")
const { Client } = require('vocafuse-node');
const client = new Client({
apiKey: process.env.VOCAFUSE_API_KEY,
apiSecret: process.env.VOCAFUSE_API_SECRET
});
const webhook = await client.webhooks.create({
url: 'https://your-domain.com/api/webhooks/vocafuse',
events: ['recording.transcribed', 'recording.failed'],
secret: 'your_webhook_secret_here'
});
console.log(`Webhook ID: ${webhook.data.id}`);
curl -X POST https://api.vocafuse.com/v1/webhooks \
-H "X-VocaFuse-API-Key: sk_live_..." \
-H "X-VocaFuse-API-Secret: ..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-domain.com/api/webhooks/vocafuse",
"events": ["recording.transcribed", "recording.failed"],
"secret": "your_webhook_secret_here"
}'
Security
Signature Verification
Always verify webhook signatures before processing events. This prevents attackers from sending fake webhooks to your endpoint.
VocaFuse signs all webhooks with HMAC-SHA256. Use the SDK's RequestValidator to verify signatures:
- Python
- Node.js
from vocafuse import RequestValidator
validator = RequestValidator('your_webhook_secret')
is_valid = validator.validate(payload, signature)
const { RequestValidator } = require('vocafuse-node');
const validator = new RequestValidator('your_webhook_secret');
const isValid = validator.validate(payload, signature);
Best Practices
- Use HTTPS - Only accept webhooks over HTTPS
- Verify signatures - Always validate the
X-VocaFuse-Signatureheader - Return quickly - Respond with 200 within 5 seconds
- Process async - Queue long-running tasks for background processing
- Handle retries - Implement idempotency using the voice note ID
- Log events - Keep audit logs of all webhook deliveries
Testing
Local Development
Use ngrok to test webhooks locally:
# Start your server
python app.py
# In another terminal
ngrok http 5000
# Use the ngrok URL for webhook registration
# Example: https://abc123.ngrok.io/api/webhooks/vocafuse
Manual Testing
Send a test webhook from the VocaFuse dashboard:
- Python
- Node.js
# Or use the API
client.webhooks('webhook_123').test()
const { Client } = require('vocafuse-node');
const client = new Client({
apiKey: process.env.VOCAFUSE_API_KEY,
apiSecret: process.env.VOCAFUSE_API_SECRET,
});
async function testWebhook() {
await client.webhooks('webhook_123').test();
}
SDK helpers like client.webhooks('webhook_123').test() are planned but not yet available in the current vocafuse-node release. For now, trigger test webhooks from the dashboard or by calling the HTTP endpoint directly.
Monitoring
Webhook Logs
View webhook delivery logs in the dashboard:
- Delivery attempts
- Response status codes
- Response times
- Retry attempts
Failed Deliveries
VocaFuse will retry failed webhooks with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | 0s |
| 2 | 30s |
| 3 | 5min |
| 4 | 30min |
| 5 | 2h |
After 5 failed attempts, the webhook will be marked as failed and no more retries will be attempted.
Error Handling
- Python
@app.route('/api/webhooks/vocafuse', methods=['POST'])
def handle_webhook():
try:
# Verify signature
if not verify_signature():
return jsonify({'error': 'Invalid signature'}), 401
data = request.get_json()
# Process event
process_event(data)
# Always return 200
return jsonify({'status': 'received'}), 200
except Exception as e:
# Log error but still return 200 to prevent retries
logger.error(f'Webhook processing error: {e}')
return jsonify({'status': 'error', 'message': str(e)}), 200
Common Issues
Issue: Signature validation fails
Cause: Incorrect secret or payload modification
Solution:
- Python
# Ensure you're using the raw payload, not parsed JSON
payload = request.get_data(as_text=True) # ✅ Correct
payload = json.dumps(request.get_json()) # ❌ Wrong
Issue: Timeouts
Cause: Slow processing in webhook handler
Solution:
- Python
# Queue for background processing
@app.route('/api/webhooks/vocafuse', methods=['POST'])
def handle_webhook():
data = request.get_json()
# Queue the task
queue.enqueue(process_webhook, data)
# Return immediately
return jsonify({'status': 'received'}), 200
Issue: Duplicate events
Cause: Webhook retries
Solution:
- Python
# Use voice note ID for idempotency
processed_ids = set()
def process_event(data):
recording_id = data['recording']['id']
if recording_id in processed_ids:
return # Already processed
# Process event...
processed_ids.add(recording_id)