Skip to content

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.ready and mv.failed for 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.


Pass webhookUrl in the task creation request:

Terminal window
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.


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.1
Content-Type: application/json
X-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).


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).

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.

Fired when a task is cancelled before it completes. Same envelope as task.failed minus errorCode/errorMessage. Refund also atomic.


Supported long-running tasks may emit named stage events. A stage emits started followed by either completed or failed for each attempt.

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.

Studio storyboard is a public Beta workflow under POST /api/v1/mv with mode="studio". Storyboard tasks may emit this sequence:

#Stage nameTypical duration
1resolve-songUsually <15s
2analyzeUsually <5s
3emotion5-30s
4concept5-30s
5character-anchor15-40s when a character anchor is needed
6narrative5-30s
7scenes10-60s
8scene-images60-240s

Followed by the task-level task.completed. Total wall-clock 3-10min for a typical 60-second song.

Scene-render tasks usually complete with task-level events only.

Finalize tasks usually complete with task-level events only.


MV create and finalize tasks may emit product-level events after the MV resource state is known.

EventWhen it fires
mv.readyAn MV task produced a ready MV resource or final video reference.
mv.failedAn 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.1
Content-Type: application/json
X-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.

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.


GuaranteeStrength
Within a task, task.stage.started precedes the corresponding task.stage.completed/failedStrong for the same stage attempt.
Stages within a task are delivered in execution orderStrong for documented stage sequences.
Across tasks: events arrive in any orderNo guarantee.
Exactly-once deliveryAt-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.


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


  1. Acknowledge fast. Respond 2xx within 30s, then continue heavier work asynchronously in your application.
  2. Dedupe by taskId + event type. Retries can re-deliver any event; the event type is in the X-Webhook-Event header.
  3. Use task-level events for billing logic. Billing settles at task-level; stage events are for UX and observability.
  4. Subscribe to task.stage.completed, not task.stage.started unless you’re building a live progress bar. Started events are noisy.
  5. Read X-Webhook-Event first. Task and stage bodies do not include an event field. MV product bodies include it as a convenience, but the header is still the routing source of truth.