HMAC validation provides an additional layer of security for webhook endpoints. It ensures that incoming webhook events originate from EasyPost and have not been altered during transmission.
EasyPost’s HMAC validation process verifies the integrity of the request body, prevents replay attacks using a timestamp tolerance window, and returns clear error codes when validation fails. This dual-layer approach ensures both authenticity and freshness of webhook requests.
Note: The recommended way to validate webhooks is by using validate_webhook() in EasyPost client libraries. This function accepts the webhook secret, headers, and request body, and automatically verifies the signature. The following sections describe the full validation process for teams that need to understand or implement the logic manually.
HMAC Validation Process
Each webhook event includes headers with a timestamp, request path, and HMAC signature. EasyPost validates these headers in two stages before forwarding the request to the application:
- Timestamp Validation: Confirms that the request was sent within an acceptable time window, preventing replay attacks.
- HMAC Signature Validation: Verifies message integrity by recalculating the signature and comparing it to the one included in the request.
A request must pass both stages to be processed. If validation fails, EasyPost returns a 401 Unauthorized response with a descriptive error message.
Timestamp Validation
Required Headers
-
x-timestamp: RFC 2822 formatted timestamp -
x-path: Request path used in signature calculation -
x-hmac-signature-v2: HMAC signature to validate
If any required header is missing, the request is rejected with 401 Unauthorized.
Timestamp Parsing
Timestamps must be in RFC 2822 format, for example:
"Tue, 19 Aug 2025 20:37:09 -0000"
The parsing extracts each component using a regular expression, converts the month name into a numeric value, and parses the timezone offset from the ±HHMM format. All components must be present and valid.
Error Responses
- Invalid format:
401 Unauthorized: Invalid timestamp format - Unknown month:
401 Unauthorized: Invalid month in timestamp - Invalid timezone:
401 Unauthorized: Invalid timezone in timestamp
Unix Timestamp Conversion
Parsed timestamps are converted into Unix time (seconds since epoch) and normalized to UTC:
utc_timestamp = local_timestamp - timezone_offset_seconds
Timezone offsets are calculated as:
offset_seconds = (hours * 3600) + (minutes * 60)
if timezone_sign == "-": offset_seconds = -offset_seconds
Validation Rules
- Requests older than the configured tolerance are rejected.
- Requests more than 30 seconds in the future are rejected.
- Requests within the valid time window are accepted.
Configuration
- Tolerance: 1 minute (60 seconds)
- Clock Skew Allowance: 30 seconds for future timestamps
- Configurable Range: 0-60 minutes
HMAC Signature Validation
Request Data
The following elements are concatenated to form the string-to-sign:
- Timestamp (
x-timestamp) - HTTP method (uppercase, such as
GET,POST,PUT,DELETE) - Request path (
x-path) - Request body (raw JSON; empty string if none)
Example
"Tue, 19 Aug 2025 20:37:09
-0000POST/webhook/test{\"event\":\"tracker.created\"}"
Note: There are no delimiters between components. The concatenation must be exact. Empty bodies must be represented as an empty string.
Signature Calculation
The HMAC signature is generated as follows:
- Create an HMAC instance using the shared secret key and SHA-256.
- Update the instance with the string-to-sign.
- Finalize and convert the result into a lowercase hexadecimal string.
Requirements
- Algorithm: HMAC-SHA256 (RFC 2104 compliant)
- Encoding: UTF-8
- Use a cryptographically secure HMAC implementation.
- Protect the secret key in memory and storage.
- Ensure consistent UTF-8 encoding for string operations.
Signature Comparison
The calculated signature is compared against the value in the x-hmac-signature-v2 header:
"hmac-sha256-hex=<character-hex-string>"
- Comparison is case-insensitive.
- Exact matches are required.
- Timing-safe comparison is recommended to prevent timing attacks.
Parameters
Validation rules require secret_key to be non-empty and timestamp_tolerance_minutes to be within the allowed range. Any other parameters are rejected.
secret_key string (required)
Shared secret used for HMAC calculation
timestamp_tolerance_minutes integer
Maximum allowed age of a request in minutes.