5 min read

Syncing Leads to HubSpot with AI

Create a Dwizi tool that allows your AI agents to insert new leads directly into your CRM.

Dw

Dwizi Team

Editorial

Syncing Leads to HubSpot with AI

We've all been there. You just had a fantastic call with a potential client. You took messy notes, scribbled down their budget, their timeline, and the names of the key decision-makers. You hang up the phone, feeling great.

And then you look at your CRM.

The dreaded empty form of HubSpot or Salesforce stares back at you. First Name. Last Name. Company. Lifecycle Stage. Source. The excitement of the sale evaporates, replaced by the drudgery of data entry. This "copy-paste tax" is where sales momentum goes to die.

But what if you didn't have to do it? What if you could just dump your raw notes—or the transcript of the call itself—into a chat window, and have an invisible assistant flawlessly organize it into your database?

The Problem: The "Intern" Gap

In the physical world, if you were a high-powered executive, you would hire an intern or an assistant. You'd hand them a stack of business cards and say, "Put these in the system." They would use their judgment to figure out which text is the phone number and which is the email.

Until recently, software couldn't do that. You had to format the data perfectly yourself.

AI Agents bridge this gap. They have the reasoning capabilities of a human assistant. They can read a messy transcript and say, "Ah, 'Alice from Acme' is the contact, and she mentioned a Q3 budget."

But an assistant who can read but cannot type is useless. We need to give the AI a keyboard. We need to give it a tool.

The Solution: A Deterministic CRM Tool

We will build a tool called create_hubspot_contact. This is not a "magic" tool that guesses what to do. It is a strict, typed interface that forces the AI to structure its thoughts before it touches your precious database.

The Implementation

We are modeling a real-world business process here. We want to capture specific fields, and we want to enforce specific rules (like valid lifecycle stages).

/**
 * Creates a new contact in HubSpot.
 * 
 * Description for LLM: "Call this tool when a user expresses interest. Extract email and name first."
 * This description acts as a prompt. It tells the LLM not just *how* to use the tool,
 * but *when*—"when a user expresses interest."
 */

// 1. The Shape of Business Data
// Notice the 'lifecycleStage' field. It's a Union Type.
// This is critical. It prevents the AI from inventing stages like "super_interested" 
// or "kinda_warm". It forces the AI to categorize the lead into one of YOUR buckets.
type Input = {
  email: string;
  firstName?: string;
  lastName?: string;
  lifecycleStage?: "lead" | "subscriber" | "customer";
};

export default async function createContact(args: Input) {
  // 2. The Gatekeeper
  // We check for the API token immediately. In Dwizi, these tokens are decrypted
  // only for the duration of this function call. They never touch the disk unencrypted.
  const token = Deno.env.get("HUBSPOT_ACCESS_TOKEN");
  if (!token) throw new Error("Missing HUBSPOT_ACCESS_TOKEN");

  const { email, firstName, lastName, lifecycleStage = "lead" } = args;

  // 3. The Payload Construction
  // We map the clean, typed input from the AI into the specific (and often ugly) 
  // format that the external API requires. The AI doesn't need to know that HubSpot 
  // expects 'firstname' in lowercase inside a 'properties' object. That's our job.
  const body = {
    properties: {
      email,
      firstname: firstName,
      lastname: lastName,
      lifecyclestage: lifecycleStage,
    },
  };

  // 4. The Handoff
  const res = await fetch("https://api.hubapi.com/crm/v3/objects/contacts", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  const data = await res.json();
  
  // 5. The Confirmation
  // We return just enough information for the AI to confirm success to the user.
  // "Success! I created contact ID 101."
  return {
    success: res.ok,
    id: data.id,
    created: data.createdAt,
    message: res.ok ? "Contact created" : data.message
  };
}

Why Typed Inputs Change Everything

The most important part of the code above isn't the fetch call—it's the type Input.

By defining lifecycleStage as "lead" | "subscriber" | "customer", you are effectively programming the AI's behavior. If the AI reads a transcript where someone says "I'm ready to buy!", it looks at your tool definition and thinks: "Okay, 'ready to buy' matches the concept of 'customer' best. I will select that option."

You are aligning the AI's fuzzy reasoning with your business's rigid requirements.

The Execution Story

Imagine you are using an AI agent to process support tickets. A ticket comes in: "Hi, my name is Alice Smith (alice@example.com). I love your product and want to upgrade to Enterprise."

Without the tool: The AI replies, "That's great, Alice! Someone will email you." (And then it forgets, and the lead is lost).

With the tool:

  1. The AI reads the email.
  2. It identifies "Alice Smith", "alice@example.com", and "Enterprise interest".
  3. It calls create_hubspot_contact with:
    {
      "firstName": "Alice",
      "lastName": "Smith",
      "email": "alice@example.com",
      "lifecycleStage": "lead"
    }
    
  4. The tool executes securely in Dwizi cloud.
  5. The contact appears in your HubSpot dashboard instantly.
  6. The AI replies to Alice: "I've connected you with our sales team. Expect an email shortly."

The data entry happened automatically, in milliseconds, with zero human effort. That is the power of an agent with hands.

Subscribe to Dwizi Blog

Get stories on the future of work, autonomous agents, and the infrastructure that powers them. No fluff.

We respect your inbox. Unsubscribe at any time.

Read Next

The Junior Dev (GitHub)

Automating the first 15 minutes of every bug report. How to build an agent that triages issues.

Read article