Skip to main content
Video generation is asynchronous by design. This recipe shows the minimum application loop you need for a production-safe integration. Create a workspace-managed webhook endpoint first and store the signing secret returned by that management operation. Video jobs reference only the endpoint ID; Phaseo keeps the secret encrypted and supports rotation without changing job requests.

1. Create the job

Store the returned video_id immediately.

2. Poll status until terminal

Your worker or application can poll:
Use polling for your own control loop even if you also enable webhooks. That gives you a direct way to recover if a webhook destination is temporarily unavailable. Persist and poll with the gateway-owned video id. native_video_id, when present, is provider-native correlation metadata; do not use it for Phaseo polling, WebSocket, or content URLs.
Video cancellation is temporarily unavailable. The cancel endpoint returns 501, and cancel_url remains null in video responses.

3. Consume webhook deliveries

The current async webhook payloads are normalized around:
  • the job identifier
  • lifecycle status
  • delivery status summary
  • recent delivery attempts
  • whether signing is enabled
Design your webhook consumer to:
  1. verify the signature
  2. treat deliveries as retryable and idempotent
  3. fetch the latest job status if the webhook payload and local state disagree
Phaseo signs every managed webhook delivery with the endpoint secret:
  • x-phaseo-timestamp: Unix timestamp in seconds
  • x-phaseo-signature: hex HMAC-SHA256 of ${timestamp}.${rawBody} using your webhook secret
  • x-phaseo-event-id: stable event id for this job/event
  • x-phaseo-event-type: event type such as video.completed
  • x-phaseo-delivery-key: idempotency key, including progress bucket when applicable
  • x-phaseo-attempt and x-phaseo-max-attempts: retry attempt metadata
Verify the signature against the exact raw request body before parsing JSON:
Store x-phaseo-delivery-key or the payload id before side effects. Return any 2xx status only after your durable state has been updated; non-2xx responses are retried with the same event id and an incremented attempt number. Webhook subscriptions accept generic job.* events or matching video.* events. If you omit events, Phaseo subscribes the webhook to job.status_changed plus terminal job.completed, job.failed, job.cancelled, and job.expired notifications. A status-change payload includes status_change.previous_status and status_change.status. If you provide events, at least one event must be valid for video jobs; all-invalid or cross-kind-only lists such as ["batch.completed"] are rejected instead of being broadened silently. Webhook callback URLs must use HTTPS. Literal private, loopback, link-local, and wildcard hosts are rejected; http://localhost, http://127.0.0.1, and http://[::1] are accepted only for local development callbacks.

4. Read the final output

When the job reaches a completed terminal state, fetch content from:
If your application only needs a download URL, use the dedicated download-url surface where supported by the endpoint.

5. What to monitor

  • job lifecycle status
  • webhook delivery success and retry counts
  • last delivery HTTP status
  • failure timestamps and error messages
These signals should live together in your internal async-job dashboard so operations can distinguish generation failures from webhook-delivery failures.
Last modified on August 10, 2026