How to Rotate a RelayLink Webhook Secret

RelayLink webhook secrets are replaced, not edited: create and test a second subscription, handle overlap duplicates, cut over, then retire the old one.

4 min read

The receiver's whsec_ secret may be exposed, copied into the wrong store, or due for a routine credential change. RelayLink has no in-place rotate control. The safe operation is a replacement: bring up a second subscription and secret, prove it, then retire the first.

Expect both subscriptions to fire during the overlap. This is not a zero-duplicate procedure.

Prepare a second receiver identity

Give the replacement a distinct name and, preferably, a distinct receiver route:

old: /hooks/relaylink/r_old -> old secret
new: /hooks/relaylink/r_new -> new secret

The route selects one expected secret before the receiver reads the body. That is safer than trying every customer secret until one verifies and makes the migration visible in logs without exposing the URL.

Prepare the new route, secret-store entry, durable inbox namespace, monitoring, and downstream queue before you depend on it. The route token is sensitive routing context, but it is not authentication; the handler must still verify RelayLink's HMAC and signed timestamp.

RelayLink's creation check allows at most five active webhook subscriptions. Keep the migration at five or fewer throughout. If all five slots are occupied, disable or remove an obsolete receiver before creating the replacement. If the old receiver is the only one you can disable, you cannot overlap it with the replacement; plan a cutover gap and reconcile the inbox afterward. Even with overlap, requests and failures can straddle the change, so promise neither zero downtime nor zero duplicate callbacks.

Create and capture the replacement secret

In /account/webhooks, create a second named subscription pointing at the prepared route. RelayLink displays the full secret once. Store it immediately in the receiver's credential store and associate it with that route and subscription name.

Do not copy the value into deployment output, a ticket, chat, source control, or a command history retained by shared tooling. Keep only a safe internal reference in operational records.

The old subscription remains active. Do not remove its secret yet.

Prove the new signature path

Send a test event from the new subscription's account-page control. Verify the complete public path:

  1. The new route receives webhook.test.
  2. It reads the exact raw body before parsing.
  3. It verifies HMAC-SHA256 over "{timestamp}.{body}" with only the new secret.
  4. It rejects a stale timestamp and mismatched header/body identifiers.
  5. It durably claims the delivery id and queues the test result.
  6. It returns 2xx within the timeout.

Confirm that using the old secret against the new route fails. Also confirm that routine logs contain neither secret, signature, full route, nor raw body.

A test event has no package id, so the receiver must branch on the authenticated event before applying package-specific validation.

Deduplicate two different kinds of repeat

RelayLink creates one delivery per active subscription. During overlap, the same package produces:

  • one delivery id for the old subscription;
  • another delivery id for the new subscription.

Within each subscription, retries retain that subscription's delivery id. Use (subscription context, delivery_id) as the transport idempotency key.

Across the two subscriptions, delivery id cannot suppress the duplicated business effect because the values are intentionally different. Use an atomic business key such as:

(RelayLink account or rotation group, event, package_id)

Store the originating subscription and delivery id beside that claim for diagnosis. The old and new callbacks should both be authenticated and acknowledged, while only the winner starts the package workflow or posts the notification.

Do not use package_id globally. Scope it to the recipient account or the internal rotation group, and include the event so future event types cannot collide. Do not call the migration zero-duplicate: two legitimate callbacks arrive, and your atomic claim is what prevents two downstream effects.

Cut over, then retire the old subscription

After a successful test, let real traffic prove the new subscription. Confirm package callbacks verify, enter the queue, and fetch through the intended user's separate MCP credential. Move dashboards and operational references to the new receiver.

Then disable the old subscription. Disabling abandons its pending and retrying deliveries and stops future packages from being queued to it. It cannot retract a POST already in flight.

During a bounded drain window, keep the old route and secret able to authenticate late requests and pass them through the same atomic transport and package-level deduplication. Once that window closes, remove the old route and delete its secret from the receiver store. Delete the old RelayLink subscription when you no longer need its account-page diagnostic record.

If the new receiver fails after cutover, fix it and use its test event. If you roll back instead, disable or remove the replacement before turning the old subscription back on so the active set remains within five. Turning one back on clears its consecutive-failure count but does not replay abandoned deliveries. Reconcile missed work with check_inbox.

What rotation does not change

The webhook secret authenticates notification POSTs only. It does not fetch packages. The worker first requires mcp_url to match the RelayLink endpoint pinned to the account, then uses that account's separate OAuth token or named API key. The fetched package remains third-party content.

Rotating the webhook secret also grants no permission to send. An agent may prepare a response after fetching, but any RelayLink delivery still waits for explicit human approval through the normal preview-and-approve path.

Frequently asked questions

Can I rotate a RelayLink webhook secret in place?
No. Create a second named subscription to receive a new secret, prove it works, move operational reliance to it, and then disable or delete the old subscription.
Will old and new subscriptions use the same delivery id during overlap?
No. Each subscription gets a different delivery id for the same package. Deduplicate retries by delivery id, and deduplicate the overlap's business effect by an account-scoped event and package key.
Does re-enabling the old subscription replay abandoned deliveries?
No. Re-enabling clears its failure count and permits future events; it does not replay deliveries abandoned when the subscription was disabled.