Skip to Content

Webhooks

AvatarGo can tell your server when something happens — a session started or ended, a pre-rendered video is ready — by sending an HTTPS POST to an address of yours. The events and what each one carries are listed in the API reference, under Webhooks.

Set up an endpoint

In the AvatarGo console , open Webhooks and press Add endpoint:

  • URL — an https:// address on your server, reachable from the internet.
  • Events — the kinds of event to send there.

The endpoint’s signing secret is shown once, right after you add it: copy it then, and keep it on your server. Send test event sends a webhook.test event (data is {}) so you can check your server answers. An account can have up to 20 endpoints.

What is sent

Each event is a POST with a JSON body:

{ "id": "evt_…", "type": "session.ended", "createdAt": "2026-11-07T09:30:00.000Z", "data": { } }

id is unique per event; data depends on the type.

Check the signature

Every request carries an AvatarGo-Signature header:

AvatarGo-Signature: t=1794040000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

t is when it was sent, in seconds since 1970; v1 is the HMAC-SHA256 of t, a dot and the request body, keyed with your signing secret, in hex. Compute it over the body exactly as received — before parsing the JSON — and check that t is recent, so an old request cannot be sent again as new.

import { createHmac, timingSafeEqual } from "node:crypto"; // header: the AvatarGo-Signature header; body: the raw request body, as a string; secret: the endpoint's signing secret. export function verifyAvatarGo(header, body, secret, toleranceSeconds = 300) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const t = Number(parts.t); if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest(); const given = Buffer.from(parts.v1 ?? "", "hex"); return given.length === expected.length && timingSafeEqual(given, expected); }

Answer, and retries

Answer with any 2xx status within 10 seconds. Anything else — another status, no answer in time, a connection that fails — is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 15 hours: 7 tries in all, over about a day.

An event can therefore arrive more than once: keep the ids you have handled and skip repeats. Do the work after you answer, not before, so a slow job does not turn into a retry.

When an endpoint keeps failing

If every delivery to an endpoint has failed for 3 days, the endpoint is turned off and your account’s owners are emailed. Fix your server, then press Turn on on the endpoint in the console: from then on, new events are sent again; those from while it was off are not.

Requests go only to addresses on the public internet, and redirects are not followed. In the console, each endpoint’s Deliveries shows what was sent in the last 30 days, with every try, and Resend sends an event again.