MixMediaAPI · Docs
Open as Markdown

Callbacks

We POST the result to your server when the task finishes — no recordInfo polling.

How to enable

Pass callBackUrl to createTask. When the task becomes success or fail, we POST the same JSON that recordInfo returns.

POST <callBackUrl>
Content-Type: application/json
X-Signature: 3f1c9a…e07b        # signature, see below
X-Timestamp: 1790621128
X-Task-Id: task_nano-banana-2_1790621023886ca4e4d
X-Event: task.success           # or task.fail
X-Delivery-Attempt: 1

What to reply

Any 2xx response within 15 seconds. Otherwise we retry after 30 s, 2 min and 10 min (4 attempts in total). An event may arrive twice — dedupe by taskId.

Signature verification

The secret (whsec_…) is in Dashboard → API keys. The signature is the hex HMAC-SHA256 of the raw request body:

import hmac, hashlib

def valid(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

# Flask: valid(request.get_data(), request.headers["X-Signature"], "whsec_...")
import crypto from "node:crypto";

function valid(rawBody, signature, secret) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

// Express: app.post("/callback", express.raw({ type: "application/json" }), (req, res) => {
//   if (!valid(req.body, req.get("X-Signature"), process.env.WEBHOOK_SECRET)) return res.sendStatus(401);
//   const task = JSON.parse(req.body).data; res.send("ok");
// });
<?php
$raw = file_get_contents('php://input');
$ok = hash_equals(hash_hmac('sha256', $raw, 'whsec_...'), $_SERVER['HTTP_X_SIGNATURE'] ?? '');
if (!$ok) { http_response_code(401); exit; }
$task = json_decode($raw, true)['data'];

Tip: reject requests whose X-Timestamp is older than 5 minutes.

Limits

The URL must be public http(s). Local and private addresses (localhost, 10.x, 192.168.x, etc.) are rejected with code 422. Delivery status is in recordInfo.data.callback and in the dashboard.