What status should a Sume webhook receiver return? Any 2xx

Sume counts any 2xx as delivered, so 202 or 204 is fine. A tested receiver that verifies the signature, stores the job id, answers 204 and defers the work.

3 min readSume
All posts

Return any 2xx status. Sume's job webhook delivery treats a response as delivered when it is ok, so 200, 202 and 204 all count, and a 204 with no body is the leanest choice. Anything else, including a 3xx, is a failed attempt.

The receiver should verify the signature, write the job id somewhere durable and answer quickly, because each attempt times out after 10 seconds.

A minimal receiver

The sample verifies the HMAC, prints the job id where a durable write belongs and answers 204. It was tested with a signed request and returned 204.

import hmac, json, http.server
from hashlib import sha256
SECRET = "s3cret"          # load from your env; refuse to start when empty
assert SECRET, "empty signing secret"

class Hook(http.server.BaseHTTPRequestHandler):
    def do_POST(self):
        body = self.rfile.read(int(self.headers.get("content-length", 0)))
        ts = self.headers.get("x-sume-webhook-timestamp", "")
        sig = self.headers.get("x-sume-webhook-signature", "")
        want = "sume-v1=" + hmac.new(SECRET.encode(), ts.encode() + b"." + body, sha256).hexdigest()
        if not any(hmac.compare_digest(e.strip().encode(), want.encode()) for e in sig.split(",")):
            self.send_response(401); self.end_headers(); return
        print("store job", json.loads(body).get("job_id"))   # durable write goes here
        self.send_response(204)           # any 2xx counts as delivered; 204 has no body
        self.end_headers()

if __name__ == "__main__":
    http.server.HTTPServer(("0.0.0.0", 8080), Hook).serve_forever()

Status codes and their effect

A bad signature gets 401 in the sample, which Sume reads as a failed attempt and retries.

Receiver responses and delivery outcome (read 2026-10-06, Sume docs and worker source)
You returnSume records
200, 202 or 204Delivered
301 or 302Failed attempt; redirects are not followed
401, 4xx or 5xxFailed attempt; retried
No answer within 10 sFailed attempt; retried

Do the work later

Slow work inside the handler risks the 10 second timeout, and a timeout means another delivery of the same event. Write the job id to a queue or table, answer, then fetch artifacts in a worker. Up to 10 attempts are made 30 seconds apart, each with a fresh timestamp and signature, so your handler must also tolerate duplicates.

If you run the receiver behind a framework, the same rule holds: return early with a 2xx and push the real work to a queue. Keep the handler idempotent as well, because delivery is at least once. A repeat can come from a retry after your slow answer, from the 10 second timeout, or from a manual POST /v1/jobs/{id}/webhook/redeliver. Dedupe on the job id and event before you act, and you can safely answer 2xx for repeats you have already processed, which stops Sume from retrying them.

Tradeoffs

Answering before the work is done means a crash after the 204 loses the event, so the write must be durable first. Reconcile by polling GET /v1/jobs/{id} for anything you stored but never completed.

Run the sample behind your usual HTTPS front end rather than exposing port 8080 directly. The webhook_url Sume accepts must be https, on the default port, with a public hostname, so the receiver itself can stay on any internal port while a proxy handles the public side.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume