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. Terminal task bodies are flat public Task descriptors. Stage bodies contain only taskId plus a stage snapshot. MV product events also include a top-level event field with the same value as the header for 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 "Idempotency-Key: webhook-song-001" \
-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 every event, read the type from X-Webhook-Event. There is no outer { event, task } envelope for task or stage events:

POST /your/webhook HTTP/1.1
Content-Type: application/json
X-Webhook-Event: task.completed

Every delivery includes these headers:

HeaderMeaning
X-Webhook-IdStable event-delivery ID. It does not change across retries; use it as the primary deduplication key.
X-Webhook-EventEvent type such as task.completed or mv.ready.
X-Webhook-TimestampISO timestamp generated for this delivery attempt. It is metadata and is not part of the body-only HMAC.
X-Webhook-AttemptOne-based delivery attempt number.
X-Webhook-SignatureOptional sha256=<hex> HMAC over the exact raw body when a webhook secret is configured.

Terminal task event bodies use the same customer-safe shape as GET /api/v1/tasks/{taskId}:

{
"taskId": "task_01H...",
"status": "COMPLETED",
"creditsRequired": 250,
"creditsCharged": 250,
"refunded": false,
"retryable": false,
"createdAt": "2026-08-08T10:00:00.000Z",
"updatedAt": "2026-08-08T10:03:07.432Z",
"processingStartedAt": "2026-08-08T10:00:01.000Z",
"processingCompletedAt": "2026-08-08T10:03:07.432Z",
"processingDuration": 186432,
"processingDurationText": "3.1m",
"inputParameters": { "prompt": "..." },
"outputResults": { "result": "..." },
"resources": [],
"warningCodes": []
}

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 once, followed by either completed or a final failed. An internal retry does not emit another started event or an intermediate failed webhook.

The event type (e.g. task.stage.completed) is in the X-Webhook-Event header. The body contains taskId and a top-level stage block; it is not a full Task descriptor and has no top-level Task status:

{
"taskId": "task_01H...",
"stage": {
"name": "scenes",
"description": "Plan scenes",
"status": "completed",
"startedAt": "...",
"completedAt": "...",
"durationMs": 47213,
"attempt": 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.

task.stage.failed is emitted only after the stage exhausts its configured attempts:

{
"taskId": "task_01H...",
"stage": {
"name": "scenes",
"description": "Plan scenes",
"status": "failed",
"startedAt": "2026-08-08T10:00:01.000Z",
"completedAt": "2026-08-08T10:02:01.000Z",
"durationMs": 120000,
"attempt": 2,
"maxAttempts": 2,
"error": {
"code": "STAGE_SCENES_FAILED",
"message": "stage timed out"
}
}
}

A final stage failure does not always mean the Task fails because some stages are optional. Use the later task-level terminal event, or poll the Task, as the authoritative outcome.

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.

GuaranteeStrength
HTTP arrival order, including stage boundaries from one taskNo guarantee. Delivery jobs run independently and retries can overtake later events.
Across tasks: events arrive in any orderNo guarantee.
Exactly-once deliveryAt-least-once — retries can re-deliver. Dedupe by the stable X-Webhook-Id.

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 X-Webhook-Id, 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 10s, then continue heavier work asynchronously in your application.
  2. Dedupe by X-Webhook-Id. The value remains stable across every delivery attempt for one event.
  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.