Webhook unknown event type: return 204, not a 500

One Sume verifier covers run and job webhooks. Route on event and answer 204 for unknown types so a new event never becomes a 500 and a retry storm.

4 min readSume
All posts

Route on the event field and answer 204 for any event you do not recognize. Sume run webhooks and generation job webhooks share one signature scheme, so one endpoint can receive both, and treating an unknown event as 204 is what stops a newly added event type from becoming a 500 and a retry storm.

Which events can one endpoint receive?

The sume-v1 scheme is identical across them, so a single verifier covers all of these. The payloads differ, so never assume a body has run_id.

Webhook events and the id each carries, from the Sume docs, read 2026-09-29.
EventSurfaceId on the body
action.run.terminalAction runsrun_id
format.run.terminalFormat runsrun_id
agent.run.terminalAgent Completionsrun_id
job.completed, job.failed, job.canceledGeneration jobsjob_id

What should the default branch do?

Acknowledge and move on. Here is a complete route built on verifyWebhook, which refuses to run without a secret.

import { verifyWebhook } from "@sume-com/sdk";

export async function POST(request: Request) {
  const body = await request.text();
  const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET;
  if (!secret) return new Response("no secret", { status: 500 });

  const ok = await verifyWebhook({ body, headers: request.headers, secret });
  if (!ok) return new Response("bad signature", { status: 401 });

  const event = JSON.parse(body);
  switch (event.event) {
    case "format.run.terminal":
      console.log("run", event.run_id);
      break;
    case "job.completed":
    case "job.failed":
      console.log("job", event.job_id);
      break;
    default:
      break; // unknown event: acknowledge, do not 500
  }
  return new Response(null, { status: 204 });
}

Why not return a 500 for something unexpected?

Sume treats network errors and non-2xx responses as failures and retries until attempts are exhausted. A 500 on an event you simply have not coded yet is therefore a retry, up to 10 attempts per delivery, repeated for every new event of that type. Acknowledging it keeps your endpoint quiet while you decide whether to handle it.

Am I losing events if I ignore them?

No: an ignored event is still acknowledged, and delivery is never the only recovery path. Keep status_url polling available, and use Redeliver to re-send a real terminal event once you add a handler.

What about the webhook.test event from Send test?

Send test posts a dummy signed payload with event set to webhook.test, and no job_id or run id. A route that switches on event with a 204 default already handles it: the test succeeds, and nothing tries to look up a job that does not exist.

Should I log the ones I skip?

Yes, briefly, with the event name. Log it and return 204, so that when Sume adds an event type you see it in your logs rather than in a wall of retries. Return 2xx quickly after durably recording anything you keep, and do heavier work afterward.

Do I still need to verify before routing?

Yes. Verify the signature against the raw body first, then parse and route. An unrecognized event that fails verification is a 401; an unrecognized event that passes is a 204. Keeping those two branches separate means a forged request never reaches your router, and a genuine new event never looks like an attack.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume