fal webhook status OK or ERROR vs the Sume job webhook

fal posts a webhook with status OK or ERROR, separate from queue status. Sume's job webhook uses the same words plus an event name. One handler for both.

5 min readSume
All posts

fal's webhook payload carries a status of OK or ERROR, and Sume's job webhook payload does too, but they sit in different places in the lifecycle. On fal, OK/ERROR is a webhook-only outcome, distinct from the queue statuses IN_QUEUE, IN_PROGRESS and COMPLETED. On Sume, status: "OK" rides next to an event name (job.completed, job.failed, job.canceled) and the job's own status stays completed, failed or canceled.

If you are porting a fal handler, the field name matches but you should branch on event, not only on status.

What does fal's webhook say?

Per the fal queue page, passing webhook_url to submit() makes fal post the result automatically on completion. The page says the webhook body has status "OK" for success or "ERROR" for failure, and that this is distinct from the three request lifecycle states. This page does not describe signature verification, which is covered separately by fal's other docs and by our fal signature comparison.

What does Sume's job webhook look like?

Sume sends terminal job events only: no progress or partial deliveries. Submit with mode: "webhook" and a public HTTPS webhook_url (callback_url is an alias, and sending either without a mode gives you webhook mode). The envelope carries event, request_id, job_id, status and payload.artifacts[], and two rules follow from it:

  • The asymmetry that matters: on Sume a canceled job also arrives with status: "ERROR".
  • If your fal code treats ERROR as a failure to alert on, canceled Sume jobs will page you. Branch on event first.
Webhook fields, fal versus Sume (read 2026-10-02)
QuestionfalSume
Outcome fieldstatus: OK or ERRORstatus: OK; failed and canceled use ERROR with an error object
Event nameNot described on the queue pagejob.completed, job.failed, job.canceled
Job status vocabularyIN_QUEUE, IN_PROGRESS, COMPLETEDqueued, processing, completed, failed, canceled
Progress eventsNot described on the queue pageNone, terminal events only
Result locationResult stored, retrievable from the result URLpayload.artifacts[] with media URLs

How do I write one handler that reads both?

Keep the verification step provider specific and normalize after it. This Python function refuses an empty secret and checks the Sume signature (sume-v1= HMAC SHA 256 over timestamp.raw_body, five minute tolerance as the docs suggest), then maps the event to your own state.

import hashlib, hmac, json, time

def verify_sume(raw: bytes, ts: str, header: str, secret: str) -> bool:
    if not secret:
        return False
    try:
        t = int(ts)
    except ValueError:
        return False
    if abs(time.time() - t) > 300:
        return False
    mac = hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256)
    want = "sume-v1=" + mac.hexdigest()
    return any(hmac.compare_digest(want, e.strip()) for e in header.split(","))

def normalize(raw: bytes) -> str:
    body = json.loads(raw)
    return {"job.completed": "done", "job.failed": "failed",
            "job.canceled": "canceled"}.get(body.get("event"), "ignore")

What should I keep as a fallback?

Sume's docs recommend keeping a polling path alongside webhooks: GET /v1/jobs/{id}/status until terminal: true, then GET /v1/jobs/{id}/result. During a signing-secret rotation the signature header carries one sume-v1= entry per live secret, newest first, which the loop above handles with any.

Sume also exposes x-sume-webhook-secret-fingerprint on every delivery so you can compare it with the dashboard when a signature fails, without sending the secret anywhere. Read the delivery details in Sume webhooks, and the broader differences in Sume vs fal.

Sources

Related posts

More in Comparisons

All Comparisons posts

Written by Sume