Thank you.
Some extra context: The Workflow tries to send contact data to my endpoint.
The following data is perfectly fine (csv):
First Name,Last Name,Contact owner,Contact unworked,Owner assigned date,Campaign of last booking in meetings tool,Contact priority,Likelihood to close,Currently in workflow,Number of Sales Activities,Member has accessed private content,Registered member,Country/Region Code,Create Date,Created by user ID,Email,Email Confirmed,Email Domain,Last Modified Date,Lifecycle Stage,Marketing contact status,Marketing contact status source name,Marketing contact until next update,Record source,Updated by user ID,Average Pageviews,Event Revenue,Latest Traffic Source,Latest Traffic Source Date,Latest Traffic Source Drill-Down 1,Latest Traffic Source Drill-Down 2,Number of event completions,Number of Pageviews,Number of Sessions,Original Traffic Source,Original Traffic Source Drill-Down 1,Original Traffic Source Drill-Down 2,Time First Seen,IP Country,Number of Form Submissions,Number of Unique Forms Submitted,"Date entered ""Lead (Lifecycle Stage Pipeline)""",Added To List On
g_test_2025-09-04_4_3,lastName_test2025-09-04_4_3,GB,TRUE,09-04-2025 16:26,,Low,1.32,FALSE,0,0,0,Sweden,09-04-2025 16:26,Björn Test,g.test+hs-wf-test_2025-09-04_4_3@test.io,,test.io,09-04-2025 16:26,Lead,Non-marketing contact,, --,CRM UI,GB,0,0,Offline Sources,09-04-2025 16:26,CRM_UI,userId:78453109,0,0,0,Offline Sources,CRM_UI,userId:78453109,09-04-2025 16:26,Sweden,0,0,09-04-2025 16:26,09-04-2025 16:26
But the following one is failing (csv):
First Name,Last Name,Contact owner,Contact unworked,Owner assigned date,Campaign of last booking in meetings tool,Contact priority,Likelihood to close,Currently in workflow,Number of Sales Activities,Member has accessed private content,Registered member,Country/Region Code,Create Date,Created by user ID,Email,Email Confirmed,Email Domain,Last Modified Date,Lifecycle Stage,Marketing contact status,Marketing contact status source name,Marketing contact until next update,Record source,Updated by user ID,Average Pageviews,Event Revenue,Latest Traffic Source,Latest Traffic Source Date,Latest Traffic Source Drill-Down 1,Latest Traffic Source Drill-Down 2,Number of event completions,Number of Pageviews,Number of Sessions,Original Traffic Source,Original Traffic Source Drill-Down 1,Original Traffic Source Drill-Down 2,Time First Seen,IP Country,Number of Form Submissions,Number of Unique Forms Submitted,"Date entered ""Lead (Lifecycle Stage Pipeline)""",Added To List On
g_test__Heikkilä__2025-09-04_4_1,lastName_test2025-09-04_4_1,GB,TRUE,09-04-2025 16:26,,Low,1.32,FALSE,0,0,0,Sweden,09-04-2025 16:26,Björn Test,g.test+hs-wf-test_2025-09-04_4_1@test.io,,test.io,09-04-2025 16:26,Lead,Non-marketing contact,, --,CRM UI,GB,0,0,Offline Sources,09-04-2025 16:26,CRM_UI,userId:78453109,0,0,0,Offline Sources,CRM_UI,userId:78453109,09-04-2025 16:26,Sweden,0,0,09-04-2025 16:26,09-04-2025 16:26
g_test__Göteborg__2025-09-04_4_2,lastName_test2025-09-04_4_2,GB,TRUE,09-04-2025 16:26,,Low,1.32,FALSE,0,0,0,Sweden,09-04-2025 16:26,Björn Test,g.test+hs-wf-test_2025-09-04_4_2@test.io,,test.io,09-04-2025 16:26,Lead,Non-marketing contact,, --,CRM UI,GB,0,0,Offline Sources,09-04-2025 16:26,CRM_UI,userId:78453109,0,0,0,Offline Sources,CRM_UI,userId:78453109,09-04-2025 16:26,Sweden,0,0,09-04-2025 16:26,09-04-2025 16:26
Importing them into the HS system is working fine. Setting up the workflow is also fine. Everything seems perfect. Just the header validation is failing for the second sample.
In PHP I have
$timestamp = $this->request->getHeader('X-HubSpot-Request-Timestamp');
$signature = $this->request->getHeader('X-HubSpot-Signature-v3');
$httpUri_decoded = str_replace(['%3A', '%2F', '%3F', '%40', '%21', '%24', '%27', '%28', '%29', '%2A', '%2C', '%3B'], [':', '/', '?', '@', '!', '$', "'", '(', ')', '*', ',', ';'], ($requestUri));
$requestBody = $this->request->getRawBody();
$preDataForSign = strtoupper($this->request->getMethod()) . $httpUri_decoded . $requestBody . $timestamp;
$signatureData = utf8_encode($preDataForSign);
$sigKey = '<MY_HS_SECRET>';
$hashSignature = base64_encode(hash_hmac(
'sha256',
$signatureData,
$sigKey,
true
));
if (hash_equals($originalSignature, $hashSignature)) {
return true;
}
return false;
Now I tried removing the method `strtoupper` call, as well as not using the utf8_encode call (especially since it is deprecated).
The crazy part is taht, for the first batch of example, the code is working, for the rest: not.