Webhooks
Webhook endpoint implementation details
This guide will walk you through setting up and securely handling webhooks. The implementation follows the Standard Webhooks specification.
Webhook Delivery
Timeouts
- Webhooks have a 10-second timeout window, including both connection and read timeouts.
- If a webhook delivery attempt fails, we will retry sending the event using exponential backoff to avoid overloading your system.
Retry Schedule The retry attempts follow this schedule:
- Immediately (first retry)
- 5 seconds after the first failure
- 5 minutes after the second failure
- 30 minutes after the third failure
- 2 hours after the fourth failure
- 5 hours after the fifth failure
- 10 hours after the sixth failure
- 14 hours after the seventh failure
- 20 hours after the eighth failure
- 24 hours after the ninth failure (final retry)
Total Timeline
For example, if a webhook fails three times before
succeeding, it will be delivered roughly 35 minutes and 5 seconds
following the first attempt.
If all the retries are exhausted, the related webhook endpoint will be disabled and a notification to the owner of the organization will be triggered.
If a webhook endpoint is removed or disabled, all delivery attempts to that endpoint will be stopped immediately.
Idempotency
- Each webhook event contains a unique
webhook-idheader. Use this to implement idempotency and avoid processing the same event multiple times. - Even if you receive the same event more than once (due to retries), your system should handle it gracefully without causing errors or duplicate actions.
Ordering
- Webhook delivery order is not guaranteed, as webhooks may be delivered out of order due to retries or network issues.
- Ensure your system can handle events arriving out of order by using the
webhook-idheader to process events correctly.
Payload
The following will be payload format:
{
"type": "video.finished",
"org_id": "3921aa39-ffff-ffff-aa80-b93e78765a29",
"data": {
"id": "4b65cc44-ffff-ffff-a7a2-f3ae21afe43c",
"self": "https://api.animationapi.com/v1/videos/4b65cc44-ffff-ffff-a7a2-f3ae21afe43c",
"status": "COMPLETED",
"video_url": "https://cdn.animationapi.com/3921aa39-ffff-ffff-ffff-b93e78765a29/vid_A8pfFffFFf4LkDUl-7Dqp.gif",
"created_at": "2025-10-18T17:20:38.835Z",
"updated_at": "2025-10-18T17:21:06.721Z"
}
}Securing Webhooks
To ensure the security of your webhooks, always validate the payloads and use HTTPS.
Verifying Signatures
Each webhook request includes a webhook-signature header — an HMAC SHA256 signature of the webhook payload and timestamp,
signed with your secret key. To verify a webhook came from AnimationAPI:
- Compute the HMAC SHA256 of this string using your webhook secret key obtained from the Create Webhook response
- Concatenate the
webhook-id,webhook-timestamp, and stringifiedpayloadvalues from the webhook with periods (.) The respective payloads for outgoing webhooks can be found in the Webhook Payload (Video Generation Started, Video Generation Finished, Video Generation Failed). - Compare the computed signature to the received
webhook-signatureheader value. If they match, the webhook is authentic.
Since we follow the Standard Webhooks specification, you can use one of their libraries to verify the signature: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries
Responding to Webhooks
- Your webhook handler must return a 2xx status code to acknowledge receipt of the event.
- Any other response will be treated as a failure, and the webhook will be retried.
Example code
// index.ts in express
import { Webhook } from "standardwebhooks";
import { type Request, type Response } from "express";
const webhook = new Webhook("ANIMATION_API_WEBHOOK_SECRET");
// Add this endpoint using Create Webhook request
app.post("/webhook/animationapi", async (req: Request, res: Response) => {
try {
const body = req.body;
const webhookHeaders: WebhookUnbrandedRequiredHeaders = {
"webhook-id": (req.headers["webhook-id"] || "") as string,
"webhook-signature": (req.headers["webhook-signature"] || "") as string,
"webhook-timestamp": (req.headers["webhook-timestamp"] || "") as string,
};
const raw = JSON.stringify(body);
const payload = await webhook.verify(raw, webhookHeaders);
// ... Rest of your code HERE ...
res.status(200).json({ received: true });
} catch (error) {
console.error("Error processing webhook:", error);
res.status(400).json({ error: "Webhook handler failed" });
}
});