GET /v1/webhooks/signing-secret returns 403: key needs account:read
Sume returns 403 insufficient_scope with required_scope account:read when a key cannot read the webhook secret. Scopes cannot be added, so mint a new key.

GET /v1/webhooks/signing-secret answers 403 insufficient_scope when the API key does not carry account:read, and the error details name the missing scope as required_scope. Scopes are fixed when a key is created, so the fix is a new key, not a patch.
Rotating the secret with POST /v1/webhooks/signing-secret/rotate needs account:write, according to the Verifying webhooks page.
What does the 403 look like?
The documented error example has code: "insufficient_scope", the message "This API key does not have permission to access this endpoint.", category: "auth", retryable: false, next_action: "authenticate", and details: { required_scope: "account:read" }.
The OpenAPI description adds that every self-service key minted from the dashboard carries the scope, so a 403 usually means an older or specially scoped key.
Why can't I add the scope to my key?
The Authentication page is blunt: scopes are fixed at creation, keys created before a scope existed do not carry it, and there is no API to patch scopes onto an existing key. The same rule explains 403 insufficient_scope on Format and Action runs.
Create a new key, move your service to it, and then revoke the old one.
| Route | Scope | Notes |
|---|---|---|
| GET /v1/webhooks/signing-secret | account:read | Returns secret, version, fingerprint, rotation window |
| POST /v1/webhooks/signing-secret/rotate | account:write | Old secret still verifies for 24 hours |
| POST /v1/webhooks/test-deliveries | account:write | Sends a dummy webhook.test payload |
Can I skip the API and read it in the dashboard?
Yes. The Webhooks tab of the dashboard (/dashboard/webhooks, then Reveal) shows the same secret. Dashboard access follows workspace permissions: the dashboard route in the repo refuses callers without the integrations-management capability, since reading the secret is enough to forge deliveries.
One secret covers both generation-job and run webhooks, so a single verifier works for both.
Confirm both sides hold the same secret
After you fetch the secret, compare its fingerprint with the x-sume-webhook-secret-fingerprint header on a delivery, which lets you check without pasting the secret anywhere.
curl -s https://api.sume.com/v1/webhooks/signing-secret \
-H "Authorization: Bearer $SUME_API_KEY" \
| jq '.data | {version, fingerprint, rotation}'What else can this route return?
The route also documents a 503 when per-recipient signing is not configured in the environment you call; in that case the API refuses rather than hand back a shared platform secret. A 401 means the key itself is missing or invalid.
Quick checklist
The points above reduce to a short list you can paste into a runbook.
- Confirm the key carries account:read before you call the route.
- Create a new key when a scope is missing; scopes cannot be patched.
- Store the secret as SUME_COM_WEBHOOK_SIGNING_SECRET.
- Compare fingerprints, not secrets, when a signature fails.
- Upgrade your verifier before rotating, because the header can carry two sume-v1 entries for 24 hours.
Sources
Related posts
More in Developers
- Sume webhook_url 400: localhost, http, credentials, alias clash
Why a Sume create call rejects webhook_url with 400 invalid_request: localhost, private IPs, http, credentials in the URL, or conflicting webhook_url aliases.
- Which Sume audio endpoint to call: TTS, STT, music, detach, timeline
A decision map for Sume's audio API: seven endpoints, what each takes in and returns, limits and list prices, and the order they chain in.
- Sume video tools: public URL or media import first? Per tool
Video captions takes a public HTTPS URL; trim, filter, inspect, frames, compose and detach need a workspace media.sume.com clip. A tool-by-tool input guide.
- Which voice does my avatar speak with? Check voice.status is ready
Sume TTS speaks in an avatar's voice when voice.status is ready. List avatars, check voice.status, then send avatar_id or avatar_handle on the TTS request.
Written by Sume