Skip to content
New storyWe built Skald to count visitors. It caught an AI-key thief.
SkaldEvery visit, truly told.

Lead attribution · HubSpot

HubSpot form attribution: contacts, meetings and won deals, beside your visits

HubSpot form attribution in Skald reads HubSpot's signed webhooks for new contacts, booked meetings and deals moved to closed won, and sets each beside the visits that came before.

HubSpot form attribution is tying each HubSpot contact, meeting or won deal to the source and pages that brought it. Skald takes them from your HubSpot app's signed webhooks (messages HubSpot sends the moment a contact, meeting or deal changes), or a workflow's Send a webhook action, and the visits from your server log.

30-day free trial · no card · we set it up within one working day · free to self-host

hubspot hook · sample · signature v3

A delivery that arrives on time

  1. methodPOST
  2. URI HubSpot calledhttps://analytics.example.com/traffic-hooks/example.com/hubspot
  3. raw body[{"subscriptionType":"contact.creation","objectId":101,…}]
  4. X-HubSpot-Request-Timestamp1792843200000 · 0 min old

HMAC-SHA256 with the app's client secret, in base64q8Zt1mWc0rX…=

X-HubSpot-Signature-v3q8Zt1mWc0rX…=

match Taken. The body is read only after this.

Sample delivery. Four parts, joined in this order, signed with the secret you paste from your HubSpot app. The signature is a check code made from those parts and the secret: change one character and it no longer matches.

[01] one delivery, five events

Which HubSpot events become leads? Three kinds count. The rest are answered and dropped.

A HubSpot app sends its events in a list, up to 100 in one delivery. Skald counts a new contact, a new meeting and a deal moved to closed won, and nothing else. Flip the values in this sample list and watch what Skald keeps in lead attribution.

[

{"subscriptionType": "contact.creation", "objectId": 101, …},

{"subscriptionType": "object.creation", "objectTypeId": , "objectId": 202, …},

{"subscriptionType": "deal.propertyChange", "propertyName": "dealstage", "propertyValue": , "objectId": 303, …},

{"subscriptionType": "contact.propertyChange", "propertyName": "email", "objectId": 101, …},

{"subscriptionType": "contact.creation", "objectId": 101, …}

]

press a value in orange

  1. contact:101Contact form · completedkept
  2. demo:202Demo · completedkept
  3. deal:303Deal · won, an outcome, not a leadkept
  4. ·not an event Skald countsignored
  5. contact:101the same contact againrepeat

Skald's answer

200 {"ok": true, "recorded": 3, "duplicates": 1}

Leads from this delivery: 2. The won deal is shown beside its lead, never as a new one.

Sample events. App events carry no visitor ID, so they are counted without a journey. More than 100 events in one delivery is refused with a 400.

[02] workflows name their action

Count a HubSpot workflow as a demo. Name it in skald_action.

A workflow's Send a webhook counts as a contact form. Add a property skald_action and Skald reads it in four steps. A name it cannot use falls back to contact, so nothing is lost. The same webhook can carry skv, the visitor ID, which joins the contact to its pages.

type a name

  1. 1 · trim, lower case"meeting"
  2. 2 · aliasesmeeting → demo
  3. 3 · a name Skald can keepyes
  4. 4 · action keptdemo · a lead, and a demo booked
{"objectId": 5501,
 "properties": {
   "skald_action": {"value": " Meeting "},
   "skv": {"value": "skv_Q2xhc3NpY0V4YW1wbGVJRA"},
   "utm_source": {"value": "linkedin"}, "utm_medium": {"value": "social"}}}

Sample workflow body. Kept as workflow:5501. A name is kept when it is 1 to 40 of a-z, 0-9, _ . : - and starts with a letter or digit.

[03] bad signature behind a proxy

HubSpot webhook says bad signature? Check the address Skald rebuilds.

HubSpot signs the full address it called. Behind a proxy (the web server in front of Skald, such as nginx, that passes each request on), Skald cannot see that address, so it rebuilds it: from its public address when the server has one set, else from the scheme and Host your proxy passes on. HubSpot decodes a few percent-escapes before signing, and Skald does the same. One character off and the signature fails.

break the proxy

HubSpot signed

https://analytics.example.com/traffic-hooks/example.com/hubspot

Skald rebuilds from X-Forwarded-Proto and Host

https://analytics.example.com/traffic-hooks/example.com/hubspot

match The signature can be checked.

Sample addresses. The public address is set by the person who runs your Skald server.

[04] set up and limits

Set up HubSpot lead tracking in four steps

One step in Skald, three in your HubSpot app.

  1. In your HubSpot app, open Auth and copy the Client secret.
  2. In Skald (Settings › This site › Integrations), paste it into Paste the signing secret from HubSpot in the HubSpot box and press Save secret.
  3. In your HubSpot app, open Webhooks and set the Target URL to the address from the HubSpot box.
  4. Subscribe to contact.creation, object.creation for meetings, and deal.propertyChange for dealstage.

Want a journey? Use a workflow's Send a webhook with the properties skv and utm_*. Cannot sign a workflow webhook on your HubSpot plan? Send it through Zapier or Make.

What it does not do

  • The secret is HubSpot's: you paste your app's client secret.
  • App events carry no visitor ID: counted, with no journey.
  • Your site fills a property named skv with skald('id'); the script tag stops at your page.
  • Journeys need visitor IDs, which are off by default.
  • Three event kinds only. Other changes to contacts, deals or tickets are answered and dropped.
  • A won deal is an outcome, shown beside its lead.
  • At most 100 events in one delivery.

questions

HubSpot form attribution FAQ

Straight answers, including what Skald does not do.

How does Skald count HubSpot events?

A new contact (contact.creation) counts as a Contact form. A new meeting (object.creation for meetings) counts as a Demo. A deal moved to closed won (deal.propertyChange for dealstage) counts as a won Deal, which is an outcome shown beside the lead.

Which secret does HubSpot use?

HubSpot signs its messages with your HubSpot app's client secret, so Skald cannot make one for it. Copy the client secret from your app's Auth page and paste it into Skald's HubSpot box. Skald never shows it again.

Can I use a HubSpot workflow instead of an app webhook?

Yes. A workflow's Send a webhook action, signed by the same app, counts as a Contact form. Add a property skald_action, for example demo in a meeting booked workflow, to count it as something else. If your HubSpot account cannot sign a workflow webhook, send it through Zapier or Make instead.

How does a HubSpot contact join the visitor's journey?

Through a workflow webhook that carries a property named skv with the visitor ID, which the page knows as skald('id'), and utm_* properties for the last touch. App subscription events carry no visitor ID, so they are counted without a journey.

Why does HubSpot say bad signature behind a proxy?

HubSpot signs the full public address it called. If your Skald sits behind a proxy, the person who runs your server may need to set Skald's public address so the two match.

Does the script tag see HubSpot meetings?

Yes. With the script tag on the page, a booking made inside a HubSpot meetings embed is counted as HubSpot meeting booked, with no webhook needed. Events, funnels and goals.

$ tail -f access.log | skald

See what AI does with your site. Free for 30 days.

No card. We set up hosted Skald for you within one working day, or you self-host it free. Every line of your log, sorted and added up.

Start free trial Live demo