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.

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.
| You return | Sume records |
|---|---|
| 200, 202 or 204 | Delivered |
| 301 or 302 | Failed attempt; redirects are not followed |
| 401, 4xx or 5xx | Failed attempt; retried |
| No answer within 10 s | Failed 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
- Sume webhook Redeliver errors: 409 running or no URL, 403 jobs:write
POST /v1/jobs/{id}/webhook/redeliver returns 409 while the job runs or if it had no webhook_url, 403 without jobs:write, and 404 for a job you cannot see.
- Sume webhook Redeliver gets a fresh signature: dedupe on job_id
Redeliver re-POSTs a job's real terminal event with a new timestamp and signature, so dedupe on job_id and event, never on the signature or timestamp.
- Sume webhook retries last 270 to 370 seconds: when to poll
Sume tries a job webhook up to 10 times, 30 seconds apart, with a 10 second timeout each. That is about 4.5 to 6 minutes of retries before you must poll.
- Sume webhook retries for 4.5 minutes: dedupe on job_id in Python
Sume retries a webhook up to 10 times, 30 s apart. Make the effect happen once with a claim row keyed on job_id, shown in runnable Python with SQLite.
Written by Sume