Retool Workflow webhook needs X-Workflow-Api-Key: use a relay

Retool Workflows authenticate webhooks with an X-Workflow-Api-Key header or query parameter. Sume documents no custom delivery headers: relay after verifying.

4 min readSume
All posts

Verify the Sume signature in a small relay, then forward to the Retool Workflow URL with the X-Workflow-Api-Key header added there. Retool authenticates webhook events with that header or a workflowApiKey query parameter, and the Sume docs document no way to add custom headers to a delivery.

Sume facts are from the Webhooks and Verifying webhooks docs; the Retool text was read 2026-09-30.

How does Retool authenticate a webhook?

Retool lists two ways: the X-Workflow-Api-Key header, or the workflowApiKey query parameter. It advises the header, because a query parameter “can be logged since it is part of the URL.”

Auth options, read 2026-09-30. Sume: Webhooks.
OptionWhere the key goesConcern
Retool headerX-Workflow-Api-KeySume documents no custom delivery headers
Retool query parameterworkflowApiKey in the URLCan be logged, per Retool
RelayHeader added by your codeYou run one more endpoint

Can I put the key in the webhook_url?

The docs do not say whether Sume keeps a query string on webhook_url, and the URL you register will appear in your own settings and logs. A relay avoids both questions and lets you check the Sume signature before anything reaches Retool.

What does the relay do?

It reads the raw body, verifies, and forwards. Two environment values are required, and the handler refuses to run without them.

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

export default {
  async fetch(request: Request, env: Record<string, string>) {
    const secret = env.SUME_COM_WEBHOOK_SIGNING_SECRET;
    if (!secret || !env.RETOOL_URL || !env.RETOOL_KEY) {
      return new Response("not configured", { status: 500 });
    }
    const body = await request.text();
    if (!(await verifyWebhook({ body, headers: request.headers, secret }))) {
      return new Response("bad signature", { status: 401 });
    }
    const res = await fetch(env.RETOOL_URL, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "X-Workflow-Api-Key": env.RETOOL_KEY,
      },
      body,
    });
    return new Response(null, { status: res.ok ? 204 : 502 });
  },
};

Which id should the workflow dedupe on?

Use job_id for job.* events and request_id for run events. Return 502 when the forward fails so Sume retries.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume