Dokumentation
Webhook API
SoundMadeSeen Webhooks
Webhooks allow your application to receive real-time notifications when events occur in your SoundMadeSeen account. Instead of repeatedly polling our API for updates, webhooks push data to your application as soon as something happens.
Getting Started
1. Create a Webhook Endpoint
First, create an endpoint in your application to receive webhook data:
Codebeispiele
@csrf_exempt
@require_http_methods(["POST"])
def soundmadeseen_webhook(request):
# Verify the webhook signature (recommended)
signature = request.headers.get('X-Webhook-Signature')
secret = 'your_webhook_secret_here' # From webhook creation response
if signature and verify_signature(request.body, signature, secret):
# Process the webhook
data = json.loads(request.body)
if data['event'] == 'video.rendered':
video_id = data['data']['video_id']
download_url = data['data']['download_url']
# Handle completed video...
elif data['event'] == 'audio.processed':
audio_id = data['data']['audio_id']
# Handle processed audio...
return HttpResponse(status=200)
else:
return HttpResponse(status=401) # Unauthorized
def verify_signature(body, signature, secret):
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware to capture raw body for signature verification
app.use('/webhook/soundmadeseen', express.raw({ type: 'application/json' }));
// Webhook endpoint
app.post('/webhook/soundmadeseen', (req, res) => {
try {
// Verify the webhook signature (recommended)
const signature = req.headers['x-webhook-signature'];
const secret = 'your_webhook_secret_here'; // From webhook creation response
if (signature && verifySignature(req.body, signature, secret)) {
// Process the webhook
const data = JSON.parse(req.body.toString());
if (data.event === 'video.rendered') {
const videoId = data.data.video_id;
const downloadUrl = data.data.download_url;
// Handle completed video...
console.log(`Video rendered: ${videoId}, URL: ${downloadUrl}`);
} else if (data.event === 'audio.processed') {
const audioId = data.data.audio_id;
// Handle processed audio...
console.log(`Audio processed: ${audioId}`);
}
res.status(200).send('OK');
} else {
res.status(401).send('Unauthorized');
}
} catch (error) {
console.error('Webhook error:', error);
res.status(400).send('Bad Request');
}
});
function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`),
Buffer.from(signature)
);
}
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook server running on port ${PORT}`);
});
<?php
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret_here'; // From webhook creation response
if ($signature && verifySignature($body, $signature, $secret)) {
// Process the webhook
$data = json_decode($body, true);
if ($data === null) {
http_response_code(400);
exit('Invalid JSON');
}
if ($data['event'] === 'video.rendered') {
$videoId = $data['data']['video_id'];
$downloadUrl = $data['data']['download_url'];
// Handle completed video...
error_log("Video rendered: $videoId, URL: $downloadUrl");
} elseif ($data['event'] === 'audio.processed') {
$audioId = $data['data']['audio_id'];
// Handle processed audio...
error_log("Audio processed: $audioId");
}
http_response_code(200);
echo 'OK';
} else {
http_response_code(401);
exit('Unauthorized');
}
function verifySignature($body, $signature, $secret) {
$expected = hash_hmac('sha256', $body, $secret);
$expectedSignature = "sha256=$expected";
return hash_equals($expectedSignature, $signature);
}
2. Create a Webhook Subscription
Use the API to subscribe to events:
curl -X POST "https://api.soundmadeseen.com/api/webhooks/" \
-H "Authorization: Api-Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_type": "video.rendered",
"target_url": "https://your-app.com/webhooks/soundmadeseen"
}'
Response:
{
"id": 123,
"event_type": "video.rendered",
"target_url": "https://your-app.com/webhooks/soundmadeseen",
"secret": "bXlfc2VjcmV0X2tleV9mb3Jfd2ViaG9va19zaWduYXR1cmVz",
"is_active": true,
"created_at": "2025-06-09T10:30:00Z"
}
Important: Save the secret value! You'll need it to verify webhook authenticity.
3. Test Your Webhook
Send a test webhook to verify your endpoint:
curl -X POST "https://api.soundmadeseen.com/api/webhooks/123/test/" \
-H "Authorization: Api-Key YOUR_API_KEY"
Available Events
Audio Events
- audio.uploaded: Audio file uploaded - User uploads an audio file
- audio.processed: Audio processing complete - Audio processing finishes successfully
- audio.failed: Audio processing failed - Audio processing encounters an error
- audio.transcribed: Audio transcribed - Audio transcription completes
- audio.transcription-failed: Audio transcription failed -Transcription encounters an error
Audio Clip Events
- audio-clip.created: Audio clip created - Audio clip is successfully created
- audio-clip.failed: Audio clip creation failed - Audio clip creation encounters an error
- audio-clip.transcribed: Audio clip transcribed - Audio clip transcription completes
- audio-clip.transcription-failed: Audio clip transcription failed - Clip transcription encounters an error
Video Events
- video.rendered: Video rendering complete - Video rendering finishes successfully
- video.failed: Video rendering failed - Video rendering encounters an error
Webhook Payload Structure
All webhooks follow this structure:
{
"event": "video.rendered",
"timestamp": "2025-06-09T15:30:00Z",
"data": {
// Event-specific data here
}
}
video.rendered
{
"event": "video.rendered",
"timestamp": "2025-06-09T15:30:00Z",
"data": {
"key": "ferfe334dfdfxjejue092g",
"title": "My Video",
"duration_seconds": 180,
"creation_method": "server",
"video_format": "mp4",
"download_url": "https://files.soundmadeseen.com/video/...",
"view_url": "https://files.soundmadeseen.com/preview/...",
"url_expires_at": "2025-06-10T15:30:00Z"
}
}
audio.processed
{
"event": "audio.processed",
"timestamp": "2025-06-09T15:30:00Z",
"data": {
"key": "123ccefhrgt466565dgfgf",
"name": "song.mp3",
"duration": 240,
"download_url": "https://files.soundmadeseen.com/audio/...",
"preview_url": "https://files.soundmadeseen.com/audio/...",
"type": "audio"
}
}
audio.transcribed
{
"event": "audio.transcribed",
"timestamp": "2025-06-09T15:30:00Z",
"data": {
"audio_file_key": "12dfdfdfdbgrtrXXadeY3XcgtRE3",
"language": "en"
}
}
Error Events (audio.failed, video.failed, etc.)
{
"event": "video.failed",
"timestamp": "2025-06-09T15:30:00Z",
"data": {
"key": "4fVVgntuDRTGhtf46rTvYT56",
"error": "save.video.to.storage.error"
}
}
Security
HMAC Signature Verification
All webhooks include an X-Webhook-Signature header with an HMAC-SHA256 signature. Always verify this signature to ensure the webhook is from SoundMadeSeen.
Codebeispiele
import hmac
import hashlib
def verify_webhook_signature(body, signature, secret):
"""Verify webhook signature"""
expected_signature = hmac.new(
secret.encode('utf-8'),
body,
hashlib.sha256
).hexdigest()
expected_signature = f"sha256={expected_signature}"
return hmac.compare_digest(expected_signature, signature)
# Usage
signature = request.headers.get('X-Webhook-Signature')
secret = 'your_webhook_secret' # From webhook creation
body = request.body
if verify_webhook_signature(body, signature, secret):
# Webhook is authentic
process_webhook(body)
else:
# Reject the webhook
return HttpResponse(status=401)
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
/**
* Verify webhook signature
* @param {Buffer|string} body - Raw request body
* @param {string} signature - Signature from X-Webhook-Signature header
* @param {string} secret - Webhook secret
* @returns {boolean} - True if signature is valid
*/
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
const expectedSignatureWithPrefix = `sha256=${expectedSignature}`;
return crypto.timingSafeEqual(
Buffer.from(expectedSignatureWithPrefix),
Buffer.from(signature)
);
}
// Usage example with Express.js
const express = require('express');
const app = express();
// Use raw middleware to get the body as Buffer for signature verification
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = 'your_webhook_secret'; // From webhook creation
const body = req.body; // This is a Buffer when using express.raw()
if (verifyWebhookSignature(body, signature, secret)) {
// Webhook is authentic
processWebhook(body);
res.status(200).send('OK');
} else {
// Reject the webhook
res.status(401).send('Unauthorized');
}
});
function processWebhook(body) {
// Parse and process the webhook data
const data = JSON.parse(body.toString());
console.log('Processing webhook:', data);
// Your webhook processing logic here
}
// Alternative usage with raw string body (if you're not using Express)
function exampleUsage() {
const signature = 'sha256=abcd1234...'; // From request headers
const secret = 'your_webhook_secret';
const body = '{"event": "test", "data": {}}'; // Raw request body as string
if (verifyWebhookSignature(body, signature, secret)) {
console.log('Webhook signature is valid');
processWebhook(body);
} else {
console.log('Invalid webhook signature');
}
}
<?php
function verifyWebhookSignature($body, $signature, $secret) {
/**
* Verify webhook signature
* @param string $body - Raw request body
* @param string $signature - Signature from X-Webhook-Signature header
* @param string $secret - Webhook secret
* @return bool - True if signature is valid
*/
$expectedSignature = hash_hmac('sha256', $body, $secret);
$expectedSignatureWithPrefix = "sha256=" . $expectedSignature;
return hash_equals($expectedSignatureWithPrefix, $signature);
}
// Usage example
function handleWebhook() {
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret'; // From webhook creation
$body = file_get_contents('php://input'); // Raw request body
if (verifyWebhookSignature($body, $signature, $secret)) {
// Webhook is authentic
processWebhook($body);
http_response_code(200);
echo 'OK';
} else {
// Reject the webhook
http_response_code(401);
echo 'Unauthorized';
}
}
function processWebhook($body) {
// Parse and process the webhook data
$data = json_decode($body, true);
if ($data === null) {
error_log('Invalid JSON in webhook body');
return;
}
error_log('Processing webhook: ' . print_r($data, true));
// Your webhook processing logic here
}
// Example usage in a webhook endpoint
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
handleWebhook();
} else {
http_response_code(405);
echo 'Method Not Allowed';
}
// Alternative standalone usage example
function exampleUsage() {
$signature = 'sha256=abcd1234...'; // From request headers
$secret = 'your_webhook_secret';
$body = '{"event": "test", "data": {}}'; // Raw request body
if (verifyWebhookSignature($body, $signature, $secret)) {
echo "Webhook signature is valid\n";
processWebhook($body);
} else {
echo "Invalid webhook signature\n";
}
}
?>
Best Practices
- Always verify signatures - Reject webhooks with invalid signatures
- Use HTTPS - Webhook URLs must use HTTPS in production
- Handle duplicates - Webhooks may be delivered more than once
- Respond quickly - Return a 2xx status code within 30 seconds
- Handle failures gracefully - We'll retry failed webhooks automatically
Ensure your webhook urls work correctly! We'll deactivate your webhook and send you an email if it fails 10 times or more.
API Reference
Create Webhook
https://api.soundmadeseen.com/public/v1/webhooks/
The body of the request must contain the fields:
- event_type - the type of event to subscribe to (from the list above)
- target_url - the url to submit the webhook to.
List Webhooks
https://api.soundmadeseen.com/public/v1/webhooks/
Get Webhook Details
https://api.soundmadeseen.com/public/v1/webhooks/{id}/
{
"id": 11,
"event_type": "audio-clip.failed",
"target_url": "http://my-valid-webhook-site.com/receiver/",
"secret": "4OcNTlmp1VmLFqSjQZTJola8fE3cPEVQqbp1BUAC3faYvC-5g0eID0qPJFB4urXQ",
"is_active": true,
"created_at": "2025-06-09T05:14:58.963861Z",
"updated_at": "2025-06-09T05:14:58.963901Z",
"consecutive_failures": 0,
"last_failure_at": null,
"last_success_at": null
}
Update Webhook
https://api.soundmadeseen.com/public/v1/webhooks/{id}/
The body of the request must contain the fields:
- target_url - the url to submit the webhook to.
- event_type - the type of event to subscribe to (from the list above)
The body of the request may contain the field:
- is_active - whether to activate or deactivate the webhook
Delete Webhook
https://api.soundmadeseen.com/public/v1/webhooks/{id}/
Test Webhook
https://api.soundmadeseen.com/public/v1/webhooks/{id}/test/
Sends a test webhook to verify your endpoint.
Regenerate Secret
https://api.soundmadeseen.com/public/v1/webhooks/{id}/regenerate-secret/
Response:
{
"message": "Secret regenerated successfully",
"new_secret": "5jfK7ezHKyFA2TFYqTH_aa3g_rfV0En_XgwwL4b8hzrP0YFEypIV6_Ud3rrVSVqb",
"warning": "Update your webhook endpoint with the new secret immediately"
}
Generates a new secret for the webhook. Warning: This will break existing integrations until you update your verification code.
View Delivery History
https://api.soundmadeseen.com/public/v1/webhooks/{id}/deliveries/
View recent delivery attempts and their status.
Troubleshooting
Webhook Not Receiving Events
- Check webhook status - Ensure is_active is true
- Verify URL - Test your endpoint manually
- Check delivery history - Look for error messages in /api/webhooks/{id}/deliveries/
- Firewall/Network - Ensure your server can receive external requests
Signature Verification Failing
- Check secret - Use the exact secret from webhook creation
- Raw body - Use the raw request body, not parsed JSON
- Character encoding - Ensure UTF-8 encoding
- Header format - Look for X-Webhook-Signature header
High Failure Rate
- Response time - Respond within 30 seconds
- HTTP status - Return 2xx status codes for success
- Error handling - Handle malformed requests gracefully
- Server capacity - Ensure your server can handle the webhook volume
Support
- API Documentation: https://docs.soundmadeseen.com
- Support Email: support@soundmadeseen.com
- Status Page: https://status.soundmadeseen.com
For webhook-specific issues, include your webhook ID and recent delivery timestamps when contacting support.