Developer docs
Integration recipe

HubSpot

Enrich a contact the moment it enters HubSpot

Endpoint
POST /api/v1/leads/enrich?mode=sync
Cost
1 enrichment credit per contact (shared with your dashboard quota)
Latency
20–40 s inline (sync mode); use async + webhook if you expect bursts

HubSpot workflow custom-code actions have a 20 s execution limit on Pro and 60 s on Enterprise. If your enrichments routinely exceed that, switch to the async pattern at the bottom of this page.

  1. Step 1

    Create a scoped API key

    In LeadIQ Pro → Settings → API, create a key labelled HubSpot workflow. Copy the liq_live_… secret once — it won't be shown again.
  2. Step 2

    Create custom contact properties in HubSpot

    In HubSpot → Settings → Properties → Contact properties, create:
    • leadiq_opportunity_score (Number)
    • leadiq_tags (Multiline text)
    • leadiq_summary (Multiline text)
    • leadiq_hiring_signals (Multiline text)
    • leadiq_opening_line (Single line text)
    Add more as you need them — each corresponds to a key from the enriched-field glossary.
  3. Step 3

    Build a workflow

    Automation → Workflows → Create workflow → Contact-based. Enrollment trigger:
    "Contact property Email is known AND Contact property Company is known." Add a Custom code action (requires Operations Hub Professional or above).
  4. Step 4

    Paste the custom code

    // HubSpot Workflow Custom Code — Node 18
    // Secrets: set LEADIQ_API_KEY as a workflow secret (don't inline it)
    exports.main = async (event, callback) => {
      const email = event.inputFields['email'];
      const company = event.inputFields['company'];
      const domain = email && email.includes('@') ? email.split('@')[1] : null;
      if (!domain || !company) return callback({ outputFields: {} });
    
      const resp = await fetch("https://leadiqpro.io/api/v1/leads/enrich?mode=sync", {
        method: "POST",
        headers: {
          "Authorization": "Bearer " + process.env.LEADIQ_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          lead: {
            email,
            name: event.inputFields['firstname'] + ' ' + event.inputFields['lastname'],
            title: event.inputFields['jobtitle'],
            company,
            domain,
          },
        }),
      });
    
      if (!resp.ok) {
        const retryAfter = resp.headers.get('retry-after');
        throw new Error('LeadIQ ' + resp.status + ' — retry-after=' + retryAfter);
      }
      const data = await resp.json();
      const e = data.enrichment || {};
    
      callback({
        outputFields: {
          leadiq_opportunity_score: e.opportunity_score,
          leadiq_tags: (e.tags || []).join(', '),
          leadiq_summary: e.company_overview?.summary || '',
          leadiq_hiring_signals: (e.intent_signals?.hiring_signals || []).join('\n'),
          leadiq_opening_line: e.engagement_playbook?.opening_line || '',
        },
      });
    };
  5. Step 5

    Map output fields to properties

    Under Output fields, add one row per custom property (leadiq_opportunity_score, leadiq_tags, …). After the custom-code action, add a Copy property value step mapping each output to the matching contact property.
  6. Step 6

    Test with one contact

    Toggle the workflow on, then enrol a single test contact. You should see the custom properties populate within ~40 s. If you get a 429 upstream_full, you've hit the platform-wide ceiling — retry in a few seconds (see the rate-limit reference).
  7. Step 7

    (Optional) Async pattern for higher volume

    For higher volume or longer-tail accounts, call the async endpoint without ?mode=sync, pass a callback_url pointing at a HubSpot webhook, and update the contact in the webhook handler. See the webhook payload shape on the overview page.