MiniMax video callback_url: echo the challenge in 3 seconds

MiniMax checks a callback_url by POSTing a challenge you must echo within 3 seconds. Sume skips the handshake and signs each terminal webhook instead.

5 min readSume
All posts

MiniMax validates a callback_url by POSTing a body with a challenge field, and your server must echo it back within 3 seconds; after that, status updates arrive as the task moves through processing, success or failed (read 2026-10-02). Sume's callback_url on /v1/videos has no handshake. It sends one signed POST when the job reaches a terminal state, and you verify the signature.

How do the two callbacks compare?

MiniMax documents the callback on its text-to-video reference and still recommends polling in its guide, which names a 10 second interval. Sume's webhook rules come from its webhooks guide and its video generation page.

MiniMax and Sume callbacks (MiniMax read 2026-10-02)
QuestionMiniMaxSume
Setup checkPOST with a challenge, echo within 3 sNone; the URL must be public HTTPS
Eventsprocessing, success, failedTerminal only: job.completed, job.failed, job.canceled
AuthenticityNot described on the pages I readHMAC SHA 256 over timestamp.body in x-sume-webhook-signature
RetriesNot describedUp to 10 attempts, 30 s apart, 10 s timeout

How do you verify a Sume webhook?

Sume signs <timestamp>.<raw_body> with your workspace signing secret and sends sume-v1=<hex> in x-sume-webhook-signature. During a secret rotation the header holds one entry per live secret, so accept any match. Reject old timestamps; five minutes is the documented default. The sketch below refuses an empty secret, which would otherwise make every signature forgeable:

import hashlib, hmac, time

def verify(raw_body: bytes, timestamp: str, header: str, secret: str, tolerance: int = 300) -> bool:
    if not secret:
        return False
    try:
        ts = int(timestamp)
    except ValueError:
        return False
    if abs(time.time() - ts) > tolerance:
        return False
    msg = f"{ts}.".encode() + raw_body
    expected = "sume-v1=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
    ok = False
    for entry in header.split(","):
        if hmac.compare_digest(entry.strip().encode(), expected.encode()):
            ok = True
    return ok

if __name__ == "__main__":
    body, now = b'{"event":"job.completed"}', str(int(time.time()))
    sig = "sume-v1=" + hmac.new(b"s3cret", f"{now}.".encode() + body, hashlib.sha256).hexdigest()
    print(verify(body, now, sig, "s3cret"), verify(body, now, sig, ""))

What about polling?

Keep polling as a fallback on both sides. Sume's docs say delivery is an optimization, never the only recovery path: a job can reach its terminal state while your endpoint was down, and status_url still has the answer. Use job_id as your idempotency key, because a redelivery sends the same event again.

MiniMax's tasks are queryable for the last 7 days according to its guide (read 2026-10-02), so a missed callback there has a deadline. Sume's polling and webhook behavior do not state a comparable expiry on the pages used here, so store the result URL when you first see it.

Which handshake mistakes are common?

  • Answering MiniMax's challenge after parsing slow business logic: respond first, work later.
  • Verifying a Sume signature against re-serialized JSON: use the raw body bytes.
  • Treating a webhook as proof of payment or output: fetch the job result before you publish.
  • Returning 200 before storing the event: Sume retries only on errors and non-2xx.

Sources

Related posts

More in Models

All Models posts

Written by Sume