Pub/Sub push ack codes and a Sume webhook relay with a signature check
Pub/Sub treats only 102, 200, 201, 202 and 204 as acks. Verify Sume's sume-v1 signature at the edge, publish, return 2xx, and let Pub/Sub retry the workers.

A Pub/Sub push subscription acknowledges a message only when your endpoint returns 102, 200, 201, 202 or 204; any other status is a negative acknowledgment and the message is sent again. For Sume webhooks, verify the signature at an edge endpoint, publish the event, and return a 2xx quickly, then let the push subscription drive your workers.
Two hops, two retry systems
Sume posts a signed job.completed, job.failed or job.canceled event to your callback_url. It retries up to 10 attempts total with a fixed 30-second delay and a 10-second timeout per attempt. Google's push docs say that when a subscriber sends too many negative acknowledgments, Pub/Sub applies push backoff between 100 milliseconds and 60 seconds. Keep the edge hop tiny so the 10-second budget is never at risk.
| Hop | Ack | Retry behavior |
|---|---|---|
| Sume to your edge | Any 2xx | 10 attempts, 30 s apart, 10 s timeout each |
| Pub/Sub to workers | 102, 200, 201, 202, 204 | Other codes are resent with push backoff |
| Authenticated push | JWT in the authorization header | Verify on the worker |
| Dedup key | job_id | Process each job_id once |
The verifier
Sume signs <timestamp>.<raw_body> with HMAC SHA 256 and sends sume-v1=<hex>. During a rotation the header carries one entry per live secret, so accept any match. The function refuses an empty secret and an old timestamp.
import hmac, hashlib, time
def verify(secret, timestamp, header, raw_body, tolerance=300):
if not secret:
raise ValueError("refusing to verify with an empty secret")
if abs(time.time() - int(timestamp)) > tolerance:
return False
mac = hmac.new(secret.encode(), f"{timestamp}.{raw_body}".encode(),
hashlib.sha256).hexdigest()
expected = f"sume-v1={mac}"
ok = False
for entry in header.split(","):
ok |= hmac.compare_digest(entry.strip(), expected)
return ok
ts = str(int(time.time()))
body = '{"event":"job.completed","job_id":"job_demo"}'
sig = "sume-v1=" + hmac.new(b"s3cret", f"{ts}.{body}".encode(),
hashlib.sha256).hexdigest()
print(verify("s3cret", ts, sig, body), verify("s3cret", ts, "sume-v1=bad", body))
What the edge does
After the check passes, publish the raw body with job_id as an attribute, then return 204. A failed check returns 401 and is not published. On the worker side, your push handler returns 204 only after the result has been stored, because any other code makes Pub/Sub resend. Since either hop can deliver twice, store job_id with a unique constraint and ignore repeats.
Rotation and testing
Reject deliveries with a timestamp outside five minutes, which is the default replay window in the Sume docs. A short window blocks replays without harming normal delivery.
- Use Send test on the dashboard to check the edge without a real job.
- Rotate the signing secret in a quiet window, and accept both entries in the header during the change.
- Compare the secret fingerprint header with the one in the dashboard if a check fails.
- Log the
job_idand the verdict, never the secret.
Fallback
After ten refused attempts Sume stops, but the job is still finished. Keep a poll on the job status for events that never arrive, and redeliver with POST /v1/jobs/{job_id}/webhook/redeliver when you need a fresh signed copy. See Webhooks for headers and rotation.
Sources
Related posts
More in Integrations
- Shopify storefront MCP moved to /api/ucp/mcp: update the URL, add Sume
Shopify's storefront MCP now lives at https://{shop}/api/ucp/mcp, not /api/mcp. Update your agent, then add Sume at mcp.sume.com/mcp for product video.
- Shopify UCP needs an agent profile per call: where a Sume clip fits
Shopify's UCP storefront MCP needs an agent profile on every request. A product clip belongs after get_product, as an async Sume job the agent collects later.
- Slack image block with a Sume URL: PNG or JPEG, alt text required
Slack's image block takes a public image_url up to 3000 characters and lists png, jpg, jpeg and gif. Generate a png with Sume, then post it with alt_text.
- Slack slash command for music: 3 s ack, 5 replies in 30 min
A Slack command must be acknowledged in 3 s; response_url then allows 5 replies in 30 min. Start the Sume music job in webhook mode and reply when it lands.
Written by Sume