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=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdt 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.
Node
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.