APIGuides
Webhooks
Get told when a format or video you started through the API is ready or failed, and check that each message came from 10xvid.
Add a webhook
Open the API keys page in the app, go to Webhooks, and give the address of your server. It must start with https://. You get a signing secret beside it. Send test sends a ping at once and shows what your server answered.
Only what you started through the API tells your webhook. What you make in the app does not.
Events
| Event | When it is sent |
|---|---|
format.ready | The Format DNA is written and passed: videos can be made from it. |
format.failed | The site could not be read, or the Format DNA could not be written. What it cost was given back. |
video.done | A video finished. Its files are in the message. |
video.failed | A video could not be finished. Its price was given back. |
image.done, image.failed | The same for image ads. |
ping | The Send test button. |
The message
A POST with a JSON body. data.object is the format or the video, the same as the API answers for it.
{
"id": "evtmgn5c1a2b3c",
"object": "event",
"type": "video.done",
"created": "2026-10-13T10:02:18.120Z",
"data": { "object": { "id": "MV40XXRN514AA9", "object": "video", "status": "done", "files": [] } }
}
Three headers come with it:
| Header | What it holds |
|---|---|
X-10xvid-Signature | t=<seconds>,v1=<signature> |
X-10xvid-Event | The event, such as video.done |
X-10xvid-Delivery | The message's id, the same on every try |
Check the signature
The signature is the HMAC SHA-256 of the seconds, a full stop and the raw body, under your webhook's secret, as Stripe signs its own. Check it, and turn away a message more than five minutes old.
import crypto from 'node:crypto';
export function isFrom10xvid(body, header, secret) {
const [, t, v1] = /t=(\d+),v1=([a-f0-9]{64})/.exec(header ?? '') ?? [];
if (!v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const want = crypto.createHmac('sha256', secret).update(`${t}.${body}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}import hashlib
import hmac
import time
def is_from_10xvid(body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t, v1 = parts.get("t", ""), parts.get("v1", "")
if not t.isdigit() or abs(time.time() - int(t)) > 300:
return False
want = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(v1, want)Answer, and tries again
Answer with any 2xx. Anything else, or no answer within 10 seconds, and the message is sent again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then given up. The last messages and what your server answered are listed under the webhook. Use X-10xvid-Delivery to notice a message you already handled.
A new secret
New secret signs every message from then on with a new one. Put it on your server before the next message arrives.