> ## Documentation Index
> Fetch the complete documentation index at: https://developers.fastdrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Changes to the FastDrop API

We version the webhook payload contract by date (`api_version` in every event envelope). Breaking changes are opt-in: existing integrations keep the behaviour they were built against.

## 2026-08-17 — Fixing footage, not just judging it

`POST /v1/fix` executes the fix a readiness check prescribes, and re-runs the
check on the result before handing it back. Two fixes today: conforming variable
frame rate footage to a constant rate, and baking a rotation flag into the
pixels. See [Fix Video](/api-reference/fix-video).

The claim worth reading twice is the second half. A file comes back marked
`succeeded` only if the original failure is confirmed gone *and* the output still
matches the source in duration, audio and orientation. If we cannot show that, the
status is `verification_failed`, the report names the check that failed and what
was measured, and you are charged nothing.

Failed fixes cost nothing in all three ways one can fail — the transcode failing,
never finishing, or finishing without passing verification.

### Changed: readiness now reports rotation

A clip stored sideways with a rotation flag used to pass the readiness check
without comment. It is now reported as `rotation_metadata`, and the contexts
disagree about it on purpose: `motion_analysis` treats it as `fix_first`, because
frame-extraction pipelines routinely decode without applying the flag and a model
handed a sideways frame returns a confident wrong answer rather than an error.
Everywhere else it is a note — editors and browsers apply the flag themselves.

**If you branch on `checks_failed`**, expect this new value. It appears in
`checks_passed` for every context on footage that does not have it.

### Fixed: the frame rate a VFR fix asks you to conform to

The `vfr` fix prescribed the clip's *average* frame rate, which on dropped-frame
footage reads lower than the camera ever shot — a clip recorded at 30fps that
averaged 20.03 was told to conform to 20.03, encoding the stutter in permanently.
It now prescribes the rate the footage declares.

**If you execute these fixes yourself**, the `fps` value in a `vfr` fix will
differ from what you saw before, and the new one is the correct target.

## 2026-08-15 — Delivery overhaul

The theme of this release: **you should not have to poll.** Webhook delivery is now something you can build on, and polling — if you still prefer it — costs a fraction of what it did.

### Fixed: webhook payload did not match the documentation

The docs described an envelope — `{"event", "data", "timestamp"}` — while the dispatcher sent a flat object with those keys absent. Anyone who followed the docs and read `payload.data.job_id` got `undefined`.

Registered endpoints now receive the documented envelope, plus an `id` for idempotency and an `api_version`. **Nothing you have built will break:** endpoints registered before today are pinned to `payload_version: "legacy"` and keep receiving the flat payload indefinitely. Check `payload_version` on `GET /v1/webhooks`; register a new endpoint to move to the envelope.

The test event now uses the same shape your endpoint receives for real events.

### Fixed: webhook failures could be silently dropped

A single malformed payload could suppress every webhook for a job while the job still reported `completed`. Each dispatch is now isolated — a job-level failure no longer costs you the batch event — and failures are logged with a stack trace.

### Fixed: `GET /v1/webhooks` returned your signing secret

The list endpoint now returns `secret_prefix` (8 characters) instead of the full secret. The full secret comes only from registration and the new rotate endpoint.

Scope, exposure window and whether you need to rotate: [FD-2026-001](/security).

### New: delivery history and replay

* `GET /v1/webhooks/deliveries` — what we sent, what your server answered, and why a delivery failed. Filter by `job_id`, `webhook_id`, `event`, `status`.
* `GET /v1/webhooks/deliveries/{id}` — the verbatim payload body, for diffing against what your handler parsed.
* `POST /v1/webhooks/deliveries/{id}/retry` — replay a delivery. Same payload, same `event_id`. **Costs no credits** — recovering a missed event no longer means re-running the job.

### New: `POST /v1/webhooks/{id}/rotate-secret`

Issue a new signing secret without deleting and re-registering the endpoint.

### New: `?wait=N` long-poll

`GET /v1/classify/{job_id}`, `GET /v1/batch/{batch_id}` and `GET /mpp/classify/{job_id}` accept `wait` (0–90 seconds). The request is held open until the job reaches a terminal state.

A 30–180 second job polled every 5 seconds costs 6–36 requests; with `wait` it costs one or two. `wait=0` is the default and is unchanged behaviour, so this is additive.

Every documented example moved from an unbounded 5-second poll loop to `wait`. The old quickstart example issued 12 requests in the first minute — enough to exceed the Free tier's 10/minute limit on a single video.

### New: `batch.progress` event

Opt in via `events` on registration. Fires at most once per 10% of a batch, never for batches under 5 videos, and never at 100% (`batch.completed` covers that). Off by default.

### New: `webhook_secret` on submissions

Pass alongside `webhook_url` to have per-request callbacks signed. Previously that path was always unsigned. Per-request callbacks are also now retried (30s / 120s / 480s) and appear in delivery history.

### Changed: `status` fields are enums in the OpenAPI spec

`JobStatusResponse.status` and batch status were typed as bare strings, so generated SDKs emitted `string` and callers hardcoded values from prose. They are now proper enums.

Two schemas also lost accidental module-qualified names — `BatchStatusResponse` was published as `app__api__v1__batch__BatchStatusResponse`. If you generate a client, regenerate it; the class names are now clean.

### Reserved for a future release

`video.transcribed`, `video.indexed`, and `readiness.flagged` are reserved event names. Subscribing to them returns `400` today rather than silently never firing.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.