Skip to main content

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

EventDescription
voicenote.transcribedVoice note successfully transcribed
voicenote.failedVoice note processing failed

Webhook Payload

voicenote.transcribed

{
"event": "voicenote.transcribed",
"timestamp": 1703001600,
"voicenote": {
"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"
}
}
}

voicenote.failed

{
"event": "voicenote.failed",
"timestamp": 1703001600,
"voicenote": {
"id": "rec_1234567890",
"identity": "user_12345",
"status": "failed",
"created_at": 1703001500
},
"error": {
"code": "TRANSCRIPTION_FAILED",
"message": "Audio quality too low for transcription"
}
}

Setup

1. Create Endpoint

Create an endpoint in your application to receive webhooks:

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 == 'voicenote.transcribed':
handle_completed(data['voicenote'])
elif event == 'voicenote.failed':
handle_failed(data['voicenote'], data['error'])

return jsonify({'status': 'received'}), 200

def handle_completed(voicenote):
print(f"Transcription: {voicenote['transcription']['text']}")
# Save to database, notify user, etc.

def handle_failed(voicenote, error):
print(f"Failed: {error['message']}")
# Log error, notify user, etc.

2. Register Webhook

Register your endpoint with VocaFuse:

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=['voicenote.transcribed', 'voicenote.failed'],
secret='your_webhook_secret_here'
)

print(f"Webhook ID: {webhook['data']['id']}")

Security

Signature Verification

danger

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:

from vocafuse import RequestValidator

validator = RequestValidator('your_webhook_secret')
is_valid = validator.validate(payload, signature)

Best Practices

  1. Use HTTPS - Only accept webhooks over HTTPS
  2. Verify signatures - Always validate the X-VocaFuse-Signature header
  3. Return quickly - Respond with 200 within 5 seconds
  4. Process async - Queue long-running tasks for background processing
  5. Handle retries - Implement idempotency using the voice note ID
  6. 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:

# Or use the API
client.webhooks('webhook_123').test()

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:

AttemptDelay
10s
230s
35min
430min
52h

After 5 failed attempts, the webhook will be marked as failed and no more retries will be attempted.

Error Handling

@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:

# 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:

# 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:

# Use voice note ID for idempotency
processed_ids = set()

def process_event(data):
voicenote_id = data['voicenote']['id']

if voicenote_id in processed_ids:
return # Already processed

# Process event...
processed_ids.add(voicenote_id)