AI video API webhooks: secure production pattern

An editorial systems diagram built from physical index cards and red-blue yarn showing generation job, event callback, verified delivery and download; no readable code

Quick answer

Verify the signature against the untouched request body before trusting a webhook. Then validate its project identity, commit a durable receipt and work item, and acknowledge with 2xx. Make downstream effects safe to repeat. Keep a status-reconciliation path because delivery can fail and canceled projects do not emit a cancellation event.

A webhook receiver is an authenticated ingestion boundary. Its immediate job is to decide whether a delivery is genuine and whether your application has safely taken responsibility for it. Downloading media, sending customer notifications and updating an editor belong behind that boundary. This guide uses Magic Hour’s current webhook contract, checked October 23, 2026, and labels the database and worker pattern below as application design recommendations.

The exact Magic Hour delivery contract

Start with the Webhook Quickstart to register your public HTTPS endpoint and selected events in the Developer Hub. Save the generated webhook secret in server-side secret storage. It is the key for authenticating incoming deliveries; it is not the API bearer token used for outgoing project requests.

The current envelope contains type and payload. For video, the subscribed types are video.started, video.completed and video.errored. Read the project identifier from payload.id, not from a made-up project_id field. The contract does not expose a separate event ID: do not build a database constraint around event.id unless a future schema actually provides it.

Contract field

Meaning for the receiver

magic-hour-event-signature

HMAC-SHA256 signature represented as hexadecimal.

magic-hour-event-timestamp

Unix seconds; use the supplied string in the signed message.

type

Event name, such as video.completed.

payload.id and payload.status

Project identity and the project state included in the notification.

payload.error

Nullable object with code and message for a project error.

payload.downloads

Array containing output url and expires_at values; do not depend on the deprecated singular download field.

A reduced illustrative body is {"type":"video.completed","payload":{"id":"example-project","status":"complete","error":null,"downloads":[]}}. This shows the envelope and routing fields, not a complete schema-valid delivery or an observed output. Real payloads contain additional project fields, and a completed project with an empty downloads array needs reconciliation before your application offers a file.

Verify raw bytes before parsing JSON

Use the secure-handler reference for the exact signature recipe. The message is the timestamp header, a literal period, then the raw JSON body as received. Compute HMAC-SHA256 with the webhook secret and compare the resulting hexadecimal signature using a constant-time comparison.

For example, if the timestamp text is 1729314984 and the received body begins {"type":"video.completed", the signed message begins 1729314984.{"type":"video.completed". This is a concatenation example, not a reusable signature. Use the full original body, including whitespace and property order. Parsing JSON and serializing it again can change that representation, so the recomputed signature may no longer match.

Capture the body before middleware consumes or rewrites it. In Express, use route-appropriate raw or text handling before a JSON parser; in a framework that exposes request-body bytes, read them once and retain them for verification. A proxy or framework adapter must preserve the representation that reaches the verifier. Parse and schema-check only after authentication succeeds.

Reject missing headers, malformed timestamps and malformed signatures cleanly. Validate that the signature is the expected hexadecimal representation before decoding or comparing buffers, and check equal lengths before calling an API that requires equal-length inputs. Use the timestamp string exactly as supplied for the HMAC calculation rather than converting it to a number and reformatting it.

The official security guide recommends a five-minute freshness window. Treat that as a receiver policy, not a job-completion deadline. Check past and future skew, keep the host clock synchronized, and measure receipt time when the HTTP request arrives. A worker that runs much later should use the durable record of verification at ingestion; it should not reinterpret an old receipt as a new delivery.

Validate identity and scope after authentication

A valid signature establishes that the body matches a holder of the configured secret. It does not establish that every field is appropriate for your particular customer action. Require a supported type and a well-formed payload.id. Match that project to a stored submission for the correct account or tenant before granting access to a result or altering an order.

Use separate endpoint configuration for environments or otherwise record which webhook configuration authenticated the delivery. An event for an unfamiliar project should be durably quarantined for review, not attached to whichever customer is currently signed in. Unknown event types should follow an explicit ignore or quarantine policy so a new upstream event cannot accidentally trigger completion logic.

Commit the receipt before acknowledging

The production-handler guide requires saving verified events before 2xx. Network failures, timeouts and non-2xx responses trigger scheduled retries after 1, 2, 4, 8 and further minutes, capped at 60 minutes between attempts. Events expire 24 hours after creation, rather than 24 hours after the first failure. Each attempt receives a new timestamp and signature. These are scheduled delays, not guaranteed arrival times; worker completion and customer notification remain separate.

Use a durable inbox record for the received type, project ID, receipt time, verification result and processing state. Retain only the payload information your support and recovery process needs, with access controls and a retention policy. Keep secrets, bearer tokens and full signed download URLs out of routine logs. Store sensitive payloads in controlled storage when they are necessary.

Commit the inbox row and durable work item together, or use a transactional outbox that a worker can discover after commit. An in-memory task started just before returning success can disappear on a process restart. If persistence fails, do not claim successful ingestion: return a failure response and let delivery recovery remain possible.

After a successful commit, return a small success response promptly. A 200 response with {"message":"accepted"} fits the documented response shape. Here, accepted means the application owns the event; it does not mean media was downloaded or a user was notified. Do not keep the HTTP request open while performing those slower effects.

Make effects repeatable without inventing an event ID

A database commit can succeed while the HTTP acknowledgment is lost. The sender can then deliver again, so your worker must tolerate another receipt. A fingerprint of the raw body can identify byte-identical deliveries in your inbox, but it is not a platform event identity. Payloads may differ in nonessential fields, including refreshed download information.

Protect the business effect independently. For a single completion notification, an application-owned unique key such as account plus project ID plus notification kind prevents two workers from creating two notification tasks. Store the effect’s state and retries durably. Choose the key for the action: a generic project-ID-only key would wrongly suppress later event types for the same project.

Exactly-once execution across your database and an external email or storage service requires those systems’ own guarantees. A local unique row alone cannot resolve every crash after a remote side effect succeeds but before your worker records success. If the downstream service supports an idempotent operation, use it; otherwise retain an uncertain state for reconciliation instead of promising perfect deduplication.

Handle late events with a current project read

Do not infer a sequence number from the event timestamp or treat payload.created_at as the event time. Neither provides a verified ordering contract. If a late video.started event arrives after your application recorded completion, avoid regressing the visible state. Reconcile with GET https://api.magichour.ai/v1/video-projects/{id}, using the stored project ID and server-side API bearer token.

The current API states are draft, queued, rendering, complete, error and canceled. The video.started notification corresponds to rendering. Cancellation has no matching webhook event, so a receiver-only design cannot detect every canceled job. Periodic, bounded reads for unresolved projects provide a recovery path; a missed callback must not cause a new generation request.

For request-level failures after that read, use the Get video details reference. The receiver should record the read failure and retry or escalate its own work; it should not conflate an unavailable status response with a failed video.

Illustrative completion handling walkthrough

Suppose your application already stored example-project for a customer’s video request. A video.completed delivery arrives. The following sequence illustrates the proposed receiver behavior; it is not a production run, benchmark or guarantee.

Stage

Application action

Evidence retained

Ingest

Capture headers and raw body; verify HMAC and freshness.

Receipt time and bounded verification reason.

Commit

Match the project; save inbox receipt and unique completion work item.

Inbox ID, project ID and pending work state.

Acknowledge

Return 200 only after the durable commit.

Acknowledgment outcome, separate from worker outcome.

Process

Read current project details, then handle the existing output if available.

Current status and effect-specific progress.

Repeat delivery

Keep the new receipt if useful; reuse or skip the already-owned effect.

Duplicate decision without a second customer notification.

Worker failure

Retain pending or uncertain work for recovery.

Attempt reason and next action; no replacement generation.

If the database is unavailable at ingest, return failure and record the operational incident through a separate reliable log path. If the download later fails, retry the download or refresh the project details; webhook delivery has already been acknowledged. If a completion and an earlier start notification conflict, read current state and retain both receipts for diagnosis.

Receiver operations checklist

Before enabling production traffic, confirm that the registered URL reaches the intended handler over HTTPS, the stored secret belongs to that configuration, and body parsing occurs after verification. Confirm that missing or invalid security headers cannot enter the trusted inbox. Keep the timestamp policy explicit and inspect clock skew before widening it.

Review the durability boundary: can pending work be found after a restart, and can two workers claim the same effect without duplicating it? Review unknown-project and unknown-event handling. Document what triggers reconciliation and who handles a persistent queue failure. Distinguish delivery failures, ingestion failures and worker failures in monitoring.

During an incident, collect the webhook configuration identifier, receipt time, event type, project ID, verification category, persistence result and worker state. A rise in signature mismatches calls for checking the secret and raw-body path; a rise in stale timestamps calls for checking clock and ingress delay. Repeated successful acknowledgments with stalled workers calls for repairing the consumer, not changing the sender’s signature settings.

For event-specific payload examples, consult the event-types reference. For a concrete automation implementation, see our image-to-video n8n workflow; its job submission remains separate from this receiver’s authentication and acknowledgment boundary.

Original documentation sources: https://docs.magichour.ai/integration/webhook/overview; https://docs.magichour.ai/integration/webhook/secure-handler; https://docs.magichour.ai/integration/webhook/create-handler; https://docs.magichour.ai/integration/webhook/event-types

Implement the verified receiver boundary

Register the endpoint, follow the raw-body signature reference, and acknowledge only after durable ingestion.

Open webhook setup

Frequently asked questions

Use webhooks for normal event delivery and bounded GET reconciliation for unresolved or conflicting states. Cancellation has no matching webhook event, so a current project read is still needed to detect canceled jobs.

A failed or lost acknowledgment can lead to another delivery. Keep a durable inbox and make business effects safe to repeat; the current envelope has no separate event ID.

Return 2xx promptly after authentication, validation and a durable receipt/work commit. A 200 JSON response with a message field fits the reference. Do not acknowledge successful ingestion when persistence failed.

David's Portrait
David Hu
Co-founder & CTO of Magic Hour
David Hu is the Co-founder and CTO of Magic Hour, where he leads engineering for AI video, image, audio, and developer products. Previously, he was a full-stack engineer at Skillz and led product development teams as the company grew from Series B through its IPO. His work spans user interfaces, APIs, media systems, and infrastructure. He writes about AI media engineering, model integrations, APIs, and production workflows.
View author →

Continue Reading

Median end-to-end completion time and successful-job sample size for 11 Magic Hour video API endpoints

How to evaluate AI media APIs: total time, cost and reliability

How to evaluate AI media APIs: total time, cost and reliability
 6 AI APIs to Build a SaaS in a Weekend

6 AI APIs for a weekend SaaS: architecture and launch checks

6 AI APIs for a weekend SaaS: architecture and launch checks
Magic Hour AI x N8n

Image-to-video in n8n: reliable Magic Hour API workflow

Image-to-video in n8n: reliable Magic Hour API workflow
Magic Hour AI x N8n

Video face swap in n8n: reliable Magic Hour API workflow

Video face swap in n8n: reliable Magic Hour API workflow
Magic Hour AI x N8n

Text-to-video in n8n: Magic Hour API workflow

Text-to-video in n8n: Magic Hour API workflow
Magic Hour vs Runway vs Pika

Magic Hour vs Runway vs Pika API: 2026 Integration Guide

Magic Hour vs Runway vs Pika API: 2026 Integration Guide