Multi-step form / wizard; Contacts API or Forms API?



We have a custom multi-step form for the sign-up process on our website.

The first page is the sign-up page (requires name, email and phone) which leads to multiple steps where more data are captured at each step.

Should I be creating multiple HubSpot forms one for each step? Or is Contacts API more sensible?

In the Contacts documentation, it says “If your contacts come through online forms, use the Form Submission endpoint.” However, in Forms documentation, it says do not use it for “updating contact properties.”

What is the recommended approach for our scenario?


Hi @captainmike,

I would recommend building the multi-step form outside of HubSpot then once they have completed the full form send everything over to HubSpot via the Forms API. This allows you to capture the cookie of the visitor and attach it to the contact record which will allow for better segmentation and analytics.

The note on the Forms API documentations is made to dissuade people from using that endpoint when integrating with a CRM which would flood a portal with Form Submissions that don’t reflect the reality of the situation. If someone fills out a form more than once, it is perfectly acceptable to use the Form Submission API to updated that contact record.



Hi Zack,

Thanks for the response. Given that users may drop off during the multi-step process, or logs out and come back to proceed with the process, I don’t feel this is the most ideal solution for us; we’d like HubSpot to have the latest data available at any point.

If we do call Forms API multiple times, once for each step, it sounds like this would potentially work?

On one of the steps, we ask the user to confirm the email address they filled in earlier in the step. However, it does not seem like Forms API allows you to update email address of an existing contact?


Hey @captainmike,

While you could do that, you could end up with a lot of form submissions in your portal that really represent one form submission. A way around that is store the information on your end then when there is an abandonment event (like there hasn’t been any new activity for 30 minutes, you send everything you have to HubSpot. I understand that’s more complicated but would better represent how many form submissions are actually taking place IMHO. As long as you are OK with the number of submissions coming in, feel free to a call for each step.

If you send HubSpot a new API call with the same cookie (the hutk as part of the hs_context field in the Forms API) HubSpot will deduplicate that and update the email address so you actually can update email address as long as it’s the same browser from before and they haven’t cleared their cookies in between.

Let me know if you have any additional questions.



Thanks, that’s all for now! :+1:


@captainmike what approach did you go with in the end?
We have a similar situation whereby we need to analytics that come with submitting a form via hs_context, but the app far better suits using the contacts API.

Essentially we have an embedded HTML5 quiz, with a form to begin with, then each slide the user clicks yes/no or some other answer, which need to update their contact record. At the moment everything is done via create or update contact by email endpoint - but this means we aren’t getting the surrounding analytics.

@zwolfson do you see any issues with the following approach for this?

  1. Name and email is submitted via hubspot forms api
  2. Next slide of the app and all subsequent ones use the create or update contact by email endpoint (

Problem I could see would be if a contact isn’t immediately created via forms api - is there any delay?

Thanks in advance


Hey @craigivemy,

It won’t be an issue if there is a delay in Form Submission processing since the create or update endpoint will take care of that. The only issue I could see here is the volume of requests. Sending one call for each step in a quiz could take you up against the daily API limit. Depending on your volume you might want to wait until someone finishes the quiz or abandons it to send the results to HubSpot.



Hi @zwolfson thanks for the quick reply. The quiz isn’t very long and whilst we are getting loads of people filling it out it would never take us near 40k per day or 10 per second, but thanks for the heads up.

Regarding the delay - let’s say there is one (do we have a definite answer on whether there is one?) for creating the contact via the form submissions endpoint, and therefore a few seconds later the contact is created instead by the create/update contact endpoint - what would happen when the form submission is processed? Would we still be missing the analytics as the contact api was responsible for creating the contact? What we are seeing now is loads of contacts with no surrounding analytics which we want to fix.




HI @zwolfson, just following on from my response yesterday - I had another question:

The HTML5 app loads in an iframe. Therefore I’d need to include the HubSpot tracking code within the iframe too, correct? And add the domain where the iframe is actually hosted to HubSpot tracking domains - is this all I’d need to do along with approach in earlier post in order to get all analytics?

Sorry - I could just test all of this if we owned the app but we don’t, we get sent the latest version to edit, so I want to get my approach 100% before requesting they send it over.


Hey @craigivemy,

There shouldn’t be a delay in form processing more than 1 second but it’s possible that under heavy loads it could get longer than that. Even if there is a delay, we’ll see when the form submission was and use that to determine the original source of the contact since that will have the earliest timestamp even if it wasn’t fully processed first. There shouldn’t be any risk of not seeing the analytics you are missing if you submit the form submission first.

If you include the quiz in the iframe the tracking code won’t know the true original source of the contact. a) does the quiz have to be loaded in an iframe? b) if it does, can you load the hutk value from the parent page into iframe, maybe via a url parameter of the iframe. That way it has the original value that we have the source tied to.



@zwolfson thanks for the info here, extremely helpful.

It doesn’t have to be in an iframe but i think if it isn’t it may restrict the functionality - there’s code in the of the iframe specific to the app and we don’t have access to the actual site. If we use the JS embed I can’t see how the iframe code is possibly going to run, I’ll reach out to Hype 3 (what the app has been built on).



@zwolfson suddenly had another thought. My server side script that will process the HubSpot form data will be on a different server to the one that contains the form - ie a different server to the app and where the HubSpot cookie is dropped.

Therefore surely I would need to somehow send that cookie data along with my form data, correct? I’ve never done this before and haven’t had any feedback on missing analytics.



Hey @craigivemy,

That’s not an issue, just make sure you send the cookie value over to the server that will be making the request to HubSpot. As long as all the information specified in the Forms API documentation is on the server that will be making the request, you’re good. To take an extreme example, you could send the information through a 100 different servers before it gets to HubSpot and it won’t make a difference. The key is making sure you capture the cookie dropped onto the user’s browser that you want to track. Once it’s captured, passing it along to another server is fine.



Thanks @zwolfson it was more that I’d never actually grabbed and sent the cookie from one server to another before, so was thinking perhaps I’d need to go back and update other sites. But after doing some research it seems cookies will be sent with AJAX requests by default, which thinking about it does make sense as they are just http requests after all.




@zwolfson apologies for yet another post on this but we are seeing some irregular behaviour.

I’ve altered the initial sign up for to use the HubSpot forms API. Unfortunately the app is still within an iframe - and on a different domain - however we have tried including the HubSpot tracking code in the head of the iframe.

Within the iframe I’m grabbing the HubSpot cookie with the following regex:

var match = document.cookie.match(new RegExp('hubspotutk' + '=([^;]+)'));
if (match) {
	window.hubspotCookie = match[1];
} else {
	window.hubspotCookie = '';

I also grab the client IP using jsonip api. Later, when the form is submitted, these bits of data are sent with the form field data to the forms api.

When I test this myself, the HubSpot cookie value is grabbed and sent with the AJAX request just fine. However, we aren’t seeing improved analytics, and therefore I’ve been logging each submission - the IP and hubspotutk cookie value - and only a couple of times does the cookie get sent, the rest of the time it’s blank.

I’m at a loss to explain this and any help would be much appreciated as we’re struggling here! Any more info I can give you just let me know.

Thanks in advance.


OK so I was overlooking the fact that the iframe won’t have the cookie, until next page load.

So…I’ve hidden a 2nd iframe within the main app iframe which checks for HubSpot cookie, if it’s not there it refreshes until it is - it’s invisible and does nothing else so nobody will know. This now seems to be working.

However, I’m thinking what if somebody already has a HS cookie, but haven’t yet converted. The tracking code within the app iframe will, presumably, generate a new HS cookie? So when they convert, previous data isn’t going to be associated with the new contact, am I correct @zwolfson?

So only way now would be to host the iframe on the same domain or subdomain so that the original cookie is shared with the inner iframes???


Hey @craigivemy,

I’m glad you got that part working, one alternative approach that sprang to mind as I read this was to load your iframe via a js embed code. That way you can load it programmatically and include the parent page’s hubspotutk value without a refreshing iframe. I haven’t tested that but just a thought.

If you want to, you can essentially ignore the information from the tracking code inside the iframe. When you send the information to HubSpot about that form submission you’ll use the cookie from the parent page (to make sure we capture the true original source). You are correct that the tracking code inside the iframe will drop a new cookie, however if you never associate that contact record with that cookie (what a form submission does and how we get the original source for each contact). If you want to associate both cookies with the same person, you could use our Tracking Code API (Identify Method) or use another Form Submission to do so. You might want to do this if someone could visit other pages where this multipart form is hosted and you want to know they are same person. To determine the original source, HubSpot will use the first known visit time of any of the cookies associated to that contact, which in this case (if I understand this correctly) should be the parent page domain.

However to keep things simple for now, you might just want to ignore the cookie value from the iframe and just focus on the one dropped by the tracking code on the parent page.



Hi @zwolfson thanks for this. What a nightmare, made so much worse by not having control over either the website code or where the iframe is hosted.

I’m going to push again to get the app directly onto the page not in an iframe. Say we go this way and a user drops directly onto the page (ie no HS cookie is present yet), the cookie is dropped, will document.cookie be able to grab it or will it need to be on a second page load? I’m mainly a backend developer, PHP in particular and in this case the cookie isn’t accessible until the next page load.

If we have to stick with an iframe, if I can get the iframe moved to the same domain/subdomain as the outer page, the cookie will be shared by default as I understand it?

Thanks again for your persistence here!


No problem @craigivemy, happy to help!

You won’t need a second page load. Here’s a little more detail on how the hubspot cookie gets dropped. First thing that happens is the embed code executes, and all that does it gets another javascript file (so that someone doesn’t have to update their embed code everytime we need to make a change). This new javascript file is delivered and executes and this is when the cookie gets dropped. That should only take a couple hundred milliseconds in more cases. So if we can wait until when we make the request to HubSpot about the submission document.cookie will have everything we need.

For the first page load, it won’t be available to the server because it gets dropped after the server has delivered the page so it wouldn’t part of the incoming request for a brand new visitor.

I’d have to test the scenario of loading an iframe into a page on the same domain/subdomain. You are correct that cookies are shared across the domain, I’m just worried that if they both happen pretty much simultaneously for a net new visitor two different cookies might get dropped before we can notice it’s the same person.



Hi @zwolfson that’s what I thought - in fact it makes sense now why we were seeing blank values - because the iframe didn’t have access to the cookie as was on a different domain.

Re: the iframe on same domain - I imagined just not including the HS tracking code on the iframe, and sending the one from the outer page through AJAX to forms API - in my head that would work!?