OmnAPI Webhook Events
OmnAPI emits webhooks for three layers of lifecycle:
- Task-level:
task.completed,task.failed,task.cancelled— fired once per task at terminal state. - Stage-level:
task.stage.started,task.stage.completed,task.stage.failed— emitted for named milestones in supported long-running tasks. Use these events for progress bars or operational dashboards without aggressive polling. - Product-level: product-specific events that summarize a resource outcome,
such as
mv.readyandmv.failedfor Music Video jobs.
All event layers fire to the same destination URL. The event type is delivered in the
X-Webhook-Event HTTP header. Task and stage event bodies are flat task payloads;
MV product events also include a top-level event field with the same value as
the header for easier product-level routing.
Subscribing
Section titled “Subscribing”Pass webhookUrl in the task creation request:
curl -X POST https://api.omnapi.com/api/v1/suno/songs \ -H "x-api-key: sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "mode": "simple", "prompt": "upbeat lo-fi", "config": { "webhookUrl": "https://yourapp.com/webhooks/omnapi" } }'The dashboard also lets you set a default destination for account-level task
events. Per-task webhookUrl takes precedence for that one request.
Task and stage delivery shape
Section titled “Task and stage delivery shape”For task.* and task.stage.* events, the event type is delivered in the
X-Webhook-Event HTTP header. The JSON body is a flat task payload —
the task fields sit at the top level, with no outer { event, task } envelope:
POST /your/webhook HTTP/1.1Content-Type: application/jsonX-Webhook-Event: task.completed{ "taskId": "task_01H...", "status": "COMPLETED", "creditsRequired": 250, "creditsCharged": 250, "createdAt": "...", "completedAt": "...", "durationMs": 187432, "inputParameters": { /* original POST body */ }, "outputResults": { /* final task output */ }}The body is essentially the same task fields returned by GET /api/v1/tasks/{taskId}.
Read the event type from the X-Webhook-Event header for task and stage events.
Stage events add a top-level stage block (see below).
Task-level events
Section titled “Task-level events”task.completed
Section titled “task.completed”Fired once when a task transitions to COMPLETED. task.outputResults
carries the final payload, creditsCharged reflects the actual cost
(may be less than creditsRequired after refunds or partial output handling).
task.failed
Section titled “task.failed”Fired once on FAILED (X-Webhook-Event: task.failed). The flat body adds:
{ "taskId": "task_01H...", "status": "FAILED", "errorCode": "MV_TOO_MANY_FAILED_SCENES", "errorMessage": "5/8 scene images failed (threshold 0.3)"}Any eligible refund to your credit balance is issued with the status transition. By the time you receive the event, that settlement is already complete.
task.cancelled
Section titled “task.cancelled”Fired when a task is cancelled before it completes. Same envelope as
task.failed minus errorCode/errorMessage. Refund also atomic.
Stage-level events
Section titled “Stage-level events”Supported long-running tasks may emit named stage events. A stage emits
started followed by either completed or failed for each attempt.
Stage payload
Section titled “Stage payload”The event type (e.g. task.stage.completed) is in the X-Webhook-Event header.
The flat task body gains a top-level stage block:
{ "taskId": "task_01H...", "status": "PROCESSING", "stage": { "name": "scenes", "description": "Plan scenes", "startedAt": "...", "completedAt": "...", "durationMs": 47213, "attemptCount": 1, "maxAttempts": 2, "payload": { "sceneCount": 8, "genre": "rap", "mvParadigm": "performance" } }}stage.payload is stage-specific. Treat unknown keys as metadata and do not
build business-critical logic around fields that are not documented for your
endpoint.
MV Studio storyboard stage timeline
Section titled “MV Studio storyboard stage timeline”Studio storyboard is a public Beta workflow under POST /api/v1/mv with
mode="studio". Storyboard tasks may emit this sequence:
| # | Stage name | Typical duration |
|---|---|---|
| 1 | resolve-song | Usually <15s |
| 2 | analyze | Usually <5s |
| 3 | emotion | 5-30s |
| 4 | concept | 5-30s |
| 5 | character-anchor | 15-40s when a character anchor is needed |
| 6 | narrative | 5-30s |
| 7 | scenes | 10-60s |
| 8 | scene-images | 60-240s |
Followed by the task-level task.completed. Total wall-clock 3-10min for a
typical 60-second song.
MV scene render stage timeline
Section titled “MV scene render stage timeline”Scene-render tasks usually complete with task-level events only.
MV finalize stage timeline
Section titled “MV finalize stage timeline”Finalize tasks usually complete with task-level events only.
MV product events
Section titled “MV product events”MV create and finalize tasks may emit product-level events after the MV resource state is known.
| Event | When it fires |
|---|---|
mv.ready | An MV task produced a ready MV resource or final video reference. |
mv.failed | An MV task failed and the MV resource cannot advance without a new request. |
The event type is still delivered in X-Webhook-Event. The JSON body includes
the same event value and MV-specific routing fields:
POST /your/webhook HTTP/1.1Content-Type: application/jsonX-Webhook-Event: mv.ready{ "event": "mv.ready", "taskId": "task_01H...", "status": "COMPLETED", "mode": "fast", "mvId": "mv_01H...", "finalMvId": "final_01H...", "finalStatus": "READY", "deliveryStatus": "ready", "viewUrl": "/api/v1/mv/mv_01H...", "finalUrl": "/api/v1/mv/mv_01H.../final"}Treat missing optional fields as “not available yet” rather than as a schema
error. For example, finalUrl is only present when a final MP4 can be requested.
task.stage.failed
Section titled “task.stage.failed”Stage failures may be retried. You receive task.stage.failed
(X-Webhook-Event: task.stage.failed) for each failed attempt:
{ "taskId": "task_01H...", "status": "PROCESSING", "stage": { "name": "scenes", "attemptCount": 1, "maxAttempts": 2, "errorCode": "STAGE_TIMEOUT", "errorMessage": "stage timed out after 120s" }}Then either:
- The next attempt produces
task.stage.completed→ task processing continues - All attempts fail → the task fails → you also receive
task.failed
A stage failure does not necessarily mean the task failed — wait for the task-level event to know the final outcome.
Ordering + delivery guarantees
Section titled “Ordering + delivery guarantees”| Guarantee | Strength |
|---|---|
Within a task, task.stage.started precedes the corresponding task.stage.completed/failed | Strong for the same stage attempt. |
| Stages within a task are delivered in execution order | Strong for documented stage sequences. |
| Across tasks: events arrive in any order | No guarantee. |
| Exactly-once delivery | At-least-once — retries can re-deliver. Dedupe by taskId, X-Webhook-Event, and stage fields when present. |
| Delivery latency | <5s p99 from event creation; minutes if early attempts fail and the event is retried. |
OmnAPI makes one initial webhook delivery attempt and then up to 5 retries with backoff 10s → 20s → 40s → 10min → 30min. HTTP 2xx is treated as success. After the final attempt the delivery is visible in the dashboard’s Webhook Events page where you can manually re-trigger.
Securing endpoints
Section titled “Securing endpoints”Use HTTPS webhook URLs on domains you control. Verify signatures when a webhook
secret is configured, dedupe on taskId + the X-Webhook-Event header, and
reject duplicate deliveries in your application. For higher-security
deployments, combine signature verification with your own allowlist or gateway
rules.
If you configure a webhook secret, OmnAPI sends
X-Webhook-Signature: sha256=<hex> where the hex value is HMAC-SHA256 over the
raw request body. A minimal Node receiver is available here:
Download webhook-receiver-node.ts
Best practices
Section titled “Best practices”- Acknowledge fast. Respond 2xx within 30s, then continue heavier work asynchronously in your application.
- Dedupe by
taskId+ event type. Retries can re-deliver any event; the event type is in theX-Webhook-Eventheader. - Use task-level events for billing logic. Billing settles at task-level; stage events are for UX and observability.
- Subscribe to
task.stage.completed, nottask.stage.startedunless you’re building a live progress bar. Started events are noisy. - Read
X-Webhook-Eventfirst. Task and stage bodies do not include aneventfield. MV product bodies include it as a convenience, but the header is still the routing source of truth.
See also
Section titled “See also”- The Task Model — task lifecycle, status enum, config, polling
- MV API — the canonical multi-stage flow
- Dashboard → Webhooks — see delivery history + manually retry failed events