Integration Guides
Webhooks
Subscribe to signed HTTPS delivery events instead of polling every async job.
What webhooks cover
- Register workspace-level webhook receivers with POST
/v1/webhooks. - Receive terminal completion and failure events for music, speech, and sound-effects jobs.
- Manage registrations with GET
/v1/webhooksand DELETE/v1/webhooks/:webhookId. - Scope requirement:
webhooks:manageorfull_access.
Register a webhook
Receivers must be https:// URLs. Private-address and unsafe redirect targets are rejected during registration.
POST
/v1/webhooksAuth: API keyBilling: FreeBehavior: Registers an HTTPS receiver and returns a signing secret onceScopes: webhooks:manage
Create a webhook registrationbash
curl -X POST https://prod-backup-backend.wubble.ai/v1/webhooks \
-H "Authorization: Bearer wbk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/wubble/webhooks",
"events": [
"music.song.completed",
"speech.text_to_speech.completed",
"sound_effects.generated"
],
"description": "Production media pipeline"
}'Sensitive data
Treat the returned webhook secret as a credential. The current public GET
/v1/webhooks implementation also includes the stored secret in its response, so avoid logging or exposing webhook list payloads downstream.Verify the signature
Verify X-Wubble-Signature against the raw request body using HMAC-SHA256. The timestamp header is useful for replay windows on your side, but the current signature is computed over the raw JSON body itself.
Signature verification
import crypto from 'node:crypto';
export function verifyWubbleWebhook(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const provided = signatureHeader.replace(/^sha256=/i, '').trim();
if (!/^[0-9a-f]+$/i.test(provided) || provided.length !== expected.length) {
return false;
}
return crypto.timingSafeEqual(
Buffer.from(provided, 'hex'),
Buffer.from(expected, 'hex'),
);
}Delivery format
Example delivery requesthttp
POST https://example.com/wubble/webhooks
Content-Type: application/json
X-Wubble-Signature: sha256=<hex>
X-Wubble-Event: music.song.completed
X-Wubble-Timestamp: 1747042875123
{
"request_id": "590b41ed-bf0f-42cf-bad9-32568c09475a",
"status": "completed",
"result_url": "https://cdn.wubble.ai/api-generations/...",
"event": "music.song.completed"
} Headers
Expect X-Wubble-Signature, X-Wubble-Event, and X-Wubble-Timestamp on each delivery.
Events
Common names include music.song.completed, speech.text_to_speech.completed, and sound_effects.generated.
Payloads
Terminal success payloads include at least request_id and status; failure payloads also include an error field.
Retries and disable behavior
GET
/v1/webhooksAuth: API keyBilling: FreeBehavior: Lists current registrationsScopes: webhooks:manage
DELETE
/v1/webhooks/:webhookIdAuth: API keyBilling: FreeBehavior: Soft-disables a webhook registrationScopes: webhooks:manage
- Deliveries retry with backoff. The default delay sequence starts at 30 seconds and expands into multi-minute and multi-hour retries.
- Repeated all-attempts-exhausted failures increment a webhook failure counter.
- After 10 terminal delivery failures, the registration is auto-disabled.
Receiver best practice
Return a fast
2xx as soon as you persist the event, then process it asynchronously in your own worker.Was this page helpful?