Documentation

Tasks & polling

Submit an asynchronous task, poll it to completion, paginate your task history, and cancel — including idempotent retries.

Asynchronous jobs run as tasks. You create one, then poll it (or receive a webhook) until it reaches a terminal state.

Create a task

POST /v1/tasks with the capability type, a source_url, and params:

curl -X POST https://api.fotovid.co/v1/tasks \
  -H "Authorization: Bearer p6_<key_id>:<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "video.watermark",
    "source_url": "https://cdn.example.com/clip.mp4",
    "params": { "watermark_type": "text", "text": "Fotovid.co" }
  }'

It returns 201 right away:

{
  "id": "01JAB...",
  "task_type": "video.watermark",
  "status": "starting",
  "urls": { "get": "https://api.fotovid.co/v1/tasks/01JAB..." }
}

Credits are reserved when the task is created; if your balance is too low the call returns 402 INSUFFICIENT_CREDITS before any work starts.

Status lifecycle

A task moves through five states:

starting → processing → succeeded
                      ↳ failed
                      ↳ canceled

succeeded, failed, and canceled are terminal.

Poll for the result

GET /v1/tasks/{id} returns the current task. Poll until status is terminal:

curl https://api.fotovid.co/v1/tasks/01JAB... \
  -H "Authorization: Bearer p6_<key_id>:<secret>"
{
  "id": "01JAB...",
  "task_type": "video.watermark",
  "status": "succeeded",
  "outputs": [{ "kind": "video", "url": "https://tempfile.fotovid.co/v/<hex>.mp4" }],
  "created_at": "2026-06-03T12:00:00Z",
  "completed_at": "2026-06-03T12:00:07Z",
  "expires_at": "2026-06-10T12:00:00Z"
}

The output lives in outputs[].url. A polling interval of 1–2 seconds is reasonable (light jobs finish in seconds); this is a recommendation, not a contract.

Prefer not to poll? Pass a webhook URL when you create the task and Fotovid will POST a signed event to it on completion. See Webhooks. Each outputs[].url is an opaque, time-limited link — it stops working at expires_at (7 days) and isn't refreshed, so download what you need before then. Treat the URL as an opaque handle: don't parse or derive anything from its path.

List your tasks

GET /v1/tasks returns your task history, newest first, with cursor pagination:

curl "https://api.fotovid.co/v1/tasks?limit=25" \
  -H "Authorization: Bearer p6_<key_id>:<secret>"
{
  "results": [ /* … */ ],
  "next_cursor": "djIsMTcxOTk5…",
  "has_next": true
}

To fetch the next page, pass the returned next_cursor back as the cursor query parameter. limit defaults to 25 (max 100). You can also filter by status, created_from, and created_to.

Cancel a task

POST /v1/tasks/{id}/cancel cancels a task that is still starting or processing and refunds the reserved credits. Canceling an already-terminal task returns 409 TASK_NOT_CANCELABLE.

Cancellation is best-effort on already-running work: a task that is mid-flight may finish computing before the cancel lands, but its result is discarded and you are not charged.

Idempotent retries

Pass an idempotency_key (≤ 64 chars) when creating a task to make retries safe. If you submit the same key twice, Fotovid returns the existing task instead of creating a second one — a single task, a single charge:

{ "type": "image.watermark", "source_url": "…", "params": { }, "idempotency_key": "order-4823" }

The replay returns 200 (rather than a fresh 201). Scope is per-account; an empty key means no deduplication.