- To validate a request using the latest version of the HubSpot signature, use the
X-HubSpot-Signature-V3header and follow the associated instructions for validating the v3 version of the signature. - For backwards compatibility, requests from HubSpot also include older versions of the signature. To validate an older version of the signature, check the
X-HubSpot-Signature-Versionheader, then follow the associated instructions below based on whether the version isv1orv2.
Validate requests using the v1 request signature
If your app is subscribed to CRM object events via the webhooks API, requests from HubSpot will be sent with theX-HubSpot-Signature-Version header set to v1. The X-HubSpot-Signature header will be an SHA-256 hash built using the client secret of your app combined with details of the request.
To verify this version of the signature, perform the following steps:
- Create a string that concatenates together the following:
Client secret+request body(if present). - Create a SHA-256 hash of the resulting string.
- Compare the hash value to the value of the
X-HubSpot-Signatureheader:- If they’re equal then this request has passed validation.
- If these values do not match, then this request may have been tampered with in-transit or someone may be spoofing requests to your endpoint.
v1 request signature examples:
Python example
Ruby example
Node.js example
Java example
232db2615f3d666fe21a8ec971ac7b5402d33b9a925784df3ca654d05f4817de
Validate requests using the v2 request signature
If your app is handling data from a webhook action in a workflow, or if you’re returning data for a custom CRM card, the request from HubSpot is sent with theX-HubSpot-Signature-Version header set to v2. The X-HubSpot-Signature header will be an SHA-256 hash built using the client secret of your app combined with details of the request.
To verify this signature, perform the following steps:
- Create a string that concatenates together the following:
Client secret+http method+URI+request body(if present) - Create a SHA-256 hash of the resulting string.
- Compare the hash value to the signature.
- If they’re equal then this request has passed validation.
- If these values do not match, then this request may have been tampered with in-transit or someone may be spoofing requests to your endpoint.
- The URI used to build the source string must exactly match the original request, including the protocol. If you’re having trouble validating the signature, ensure that any query parameters are in the exact same order they were listed in the original request.
- The source string should be UTF-8 encoded before calculating the SHA-256 hash.
Example for a GET request
For aGET request, you’d need your app’s client secret and specific fields from the metadata of your request. These fields are listed below with placeholder values included:
- Client secret:
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy - HTTP method:
GET - URI:
https://www.example.com/webhook_uri - Request body:
""
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyyGEThttps://www.example.com/webhook_uri
After calculating a SHA-256 hash of the concatenated string above, the resulting signature you’d expect to match to the one in the header would be: eee2dddcc73c94d699f5e395f4b9d454a069a6855fbfa152e91e88823087200e
Example for a POST request
For aPOST request, you’d need your app’s client secret, specific fields from the metadata of your request, and a string representation of the body of the request (e.g., using JSON.stringify(request.body) for a Node.js service). These fields are listed below with placeholder values included:
- Client secret:
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy - HTTP method:
POST - URI:
https://www.example.com/webhook_uri - Request body:
{"example_field":"example_value"}
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyyPOSThttps://www.example.com/webhook_uri{"example_field":"example_value"}
After calculating a SHA-256 hash of the concatenated string above, the resulting signature you’d expect to match to the one in the header would be:9569219f8ba981ffa6f6f16aa0f48637d35d728c7e4d93d0d52efaa512af7900
After [SHA-ing] the signature, you could then compare the resulting expected signature to the one provided in the x-hubspot-signature header of the request:
The code snippets below details how you could incorporate v2 request validation for a GET request if you were running an Express server to handle incoming requests. Keep in mind that the code block below is an example and omits certain dependencies you might need to run a fully-featured Express service. Confirm that you’re running the latest stable and secure libraries when implementing request validation for your specific service.
v2 request signature examples:
Node.js example
Java example
Validate the v3 request signature
TheX-HubSpot-Signature-v3 header will be an HMAC SHA-256 hash built using the client secret of your app combined with details of the request. It will also include a X-HubSpot-Request-Timestamp header.
When validating a request using the X-HubSpot-Signature-v3 header, you’ll need to
- Reject the request if the timestamp is older than 5 minutes.
- In the request URI, decode any of the URL-encoded characters listed in the table below. You do not need to decode the question mark that denotes the beginning of the query string.
| Encoded value | Decoded value |
|---|---|
%3A | : |
%2F | / |
%3F | ? |
%40 | @ |
%21 | ! |
%24 | $ |
%27 | ' |
%28 | ( |
%29 | ) |
%2A | * |
%2C | , |
%3B | ; |
- Create a utf-8 encoded string that concatenates together the following:
requestMethod+requestUri+requestBody+ timestamp. The timestamp is provided by theX-HubSpot-Request-Timestampheader. - Create an HMAC SHA-256 hash of the resulting string using the application secret as the secret for the HMAC SHA-256 function.
- Base64 encode the result of the HMAC function.
- Compare the hash value to the signature. If they’re equal then this request has been verified as originating from HubSpot. It’s recommended that you use constant-time string comparison to guard against timing attacks.
POST request if you were running a backend service to handle incoming requests. Keep in mind that the code blocks below omit certain dependencies you might need to run a fully-featured backend service. Confirm that you’re running the latest stable and secure libraries when implementing request validation for your specific service.