Webhooks let you receive an HTTP POST whenever a relevant event happens in your account — no polling required.
⚠️ Coming soon
Webhook delivery is under active development. The endpoints and
event shapes below reflect the planned API. Until they ship, poll
GET /v1/video/{videoId}
every 10–30 seconds to track processing status.
Planned event types
| Event | When it fires |
|---|---|
video.processing.started | Transcoding begins |
video.processing.completed | All renditions ready, video playable |
video.processing.failed | Transcoding error (payload includes reason) |
wallet.low_balance | Master balance drops below your configured threshold |
wallet.refilled | Top-up completes (post-payment confirmation) |
playback.token.exhausted | Daily token quota hit for an API key |
Planned delivery contract
- Method:
POSTover HTTPS - Content-Type:
application/json - Signature:
X-Ansdev-Signature: t=…,v1=…(HMAC-SHA256) - Timeout: 10 seconds per attempt
- Retries: exponential backoff at 1, 5, 25, 125, 625 seconds (5 retries over ~12 minutes), then dead-letter
- Idempotency: every payload carries a unique
eventId— store it and dedupe on your side
Planned payload shape
{
"eventId": "evt_8f3a9b2c1d4e5f6a",
"eventType": "video.processing.completed",
"createdAt": "2026-05-23T10:15:00Z",
"data": {
"videoId": "vid_abc123def456",
"durationSec": 120,
"qualities": ["1080p", "720p", "480p"],
"encrypted": false
}
}
Verifying signatures (planned)
import crypto from 'node:crypto';
function isValid(rawBody, signatureHeader, secret) {
const [tPart, vPart] = signatureHeader.split(',');
const timestamp = tPart.split('=')[1];
const signature = vPart.split('=')[1];
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex'),
);
}
💡 Polling pattern until webhooks ship
For most apps, a simple poll loop is enough until webhooks land:
async function waitForReady(videoId) {
while (true) {
const { data } = await fetch(
`https://api.ansdev.cloud/v1/video/${videoId}`,
{ headers: { 'x-ansdev-key': process.env.ANSDEV_API_KEY } },
).then((r) => r.json());
if (data.status === 'COMPLETED') return data;
if (data.status === 'FAILED') throw new Error('Transcode failed');
await new Promise((r) => setTimeout(r, 10_000));
}
}
Poll once every 10–30 seconds — there's no quota on metadata reads.