Dashboard

Webhooks

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

EventWhen it fires
video.processing.startedTranscoding begins
video.processing.completedAll renditions ready, video playable
video.processing.failedTranscoding error (payload includes reason)
wallet.low_balanceMaster balance drops below your configured threshold
wallet.refilledTop-up completes (post-payment confirmation)
playback.token.exhaustedDaily token quota hit for an API key

Planned delivery contract

  • Method: POST over 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.