Custom-mode webhooks can be signed with HMAC-SHA256 so your server can reject spoofed payloads. Opt in by passing enable_signature: true when creating the webhook — backward-compatible, off by default.
The signed string is "{timestamp}.{raw_body}", joined with a literal dot. Use the raw request body — do not re-serialize the JSON, whitespace differences will break the check.
Verify on your server
Reject requests where X-Linkup-Timestamp is more than ~5 minutes off from your current clock — protects against replay of an old captured payload.
Rotate the secret
POST /v2/webhooks/{webhook_id}/rotate-secret
Generates a fresh secret. The previous one is invalidated immediately, so deploy the new secret to your verifier before calling this. Returns the new secret in plaintext once.
Disable signing
DELETE /v2/webhooks/{webhook_id}/secret
Clears the secret. Subsequent POSTs go out without X-Linkup-Signature headers.
Notes
- Hosted-mode webhooks (SSE/polling) are not signed — they are already authenticated by your
api_key on the stream URL.
- Legacy V1 webhooks (
webhook_url on the account doc) are not signed. V2-only feature.
disconnection events are signed too when signing is enabled on the webhook.
GET /v2/webhooks returns signature_enabled: true|false. The secret itself is never returned by list/get — if you lose it, rotate.