Skip to content

Rotate the signing secret

Issue a new signing secret for a webhook.

http
POST /v1/webhooks/{id}/rotate-secret

Requires a Full access API key.

Path parameters

ParameterTypeRequiredDescription
idstringYesThe webhook id.

Example

bash
curl -X POST "https://api.sendgrail.com/v1/webhooks/$WEBHOOK_ID/rotate-secret" \
  -H "Authorization: Bearer $SENDGRAIL_API_KEY"
js
const res = await fetch(
  `https://api.sendgrail.com/v1/webhooks/${id}/rotate-secret`,
  {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}` },
  },
);

const { signing_secret } = await res.json();

Response

The webhook, with its new signing secret:

json
{
  "object": "webhook",
  "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0",
  "name": "Production handler",
  "endpoint": "https://example.com/handler",
  "events": ["email.sent", "email.bounced"],
  "status": "enabled",
  "created_at": "2026-08-22T15:28:00+00:00",
  "signing_secret": "whsec_xxxxxxxxxx"
}

Rotating without dropping events

The moment you rotate, everything we send is signed with the new secret. But deliveries that were already in flight were signed with the old one, and they are still valid — so a handler that only knows the new secret will reject the last few events signed with the old one.

Rotate without a gap by accepting both for a short while:

  1. Call rotate, and store the new secret alongside the old one.
  2. In your handler, verify each event against the new secret; if that fails, try the old one. Accept the event if either matches.
  3. After a few minutes — long enough for anything in flight to have arrived — drop the old secret and verify against the new one alone.

See Verifying signatures for the verification code itself.

When to rotate

Rotate if the secret may have leaked — it appeared in a log you shared, a former teammate had it, it was committed to a repo. Rotating invalidates the old secret for all future events, so anyone holding it can no longer forge a request your handler will trust.

The secret is never recorded in your request logs — it comes back as [redacted] there — so a rotate is about your own copies of it, not ours.

Transactional email on your own domain.