Webhook Signatures
Relevant source files
The following files were used as context for generating this wiki page:
Webhook Signatures provide a security mechanism to verify that incoming requests are sent by Resend and have not been tampered with during transit. The system uses HMAC-SHA256 signatures and specific HTTP headers to authenticate the payload against a shared signing secret.
The verification process ensures the request originates from a trusted source, contains a valid timestamp within a defined tolerance window, and matches the expected cryptographic hash. This protects your application from replay attacks and unauthorized data injection.
Sources: src/WebhookSignature.php:1-65, src/Service/Webhook.php:97-100
Signature Verification Logic
The WebhookSignature class implements the core verification logic. It extracts identifiers, timestamps, and signatures from the request headers and compares them against a locally generated signature.
Verification Workflow
The following diagram illustrates the step-by-step process used to verify a webhook request:
The verification process requires three specific headers: svix-id, svix-timestamp, and svix-signature.
Sources: src/WebhookSignature.php:9-20, tests/WebhookSignature.php:20-24
Signature Generation
To generate the expected signature, the system concatenates the message ID, the timestamp, and the raw request payload, separated by dots. It then calculates an HMAC-SHA256 hash using the decoded signing secret and encodes the result in Base64.
Sources: src/WebhookSignature.php:48-55, tests/Fixtures/Webhook.php:114-117
Components and Data Structures
Required Headers
The verification logic specifically looks for Svix-compatible headers in the request array.
| Header | Description |
|---|---|
svix-id | Unique identifier for the webhook message. |
svix-timestamp | Unix timestamp of when the message was sent. |
svix-signature | One or more versioned signatures (e.g., v1,signature_hash). |
Sources: src/WebhookSignature.php:14-17, tests/Fixtures/Webhook.php:120-123
Signing Secret Handling
The system supports secrets provided with or without the whsec_ prefix. Before use, the prefix is removed and the remaining string is Base64 decoded.
Sources: src/WebhookSignature.php:57-68, tests/WebhookSignature.php:36-42
Timestamp Tolerance
Verification includes a tolerance check to prevent replay attacks. By default, the system allows a 300-second (5-minute) difference between the message timestamp and the current server time.
Sources: src/WebhookSignature.php:73-90, tests/WebhookSignature.php:51-68
Implementation Classes
WebhookSignature Class
The WebhookSignature class contains static methods to perform the cryptographic operations.
Sources: src/WebhookSignature.php:7-91
Webhook Service
The Resend\Service\Webhook class provides a high-level verify method that acts as a wrapper for the WebhookSignature::verify implementation.
Sources: src/Service/Webhook.php:97-100
Error Handling
If verification fails, the system throws a WebhookSignatureVerificationException. Common failure reasons include:
- Missing required headers.
- Invalid timestamp format.
- Timestamp outside the tolerance window (too old or too new).
- No signatures in the
svix-signatureheader match the expected v1 signature.
Sources: src/Exceptions/WebhookSignatureVerificationException.php:7-9, src/WebhookSignature.php:19, 75, 83, 87, 42
Usage Example
To verify a webhook in a PHP application, you pass the raw request body, the headers, and your signing secret to the verify method.
$resend = Resend::client('re_123456789');
$verified = $resend->webhooks->verify(
$request->getContent(), // Raw payload
$request->getHeaders(), // Array of headers
'whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw'
);
Sources: src/Service/Webhook.php:97-100, tests/Service/Webhook.php:137-142
Webhook Signatures ensure the integrity and authenticity of events delivered by Resend, allowing you to process email.sent, email.delivered, and other events securely.
Sources: src/Webhook.php:13, tests/Fixtures/Webhook.php:9-10