On this page · 9 sections
Overview
The Kustiq HubSpot integration runs B2B enrichment on every HubSpot company and contact in your portal. It writes classification, fit and churn scores, buying and pain signals, detected tools, funding stage, and contact role data straight into HubSpot properties so your filters, lists, and workflows pick them up without any manual export.
Two surfaces, one pipeline. Companies get 33 enriched properties: segment, vertical, account tier, churn risk, growth signal, fit score, funding stage, buying and pain signals, and 24 more. Contacts get 4 enriched properties: kustiq_email_confidence, kustiq_contact_role, kustiq_contact_department, kustiq_contact_seniority. That is 37 writeable fields total. Counts match the HowTo schema, the FAQ schema, and the property reference below, in that order.
With Kustiq, a company record refreshes every time you run a sync. With raw HubSpot, the same record stays whatever the rep typed at create time.
Setup, in 5 minutes
Six steps. HubSpot-side OAuth, no API key paste, no IP allowlist. Refresh token encrypted at rest.
Open Integrations in your Kustiq workspace
~ 20 secondsFrom the dashboard, go to Settings → Integrations. Find the HubSpot card, click Connect. You stay signed into Kustiq the whole time.

Authorise the OAuth consent screen
~ 40 secondsHubSpot opens its consent dialog. Pick the portal, review the six scopes (crm.objects.companies.read, crm.objects.companies.write, crm.schemas.companies.read, crm.schemas.companies.write, crm.objects.contacts.read, crm.objects.contacts.write), click Connect app. HubSpot redirects back to Kustiq and the integration card flips to Connected.

Choose what to import
~ 30 secondsPick a HubSpot company list or all companies. Kustiq imports name, domain, and the HubSpot record ID, then matches each row against the Kustiq profile graph. Empty domains are skipped, not enriched.

Confirm the matched profiles
~ 1 minute for 100 rowsKustiq shows the match table: HubSpot name on the left, Kustiq profile on the right. Matching is by domain; rows with an empty or free-mail domain stay unmatched instead of being guessed. You confirm and the import queue starts.

Run the first sync
~ 2 minutes for 1,000 rowsClick Sync now. Kustiq writes 33 company properties and 4contact properties to every matched record, in batches of 100, respecting HubSpot’s 100-req/10-sec rate limit. Progress shows per-batch status and skipped reasons.

Verify in HubSpot
~ 30 secondsOpen any synced company in HubSpot. The right sidebar shows a Kustiq section with all 33 company properties, prefixed kustiq_*. The 4 contact properties land on the contact card, same prefix. Run Sync now again anytime to push fresh enrichment.

Property reference
Every property is namespaced kustiq_* so it never collides with anything you already have. 33 company + 4 contact = 37 enriched properties. Counts match the HowTo + FAQ schemas. If a number disagrees somewhere, this section wins.
Company properties
33 fields- kustiq_segmenttext
- kustiq_verticaltext
- kustiq_account_tiertext
- kustiq_churn_risktext
- kustiq_growth_signaltext
- kustiq_confidencenumber
- kustiq_fit_scorenumber
- kustiq_icp_tiertext
- kustiq_icp_scorenumber
- kustiq_sales_prioritynumber
- kustiq_funding_stagetext
- kustiq_buying_signalstextarea
- kustiq_pain_signalstextarea
- kustiq_competitorstextarea
- 19 more (company context, market intelligence, outreach fields). Written on every sync into the same
kustiq_*namespace.
Contact properties
4 fields- kustiq_email_confidencetext
- kustiq_contact_roletext
- kustiq_contact_departmenttext
- kustiq_contact_senioritytext
These are the only contact-side writes Kustiq performs. No name, no email, no phone is overwritten. Email confidence is read-only output, never input.
Connect your HubSpot in 5 minutes.
Free tier: 3 profiles a week, no card. The full property reference writes on the first sync.
Permissions and scopes
Kustiq requests six HubSpot scopes, no more. You can revoke any of them from HubSpot → Settings → Integrations without contacting support.
crm.objects.companies.read· read native HubSpot company fields (name, domain, industry, numberofemployees) for matching against the Kustiq profile graph.crm.objects.companies.write· write the 33kustiq_*company properties on matched records.crm.schemas.companies.read· check thekustiq_*property group exists and matches Kustiq’s expected schema on every sync.crm.schemas.companies.write· create thekustiq_*property group on first connect, once per portal.crm.objects.contacts.read· read contact name, email, associations so contact role and seniority can be attributed to the right person.crm.objects.contacts.write· write the 4kustiq_*contact properties (email confidence, role, department, seniority). No native contact field is ever overwritten.
Kustiq does not request files, tickets, e-commerce, deals, or any conversation scope. If your IT review asks for a written list of what we touch, the six lines above are the answer.
Webhooks
Kustiq sends a webhook to your endpoint every time a company or contact is enriched. Each request is signed with HMAC-SHA256 over the raw body, using a shared secret you generate per workspace.
The signature lives in the X-Kustiq-Signature header as a bare 64-character hex digest. The body has a top-level timestamp field (unix seconds) for replay protection. Reject any request whose computed signature does not match in constant time. Reject any request whose body timestamp is older than 5 minutes.
// Express handler. Use express.raw({ type: "application/json" }). const crypto = require("crypto"); const SECRET = process.env.KUSTIQ_WEBHOOK_SECRET; function verify(req) { const sig = req.headers["x-kustiq-signature"] || ""; if (sig.length !== 64) return false; const mac = crypto .createHmac("sha256", SECRET) .update(req.body) .digest("hex"); const ok = crypto.timingSafeEqual( Buffer.from(mac, "hex"), Buffer.from(sig, "hex"), ); if (!ok) return false; // Replay check: body has top-level "timestamp" (unix seconds). const body = JSON.parse(req.body); return Date.now() / 1000 - body.timestamp < 300; }
Retry policy: a non-2xx response or no response inside 10s triggers one retry 5 seconds later. After that the event is dropped; every attempt is logged with status code and latency.
Sync rules
- Cadence. On demand. Every Sync now run rewrites all matched records. On Pro, Kustiq also refreshes churn signals from HubSpot once a day.
- Direction. Properties flow one way, Kustiq to HubSpot. On Pro, Kustiq reads deal and contact-activity counts daily to power churn scoring; it never reads email content.
- Conflict. Kustiq only writes
kustiq_*properties. Native HubSpot fields (industry,numberofemployees) are read for matching, never overwritten. - Match key. Domain, normalized (lowercase, www and email prefixes stripped). Rows without a usable domain are skipped, never guessed.
- Rate limit. 100 requests / 10 seconds, batched 100 records at a time. Rate-limited batches are logged; re-running the sync retries them.
- Disconnect. Revoking from HubSpot stops new writes.
kustiq_*properties stay on records until you delete the property group.
Troubleshooting
Twelve known states. Each has a stable anchor so you can link a teammate to the exact row.
OAuth token rejected by HubSpot #
The refresh token was revoked from HubSpot’s side. Reconnect from Settings → Integrations. Existing kustiq_* properties stay on records.
Missing scope crm.objects.companies.write #
The connecting user is not a HubSpot Super Admin and the portal blocked the scope. Ask a Super Admin to reconnect, or grant the scope to the user role.
Property group kustiq_* already exists with conflicting types #
Someone created kustiq_vertical manually with a type Kustiq does not manage. Delete the manual property, run sync again. Kustiq recreates the group with the types it owns.
Rate limit hit, batch skipped #
Another integration is consuming HubSpot’s limits. Kustiq logs the failed batch and keeps going. Re-run the sync once the limit clears; writes are idempotent, so nothing duplicates.
Company domain is free-mail or generic #
The HubSpot row has no domain or a generic one (gmail, hotmail) Kustiq refuses to match on. Add the real company domain on the record, or drop the row from the synced list.
Empty domain on company record #
Kustiq skips matching when the HubSpot domain field is blank. Bulk-set the domain in HubSpot, then re-run sync.
Custom kustiq_* property locked as read-only #
A workflow toggled the property to read-only. Unlock from Settings → Properties, or let Kustiq manage the entire kustiq_* group.
HubSpot portal in trial-paused state #
HubSpot freezes write access on expired trials. Reactivate the portal, then run the sync again.
Webhook signature mismatch #
Most likely the body was parsed as JSON before signing was checked, or the secret on your side drifted from Kustiq’s. Verify on the raw body bytes (no whitespace re-encoding). Secrets are shown once at creation; if drift is suspected, delete and recreate the endpoint from the dashboard to issue a fresh one.
Webhook body timestamp older than 5 minutes #
Server clock drift. Sync your endpoint host with NTP. Kustiq’s body timestamp field is always in UTC unix seconds.
Free tier hit weekly profile limit #
Free workspaces enrich 3 profiles a week. Upgrade to Insight ($39/mo) for 200 credits, or Pro ($119/mo) for 800 credits.
FAQ
How many properties does Kustiq write to HubSpot?
Does Kustiq overwrite native HubSpot fields like industry or numberofemployees?
kustiq_* namespace. Native HubSpot fields are read during matching, never modified. If you want to mirror a Kustiq value into a native field, build a HubSpot workflow.What scopes does the integration need?
crm.objects.companies.read, crm.objects.companies.write, crm.schemas.companies.read, crm.schemas.companies.write, crm.objects.contacts.read, crm.objects.contacts.write. The full list and what each one does is in the Permissions section.Can I use the integration on the free tier?
How do I disconnect Kustiq from HubSpot?
Settings → Integrations → Kustiq → Uninstall. From Kustiq: Settings → Integrations → HubSpot → Disconnect. Kustiq deletes its stored tokens immediately on disconnect; uninstalling from HubSpot revokes the grant on HubSpot’s side. Existing kustiq_* properties stay on records until you delete the property group.Where is the data stored?
What happens if HubSpot rate-limits the sync?
Changelog
- 2026-05-16 · v2.1 Pair-lock production-ready audit. Scope list corrected to six (
crm.objects.companies.read/write,crm.schemas.companies.read/write,crm.objects.contacts.read/write) to match the actual OAuth grant. Webhook verifier snippets rewritten to match the bare-hexX-Kustiq-Signatureheader the server emits. Free-tier copy unified to 3 profiles a week across hero, mid-CTA, final CTA, and JSON-LD. Pricing fixed inERR_QUOTA_FREE(Insight 200 credits, Pro 800 credits). JSON-LD @id-graph linked to root#organd#site. - 2026-05-08 · v2.0 Property count clarified to 33 + 4 = 37 across docs, JSON-LD, and OG. Webhook section adds verification snippets. Six setup-step screenshots wired to HowToStep schema. New “Last verified” badge.
- 2026-03-08 · v1.0 Initial release.
HubSpot, enriched on the next sync.
3 profiles a week, no card. The integration writes 33 company + 4 contact properties on the first run.