Integration · HubSpot CRM

HubSpot enrichment, 37 fields per company, synced in one click.

Connect HubSpot in 5 minutes and Kustiq writes 37 enriched fields onto every company and contact: 33 company properties, 4 contact properties, re-synced on every enrichment run.

Last updated 2026-05-16Last verified · 2026-05-16v2.1 releaseChangelog
3 profiles a week, no cardOAuth 2.0 · revocableUS + EU infrastructurecrm.objects.companies.writeGDPR + DPA ready
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.

Connect, authorise, import, sync — about 5 minutes.

Open Integrations in your Kustiq workspace

~ 20 seconds

From 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 seconds

HubSpot 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 seconds

Pick 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 rows

Kustiq 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 rows

Click 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 seconds

Open 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 33 kustiq_* company properties on matched records.
  • crm.schemas.companies.read · check the kustiq_*property group exists and matches Kustiq’s expected schema on every sync.
  • crm.schemas.companies.write · create the kustiq_* 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 4 kustiq_* 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.

POST /webhooks/kustiq
// 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.

ERR_HS_401

OAuth token rejected by HubSpot #

The refresh token was revoked from HubSpot’s side. Reconnect from Settings → Integrations. Existing kustiq_* properties stay on records.

ERR_HS_403

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.

ERR_HS_409

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.

ERR_HS_429

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.

ERR_MATCH_LOW

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.

ERR_DOMAIN_EMPTY

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.

ERR_PROP_READONLY

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.

ERR_PORTAL_PAUSED

HubSpot portal in trial-paused state #

HubSpot freezes write access on expired trials. Reactivate the portal, then run the sync again.

ERR_WEBHOOK_SIG

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.

ERR_WEBHOOK_STALE

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.

ERR_QUOTA_FREE

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?
37 in total: 33 company + 4 contact properties. HowTo schema, FAQ JSON-LD, and the reference table above all match. If a number disagrees anywhere, file a doc bug.
Does Kustiq overwrite native HubSpot fields like industry or numberofemployees?
Kustiq only writes properties under the 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?
Six: 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?
The integration itself connects on every plan. The free tier enriches 3 profiles a week, so you can wire HubSpot end to end and verify the sync, then upgrade to Insight or Pro to enrich the rest of your portal.
How do I disconnect Kustiq from HubSpot?
Two paths. 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?
Where the rest of the platform lives: the primary database is Supabase (US East) with API workers on Hetzner in the EU. There is no per-workspace region picker. Refresh tokens are encrypted at rest and rotate whenever HubSpot issues a new one. Details at /trust.
What happens if HubSpot rate-limits the sync?
Kustiq paces writes under HubSpot’s 100-requests-per-10-seconds limit and batches updates 100 records at a time. A rate-limited batch is logged and skipped; re-run the sync once the limit clears. Writes are idempotent, so a re-run never duplicates.

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-hex X-Kustiq-Signature header the server emits. Free-tier copy unified to 3 profiles a week across hero, mid-CTA, final CTA, and JSON-LD. Pricing fixed in ERR_QUOTA_FREE (Insight 200 credits, Pro 800 credits). JSON-LD @id-graph linked to root #org and #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.
Ship it · Free tier

HubSpot, enriched on the next sync.

3 profiles a week, no card. The integration writes 33 company + 4 contact properties on the first run.