Validating Properties AND Form Fields through the HubSpot Forms API

More and more often, embedded HubSpot forms are failing to load on browsers with strict tracking and privacy protection. On enterprise level websites, this is simply unacceptable. But that’s a fight for another day.

Now we find ourselves converting countless forms to use the HubSpot API for form submissions, which presents many challenges. Specifically, the https://api.hubapi.com/marketing/v3/forms/ (to get the forms and fields) and https://api.hsforms.com/submissions/v3/integration/secure/submit/ (to submit the form) endpoints.

The biggest challenge is validation. The forms can have validation rules. The properties associated with the fields can have validation rules. And you guessed it. Those don’t come supplied when you query the form. Those require 2 additional API requests to get properties and property validations.

So, we make 4 API requests to get the form, get the properties, get the property validation rules, and then finally submit the form. Obviously, we cache where we can.

I’m curious what others have attempted to not only handle front-end but also back-end validation before submitting via the Forms API. There are a tremendous amount of conditions to account for.

It suuuuuuure would be helpful if the Forms API submissions (which is now legacy? but there’s no v4? or is there?) would send a response back about which fields failed to validate and why, but nope. You get a 200 whether they pass or not.

So we are just blindly trying to validate based on conditions after we retrieve those and match them with the form fields. There’s no SDK or library to account for these? No payload responses with messaging? Nothing?

Hey @squatchcreative,

Thanks for bringing this to the community! I appreciate the detailed write up and feedback. While we wait for others from the community to chime in – here are a few resources that may help clarify what the APIs currently expose:

The submission documentation still points to the v3 legacy endpoint and notes that skipValidation is deprecated. I’d love to hear how others are this:

Hey @Alex_Boissonneault, @SteveHTM, @Anton – do you have any suggestions for @squatchcreative on this?

Thank you!!

Sam, Community Manager

Hey @squatchcreative, thanks for the loop-in @stassey!

The silent 200 is actually documented behavior, and it flips the whole problem: the secure/authenticated endpoint intentionally skips validation. That was the whole point of the June 2023 amendment, authenticated submissions are treated as “the integration already validated this, accept fields as-is.” So you’re using the one endpoint that will never tell you what failed.

Flip it around: submit from your backend to the plain v3 endpoint instead:

POST https://api.hsforms.com/submissions/v3/integration/submit/{portalId}/{formGuid}

No token needed, and it does validate against the form definition and returns a 400 with an errors array - errorType + message per field (REQUIRED_FIELD, INVALID_EMAIL, FIELD_NOT_IN_FORM_DEFINITION, etc.). That’s the validation feedback you were looking for, it just lives on the endpoint nobody expects. The trade-off is you can only submit fields that exist on the form definition, which for a straight embedded-form replacement is exactly what you want anyway.

So the mental model is: plain endpoint = “HubSpot, validate this and tell me why it failed”, secure endpoint = “I’ve validated it, just take it.” Use secure only when you need to push fields that aren’t on the form.

On your other questions: no, there’s no v4. v3 submissions being filed under “legacy” docs is just doc reshuffling, it’s still the current supported path (only the forms management API got the refresh). And no official SDK that stitches form fields + property validation rules together, your 4-call cache-and-merge approach is unfortunately state of the art. One tip there: sync the form definitions + validation rules on a schedule or via webhook into your own store instead of fetching per render, so runtime is just one call, the submit.

Worth dropping the “return property-rule failures in the submission response” ask on the Ideas board too, that gap is real.

Thank you @Alex_Boissonneault for this very detailed response. But… I suppose the gap is still real like you said because:

  1. This doesn’t validate against connected property definitions, just form field definitions
  2. This doesn’t address any front-end validation support like an embedded form provides and we are still on our own to ping the API, get them, try to match them, and build the whole validation UX

Or is that an inaccurate assessment?

And even though you mentioned

The trade-off is you can only submit fields that exist on the form definition…

I still got a 200 at the insecure endpoint when passing the “page_uri”, “page_name”, and “hutk” so it would show the Conversion Page, but the submission had notices like “This value wasn’t passed to the Contact record. ‘hutk’ isn’t a Contact property.” Or is that also an inaccurate interpretation of what you said?

Thanks again!

Fair pushback, you’re right on both, with one fix.

On validation: yep, the gap is real, and here’s why it can’t work the way we want, the CRM write happens after the 200 comes back. Property rules do throw proper 400s now (since mid-2023), but only on direct CRM writes. Forms process async, so failures land as those “wasn’t passed to the record” notices instead of in the response. Definitely worth an Ideas board post.

On hutk / page_uri / page_name, those shouldn’t be in fields[] at all. They go in the context object:

json

{
  "fields": [ ... ],
  "context": {
    "hutk": "...",
    "pageUri": "https://...",
    "pageName": "..."
  }
}

That fixes your Conversion Page attribution and the notices go away. (The nuance: non-form fields that are CRM properties get a 400, ones that aren’t get silently dropped with a notice, you hit the second.)

For the validation UX, what’s worked for me: the rule types are a small closed set (min/max, range, regex, alpha/numeric), so compile form definition + property rules into one JSON Schema or Zod schema per form, cache it server-side, refresh nightly or on a property-change webhook. Same schema drives front-end UX and your backend check, and runtime is one call, the submit.

And if real per-field errors ever matter more than form analytics: upsert via the CRM API (proper 400s with rule details), fire the form submit just for attribution. Ugly but works.

Thanks again for the response. This thread has been very helpful.

I appreciate your notes about the context vs. form data. I was sending it all to my backend handler and not separating correctly when building the payload.

We have different ideas about small haha. This is a big list. With many intricacies. Just the PHONE_NUMBER_WITH_EXPLICIT_COUNTRY_CODE goes down a rabbit hole of REGEX, formatting, etc. that benefits from a separate JS library like libphonenumber-js just to mimic what HubSpot does to validate and how it displays to a user like a regular form embed.

But, I’m doing something similar to what you suggested. Pinging the forms once a day and caching them. Pinging the properties and the property rules once a day and caching them. Then some secondary functions that scan the forms, their fields, and match them to the properties and property validations to my cached list of forms. I’m actually outputting some JSON in the data attributes of the form rule definitions and property definitions of each field when the form renders so my front end can just dynamically check them.

Bigger picture, the Forms API is going to get leaned on more and more as HubSpot form embeds fail to render for strict privacy tracking. That’s the whole reason I’m here, rebuilding the forms and trying to emulate the UX as best as possible. I absolutely believe an SDK or changes to the API are necessary to account for things like property validations. That ruleset could change. The arguments each ruleType takes could change. Right now, it’s practically hard-coded into my app and that is not a comfortable place to be.

Thank you again for your insight.

Totally resonate with the frustration on custom form emulation. The disconnect between client-side UX expectations and HubSpot’s asynchronous backend form validation is brutal, especially for phone number formatting and email domains.

What ended up saving sanity on our end was running a hybrid approach: hitting the CRM v3 Contact search/validation endpoint via our backend proxy first to catch format mismatches synchronously, and then submitting through the Forms API purely for attribution and campaign workflows. It adds an extra hop, but it prevents the silent drops.