{ "hubspot" :
Enrich HubSpot companies with Dutch registry data
Short answer
HubSpot has no KVK field and does not enrich from the Dutch trade register. You create custom properties on the Company object and fill them from a workflow webhook action, a coded action, or a private app listening to CRM webhooks. We do not ship a HubSpot app: you call our REST API from that middleware.
# write the enrichment back
PATCH "/crm/v3/objects/companies/512"
{
"properties": {
"name": "Acme B.V.",
"kvk_number": "68750110",
"sbi_code": "6201",
"vat_valid": "true"
}
}
The properties you create
HubSpot ships name, domain, city and numberofemployees. Those hold whatever sales typed or whatever HubSpot inferred from an email domain. Registry data does not belong in the same fields. Create your own properties with a clear provenance.
| Property | Type | From the API |
|---|---|---|
kvk_number | single-line text | kvkNumber |
statutory_name | single-line text | statutoryName |
legal_form | dropdown | legalForm |
sbi_code | single-line text | activities[].sbiCode where isMain |
vat_number | single-line text | vat.number |
vat_valid | boolean | vat.valid |
company_active | boolean | isActive |
enriched_at | date picker | time of the call |
enriched_at looks like overhead until someone asks how old the data is. Without a timestamp you cannot re-enrich selectively, so you re-enrich everything every month and walk straight into your quota.
The integration point: workflow, coded action or private app
There are three places the call can originate, and they differ in cost and in what they can handle.
Workflow with a webhook action. Enroll companies where kvk_number is known and enriched_at is empty. HubSpot POSTs to your endpoint, you perform the lookup and write back through the CRM API. Simple, and your logic lives in your own code.
Coded action. Runs inside HubSpot, no server of your own. Requires Operations Hub Professional and inherits that runtime’s execution limits. Fine for one lookup per record, wrong for a backfill.
Private app on CRM webhooks. Subscribe to company.propertyChange for kvk_number. Enrichment then happens the moment the number changes, on any tier. The payload is an array of events:
[
{
"subscriptionType": "company.propertyChange",
"objectId": 512,
"propertyName": "kvk_number",
"propertyValue": "68750110",
"occurredAt": 1755820800000
}
]
Process that array asynchronously. HubSpot batches events and expects a fast 2xx: a handler that blocks on our API during a spike runs into timeouts, and HubSpot then redelivers the same event.
Write back in batches, not per record:
POST /crm/v3/objects/companies/batch/update
with at most a hundred records per call. On our side that pairs with POST /v1/lookup/batch and a body of { "kvkNumbers": [...], "enrich": true }. Both sides stay inside their limits.
Where the input comes from
The KVK number has to enter the CRM somehow. Two routes:
- Your own form, with our widget attached to the input. The visitor searches by name, picks a company, and you submit the KVK number to the HubSpot Forms API.
- Manual entry by sales on the company record. That needs validation afterwards, because a hand-typed number is exactly as reliable as retyping implies.
What does not work: attaching the widget to an embedded HubSpot form that renders inside an iframe. Your script cannot reach that input. Check how your embed renders before promising autocomplete on a HubSpot form. If it is an iframe, build the form yourself and post to the Forms API.
Either way, write the number into a property that only your integration touches. A field edited by both sales and a sync turns into an argument about which value is correct, and the register always loses that argument to whoever typed last.
What actually breaks in production
Domain deduplication collapses your group structure. HubSpot uses the domain as the key for automatic association. A holding, its operating company and a franchise location often share one website. HubSpot ends up with one company where you have three KVK numbers. Key your own dedupe on kvk_number, not on domain, and accept that you sometimes want two HubSpot records on the same domain.
The workflow triggers itself. Enroll on “kvk_number is known”, then write properties back to the same record, and it re-enrolls on the next change. Turn re-enrollment off explicitly and filter on enriched_at is unknown.
A company returns 404. Deregistered entries leave the register. Treat NOT_FOUND as information rather than an error: set company_active to false and enriched_at to now, or your workflow retries it on every run.
The first backfill eats your monthly quota. Enriching five thousand companies is five thousand lookups. Filter down to the records where it actually changes something and spread the rest. HubSpot meters its own limit per ten seconds, so batches of a hundred records per call keep both sides calm.
For the wider approach to CRM enrichment there is a longer piece in CRM company data enrichment.
Frequently asked
Do you have a HubSpot marketplace app?
Which HubSpot tier do I need?
Which field should I key on?
Will enrichment overwrite what sales typed in?
Updated:
One call gives you the whole company
KVK data, the derived VAT number and a live VIES check, in a single request. The free plan covers 50 lookups a month and needs no card.
- 50
- free lookups a month
- 1
- request instead of three
- 0
- cards, contracts or sales calls