API Reference
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/webhooks and DELETE/v1/webhooks/:webhookId.
  • Scope requirement: webhooks:manage or full_access.

Register a webhook

Receivers must be https:// URLs. Private-address and unsafe redirect targets are rejected during registration.

POST/v1/webhooks
Auth: 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/webhooks
Auth: API keyBilling: FreeBehavior: Lists current registrationsScopes: webhooks:manage
DELETE/v1/webhooks/:webhookId
Auth: 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?