Webhooks
Instead of polling GET /api/v1/jobs/:id, you can provide a webhook URL when creating a job. EditClips will send an HTTP POST to your URL when the job completes or fails.
Setting up a webhook
Pass a webhook field when creating a job:
curl -X POST https://editclips.online/api/v1/jobs \
-H "Authorization: Bearer ec_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "anything-to-mp4",
"inputs": ["https://example.com/video.mov"],
"webhook": "https://yourapp.com/webhooks/editclips"
}' The webhook URL must use HTTPS. HTTP URLs are rejected with a 400 error.
Webhook events
EditClips sends two event types:
job.completed
Sent when a job finishes successfully.
POST https://yourapp.com/webhooks/editclips
Content-Type: application/json
{
"event": "job.completed",
"job": {
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"tool": "anything-to-mp4",
"status": "completed",
"outputUrl": "https://r2.editclips.online/...",
"outputFilename": "video.mp4",
"outputSize": 10485760,
"creditsCharged": 4,
"metrics": {
"wallTimeMs": 83000,
"cpuTimeMs": 72000,
"peakMemoryBytes": 536870912,
"estimatedCostMicroUsd": 1250
}
}
} The outputUrl is a presigned R2 download link valid for 1 hour. Download the file promptly.
job.failed
Sent when a job permanently fails (after all retry attempts are exhausted).
POST https://yourapp.com/webhooks/editclips
Content-Type: application/json
{
"event": "job.failed",
"job": {
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"tool": "anything-to-mp4",
"status": "failed",
"errorMessage": "FFmpeg exited with code 1: Invalid input format"
}
} When a job fails, all reserved credits are refunded automatically.
Webhook fields
| Field | Type | Description |
|---|---|---|
event | string | job.completed or job.failed |
job.id | string | Job UUID |
job.tool | string | Tool slug used for processing |
job.status | string | completed or failed |
job.outputUrl | string | Presigned download URL (completed only, valid 1 hour) |
job.outputFilename | string | Suggested filename (completed only) |
job.outputSize | number | File size in bytes (completed only) |
job.creditsCharged | number | Final credits charged (completed only) |
job.errorMessage | string | Error description (failed only) |
job.metrics | object | Processing metrics (completed only, may be null) |
Handling webhooks
Best practices
- Respond quickly — Return a
2xxstatus within a few seconds. Do heavy processing asynchronously - Be idempotent — Use the
job.idto deduplicate. While unlikely, a webhook may be delivered more than once - Download promptly — The
outputUrlexpires after 1 hour. If you miss it, poll GET /api/v1/jobs/:id to get a fresh URL - Use polling as a fallback — If your webhook endpoint is unreachable, delivery silently fails. Poll periodically as a safety net
Example handler (Node.js)
app.post("/webhooks/editclips", async (req, res) => {
const { event, job } = req.body
if (event === "job.completed") {
// Download the output file
const response = await fetch(job.outputUrl)
const buffer = await response.arrayBuffer()
await fs.writeFile(`outputs/${job.outputFilename}`, Buffer.from(buffer))
}
if (event === "job.failed") {
console.error(`Job ${job.id} failed: ${job.errorMessage}`)
}
res.sendStatus(200)
}) Retry behavior
Webhook delivery is attempted once. If your endpoint is unreachable or returns an error, the webhook is not retried. The job result remains available via GET /api/v1/jobs/:id — use polling as a fallback to ensure you never miss a result.