HubSpot implementation guide
Enrich a HubSpot contact with TargetWise
Read a HubSpot contact, request missing work email or business phone fields, review the result and apply a safe update through the Contacts API.
Updated 10 September 2026 · Documentation and review policy
Start with a reviewed test contact.
- Create a named TargetWise workspace key.
- Use a HubSpot app credential with contact read and write permissions. Retrieve the contact by its record ID; keep the token in your backend secret store. The request and update shapes follow the HubSpot Contacts API.
- Confirm that the name and company domain identify this CRM contact. The fictional record below must be replaced with your own approved test record.
- Download targetwise-crm.mjs beside your Node.js script. It proposes changes by default.
Read, enrich and inspect the proposed update.
import { enrichCrmContact } from './targetwise-crm.mjs';
const outcome = await enrichCrmContact({
platform: 'hubspot',
recordId: process.env.CRM_CONTACT_ID,
identity: {
"first_name": "Alex",
"last_name": "Reed",
"company_domain": "example.com"
},
targetwiseKey: process.env.TARGETWISE_API_KEY,
crmToken: process.env.CRM_ACCESS_TOKEN,
includePhone: false,
apply: false,
});
// Inspect outcome.patch in your secure review interface.
// Log only status and request ID, not the contact values.
console.log(outcome.status, outcome.requestId);The helper reads the existing contact, skips a lookup when the requested fields are already filled, and calls POST /api/v1/contacts/enrich once. Phone retrieval is off by default. The lookup can consume your plan’s usage balance even when the CRM update is a dry run.
Keep trusted data and inspect unresolved fields.
| TargetWise field | HubSpot property | Write rule |
|---|---|---|
| data.work_email | Only when the existing value is empty and the returned identity was reviewed | |
| data.business_phone | phone | Only if phone was requested; preserve the current value |
| data.phone_type | Do not infer mobilephone | unknown is not a verified mobile classification |
| request_id | Your integration event log | Store for tracing without logging the result body |
{
"object": "enrichment_result",
"status": "partial",
"request_id": "illustrative-request",
"data": {
"work_email": "alex@example.com",
"business_phone": null
},
"unresolved_fields": [
"business_phone"
]
}A partial result may still contain the email you requested. A missing value never clears an existing CRM field. Add company enrichment as a separate, deliberate request when account context is needed; use reverse email lookup when the business email is your starting identifier.
Apply only after the test record passes review.
Set apply: true for your reviewed test run. The example re-reads the record and stops if its tracked fields changed. HubSpot updates are not an atomic fill-if-empty operation. Serialize writes for each contact in your integration and use a review or shadow-property workflow if other systems can edit the same fields concurrently.
- Test a missing email, an already-filled record, a partial response, no match and a record changed during the lookup.
- Stop on 400 or 401. Correct input or authentication. On 402, review balance. On 429, respect Retry-After and workspace limits; do not loop immediately.
- Store an integration event ID with the completed outcome. Check it before re-running the event; a repeated POST is not automatically free.
The downloadable mapping and orchestration example is covered by local fixture tests, including CRM HTTP requests. Run this checklist in your own CRM sandbox before enabling production writes. This guide does not install a native CRM connector.