Webhooks
Receive real-time HTTP callbacks when your render jobs complete or fail, instead of polling the job status endpoint.
How webhooks work
When you include a webhook_uri field in your movie script, PhantomFlow sends an HTTP POST request to that URL when the render job reaches a terminal state (completed or failed). This eliminates the need to poll the status endpoint and enables event-driven architectures.
Setting the webhook URL
Add the webhook_uri field at the top level of your movie JSON when submitting a render job:
{
"width": 1920,
"height": 1080,
"webhook_uri": "https://your-server.com/webhooks/phantomflow",
"scenes": [
{
"duration": 5,
"elements": [
{
"type": "text",
"text": "Hello World",
"font_size": 48,
"position": "center"
}
]
}
]
}Webhook payload
When the job completes or fails, PhantomFlow sends a POST request with a JSON body to your webhook URL.
Completed Job Payload
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"output_url": "https://cdn.phantomflow.dev/output/a1b2c3d4.mp4",
"duration_seconds": 15.2,
"credits_used": 16
}Failed Job Payload
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "failed",
"error": {
"code": "RENDER_FAILED",
"message": "FFmpeg exited with code 1: Invalid video source URL"
}
}Payload Fields
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | string | Yes | The job UUID that triggered the webhook. |
status | string | Yes | "completed" or "failed". |
output_url | string | No | Download URL for the rendered video. Present only on completion. |
duration_seconds | number | No | Duration of the rendered video. Present only on completion. |
credits_used | integer | No | Actual credits consumed. Present only on completion. |
error | object | No | Error details with code and message. Present only on failure. |
Retry behavior
If your webhook endpoint returns a non-2xx status code or the connection times out, PhantomFlow retries the delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 10 minutes |
After 3 failed attempts, the webhook delivery is abandoned. You can always retrieve the job result by polling GET /api/jobs/:job_id.
Verifying webhook authenticity
To verify that a webhook came from PhantomFlow and not a malicious third party, check the following:
- Job ID validation: Look up the job_id in the payload against your own records. If you did not submit a job with that ID, reject the request.
- HTTPS: Always use an HTTPS webhook URL to prevent payload interception.
- IP allowlisting: If your infrastructure supports it, allowlist the IP ranges of the PhantomFlow worker servers.
Best Practices
- Return a 200 status code quickly from your webhook handler. Process the payload asynchronously if needed.
- Make your webhook handler idempotent. The same event may be delivered more than once due to retries.
- Store the job_id and check for duplicates before processing to prevent double-processing.
- Use the SSE progress endpoint (
GET /api/jobs/:id/progress) for real-time progress updates during rendering, and webhooks for final completion notifications.