Get the Sume webhook secret with GET /v1/webhooks/signing-secret

Read the workspace webhook secret with an account:read key and load it as SUME_COM_WEBHOOK_SIGNING_SECRET without printing it. A Python deploy step.

4 min readSume
All posts

Sume webhooks are signed with a secret that is derived for your workspace. It is not a shared platform value. You can reveal it on the Webhooks tab of the dashboard, or read it from GET /v1/webhooks/signing-secret with an API key that has the account:read scope. The docs say to store it as SUME_COM_WEBHOOK_SIGNING_SECRET, the same name that the delivery worker uses.

When the API route helps

A dashboard copy is fine for one service. The API route fits a deploy script that provisions several receivers, or a rotation check that must run unattended. Job webhooks and run webhooks share this one secret, so a single receiver setting covers both.

Scopes

A key without account:read receives a 403. Create a separate key with that scope for the deploy step, so that the key your application runs with can be narrower. Keep the secret out of logs and build output. The fingerprint is the safe thing to print, since it identifies a secret without revealing it.

Where the secret and its fingerprint appear (read 2026-10-04)
PlaceWhat it shows
Dashboard Webhooks tab (Reveal)The secret and its fingerprint
GET /v1/webhooks/signing-secretThe secret (needs account:read)
x-sume-webhook-secret-fingerprint headerThe fingerprint of the secret that signed the delivery
webhook_delivery.signing_secret_fingerprintThe same fingerprint on the receipt

Deploy step

The script writes the secret to a file that the receiver loads, with owner-only permissions, and prints nothing sensitive. The response is an object named data with scope, version, fingerprint and secret, and the secret is 64 hex characters.

import json, os, stat, sys, urllib.request

req = urllib.request.Request("https://api.sume.com/v1/webhooks/signing-secret",
                             headers={"x-api-key": os.environ["SUME_ADMIN_KEY"]})
with urllib.request.urlopen(req, timeout=15) as resp:
    body = json.load(resp)

secret = body["data"]["secret"]
if len(secret) < 32:
    sys.exit("unexpected signing secret length")

path = "/etc/myapp/sume-webhook.env"
with open(path, "w") as f:
    f.write(f"SUME_COM_WEBHOOK_SIGNING_SECRET={secret}\n")
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
print("wrote", path, "fingerprint", body["data"]["fingerprint"])

After rotation

During a rotation the signature header carries one entry for each live secret, newest first. Accept the delivery if any sume-v1= entry matches, and refresh the stored secret once the old one is retired.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume