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 2xx status within a few seconds. Do heavy processing asynchronously
  • Be idempotent — Use the job.id to deduplicate. While unlikely, a webhook may be delivered more than once
  • Download promptly — The outputUrl expires 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.