KosmoKrator

productivity

HubSpot Lua API for KosmoKrator Agents

Agent-facing Lua documentation and function reference for the HubSpot KosmoKrator integration.

25 functions 12 read 13 write Bearer token auth

Lua Namespace

Agents call this integration through app.integrations.hubspot.*. Use lua_read_doc("integrations.hubspot") inside KosmoKrator to discover the same reference at runtime.

Agent-Facing Lua Docs

This is the rendered version of the full Lua documentation exposed to agents when they inspect the integration namespace.

HubSpot — Lua API Reference

Overview

The HubSpot integration provides 26 tools for managing contacts, companies, deals, tickets, associations, engagements, and CRM metadata. All calls go through app.integrations.hubspot.<tool_name>({ ... }) and return a Lua table.

Authentication

HubSpot uses a Private App Access Token (format: pat-...) with Bearer token authentication. The token is configured once in the integration settings and sent automatically on every request — you never pass it in Lua calls.

Create a token: HubSpot → Settings → Integrations → Private Apps → Create a private app

Object Types

HubSpot CRM objects are referenced by type string throughout the API:

Type StringObject
contactsContact
companiesCompany
dealsDeal
ticketsTicket

These type strings are used in associations, pipelines, and properties endpoints.

Pagination

List endpoints (list_deals, list_owners, list_forms) use cursor-based pagination. Pass after from a previous response to fetch the next page:

-- First page
local page1 = app.integrations.hubspot.list_deals({ limit = 50 })

-- Next page
if page1.after then
  local page2 = app.integrations.hubspot.list_deals({ limit = 50, after = page1.after })
end

The after field is nil when there are no more results.

Common Properties

HubSpot uses internal property names. The most common ones:

ObjectProperty NameDescription
ContactfirstnameFirst name
ContactlastnameLast name
ContactemailEmail address
ContactphonePhone number
ContactcompanyCompany name
CompanynameCompany name
CompanydomainWebsite domain
DealdealnameDeal name
DealamountDeal amount
DealpipelinePipeline ID
DealdealstageStage ID within pipeline
DealclosedateExpected close date
Dealhubspot_owner_idAssigned owner ID
TicketsubjectTicket subject/title
TicketcontentTicket body/description
Tickeths_pipelineTicket pipeline ID
Tickeths_pipeline_stageTicket pipeline stage ID

Use list_properties to discover all available properties for any object type.


Contacts

app.integrations.hubspot.create_contact(...)

Create a new contact in HubSpot CRM. Returns the contact’s HubSpot ID and properties.

local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  last_name = "Smith",
  email = "[email protected]",
  phone = "+1-555-0123",
  company = "Acme Corp"
})
-- contact.id          → "12345"
-- contact.properties  → { firstname = "Jane", lastname = "Smith", email = "[email protected]", ... }

Pass custom properties via the properties table:

local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  email = "[email protected]",
  properties = {
    jobtitle = "CTO",
    city = "San Francisco",
    lifecyclestage = "lead"
  }
})
ParameterTypeRequiredDescription
first_namestringnoContact first name (maps to firstname)
last_namestringnoContact last name (maps to lastname)
emailstringnoContact email address
phonestringnoContact phone number
companystringnoContact company name
propertiesobjectnoAdditional custom properties as key-value pairs

At least one property is required.


app.integrations.hubspot.get_contact(...)

Retrieve a HubSpot contact by ID. Optionally specify which properties to return.

local contact = app.integrations.hubspot.get_contact({
  id = "12345"
})
-- contact.id          → "12345"
-- contact.properties  → { firstname = "Jane", email = "[email protected]", ... }

Fetch only specific properties to reduce response size:

local contact = app.integrations.hubspot.get_contact({
  id = "12345",
  properties = { "firstname", "lastname", "email" }
})
ParameterTypeRequiredDescription
idstringyesHubSpot contact ID
propertiesarraynoList of property names to include

app.integrations.hubspot.update_contact(...)

Update an existing HubSpot contact. Only the properties you pass are changed.

local result = app.integrations.hubspot.update_contact({
  id = "12345",
  properties = {
    phone = "+1-555-9999",
    jobtitle = "VP Engineering"
  }
})
-- result.id          → "12345"
-- result.properties  → { phone = "+1-555-9999", jobtitle = "VP Engineering", ... }
ParameterTypeRequiredDescription
idstringyesHubSpot contact ID
propertiesobjectyesProperties to update as key-value pairs

app.integrations.hubspot.search_contacts(...)

Search HubSpot contacts with filters. Pass a query string for text search or structured filter groups.

local results = app.integrations.hubspot.search_contacts({
  query = "[email protected]",
  properties = { "firstname", "lastname", "email" },
  limit = 10
})
-- results.results  → { { id = "12345", properties = { ... } }, ... }

Search with structured filters:

local results = app.integrations.hubspot.search_contacts({
  properties = { "firstname", "lastname", "email", "lifecyclestage" },
  limit = 25,
  filter_groups = {
    {
      filters = {
        {
          property_name = "lifecyclestage",
          operator = "EQ",
          value = "marketingqualifiedlead"
        }
      }
    }
  }
})
ParameterTypeRequiredDescription
querystringnoText search across contact properties
propertiesarraynoProperty names to include in results
limitintegernoMax results to return
filter_groupsarraynoStructured filter groups for advanced search

app.integrations.hubspot.delete_contact(...)

Permanently delete a HubSpot contact by ID. This action cannot be undone.

local result = app.integrations.hubspot.delete_contact({
  id = "12345"
})
-- result.id      → "12345"
-- result.deleted → true
ParameterTypeRequiredDescription
idstringyesHubSpot contact ID

app.integrations.hubspot.create_or_update_contact(...)

Create or update a contact matched by email. If a contact with that email exists, it is updated; otherwise a new one is created. The response includes an action field indicating what happened.

local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  last_name = "Smith",
  phone = "+1-555-0123",
  company = "Acme Corp"
})
-- result.id       → "12345"
-- result.action   → "created"  (or "updated")
-- result.properties  → { firstname = "Jane", email = "[email protected]", ... }

Include custom properties:

local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  properties = {
    jobtitle = "CTO",
    city = "Berlin"
  }
})
ParameterTypeRequiredDescription
emailstringyesEmail address used for lookup and matching
first_namestringnoContact first name
last_namestringnoContact last name
phonestringnoContact phone number
companystringnoContact company name
propertiesobjectnoAdditional custom properties as key-value pairs

Companies

app.integrations.hubspot.create_company(...)

Create a new company in HubSpot CRM.

local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com"
})
-- company.id          → "67890"
-- company.properties  → { name = "Acme Corp", domain = "acme.com", ... }

With custom properties:

local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com",
  properties = {
    industry = "TECHNOLOGY",
    city = "New York",
    description = "A technology company"
  }
})
ParameterTypeRequiredDescription
namestringnoCompany name
domainstringnoCompany website domain
propertiesobjectnoAdditional custom properties as key-value pairs

At least one company property is required.


app.integrations.hubspot.get_company(...)

Retrieve a HubSpot company by ID.

local company = app.integrations.hubspot.get_company({
  id = "67890"
})
-- company.id          → "67890"
-- company.properties  → { name = "Acme Corp", domain = "acme.com", ... }

With specific properties:

local company = app.integrations.hubspot.get_company({
  id = "67890",
  properties = { "name", "domain", "industry" }
})
ParameterTypeRequiredDescription
idstringyesHubSpot company ID
propertiesarraynoList of property names to include

app.integrations.hubspot.update_company(...)

Update an existing HubSpot company.

local result = app.integrations.hubspot.update_company({
  id = "67890",
  properties = {
    industry = "FINANCE",
    city = "London"
  }
})
-- result.id          → "67890"
-- result.properties  → { industry = "FINANCE", city = "London", ... }
ParameterTypeRequiredDescription
idstringyesHubSpot company ID
propertiesobjectyesProperties to update as key-value pairs

app.integrations.hubspot.search_companies(...)

Search HubSpot companies with filters. Pass a query string for text search or structured filter groups.

local results = app.integrations.hubspot.search_companies({
  query = "Acme",
  properties = { "name", "domain", "industry" },
  limit = 10
})
-- results.results  → { { id = "67890", properties = { ... } }, ... }
ParameterTypeRequiredDescription
querystringnoText search across company properties
propertiesarraynoProperty names to include in results
limitintegernoMax results to return
filter_groupsarraynoStructured filter groups for advanced search

Deals

app.integrations.hubspot.create_deal(...)

Create a new deal in HubSpot CRM.

local deal = app.integrations.hubspot.create_deal({
  dealname = "Enterprise License Deal",
  amount = "50000",
  pipeline = "default",
  dealstage = "appointmentscheduled",
  closedate = "2026-06-30"
})
-- deal.id          → "99999"
-- deal.properties  → { dealname = "Enterprise License Deal", amount = "50000", ... }

With custom properties:

local deal = app.integrations.hubspot.create_deal({
  dealname = "Partner Deal",
  amount = "25000",
  properties = {
    deal_type = "newbusiness",
    description = "Partner referral"
  }
})
ParameterTypeRequiredDescription
dealnamestringnoDeal name
amountstringnoDeal amount
pipelinestringnoPipeline ID or internal name
dealstagestringnoDeal stage ID or internal name
closedatestringnoExpected close date (ISO 8601 or milliseconds)
propertiesobjectnoAdditional custom properties as key-value pairs

At least one deal property is required.


app.integrations.hubspot.get_deal(...)

Retrieve a HubSpot deal by ID.

local deal = app.integrations.hubspot.get_deal({
  id = "99999"
})
-- deal.id          → "99999"
-- deal.properties  → { dealname = "Enterprise License Deal", amount = "50000", ... }

With specific properties:

local deal = app.integrations.hubspot.get_deal({
  id = "99999",
  properties = { "dealname", "amount", "dealstage", "pipeline" }
})
ParameterTypeRequiredDescription
idstringyesHubSpot deal ID
propertiesarraynoList of property names to include

app.integrations.hubspot.update_deal(...)

Update an existing HubSpot deal. Commonly used to move deals through pipeline stages.

local result = app.integrations.hubspot.update_deal({
  id = "99999",
  properties = {
    dealstage = "closedwon",
    amount = "55000",
    closedate = "2026-04-05"
  }
})
-- result.id          → "99999"
-- result.properties  → { dealstage = "closedwon", amount = "55000", ... }
ParameterTypeRequiredDescription
idstringyesHubSpot deal ID
propertiesobjectyesProperties to update as key-value pairs

app.integrations.hubspot.list_deals(...)

List deals with cursor-based pagination.

local page = app.integrations.hubspot.list_deals({
  limit = 50,
  properties = { "dealname", "amount", "dealstage", "closedate" }
})
-- page.results  → { { id = "99999", properties = { ... } }, ... }
-- page.total    → 50
-- page.after    → "eyJuZXh0IjoiMTAwIn0"  (cursor for next page, nil if last page)

Fetch the next page:

if page.after then
  local page2 = app.integrations.hubspot.list_deals({
    limit = 50,
    after = page.after,
    properties = { "dealname", "amount", "dealstage" }
  })
end
ParameterTypeRequiredDescription
limitintegernoMax deals to return (default 10, max 100)
afterstringnoPagination cursor from previous response
propertiesarraynoProperty names to include in results

Tickets

app.integrations.hubspot.create_ticket(...)

Create a new ticket in HubSpot CRM.

local ticket = app.integrations.hubspot.create_ticket({
  subject = "Login page not loading",
  content = "Customer reports that the login page returns a 500 error since yesterday.",
  hs_pipeline = "0",
  hs_pipeline_stage = "1"
})
-- ticket.id          → "55555"
-- ticket.properties  → { subject = "Login page not loading", content = "...", ... }

With custom properties:

local ticket = app.integrations.hubspot.create_ticket({
  subject = "Billing inquiry",
  content = "Customer wants to upgrade their plan.",
  properties = {
    priority = "HIGH",
    source_type = "PHONE"
  }
})
ParameterTypeRequiredDescription
subjectstringyesTicket subject / title
contentstringnoTicket body content / description
hs_pipelinestringnoPipeline ID for the ticket
hs_pipeline_stagestringnoPipeline stage ID for the ticket
propertiesobjectnoAdditional custom properties as key-value pairs

app.integrations.hubspot.get_ticket(...)

Retrieve a HubSpot ticket by ID.

local ticket = app.integrations.hubspot.get_ticket({
  id = "55555"
})
-- ticket.id          → "55555"
-- ticket.properties  → { subject = "Login page not loading", content = "...", ... }

With specific properties:

local ticket = app.integrations.hubspot.get_ticket({
  id = "55555",
  properties = { "subject", "content", "hs_pipeline_stage", "priority" }
})
ParameterTypeRequiredDescription
idstringyesHubSpot ticket ID
propertiesarraynoList of property names to include

app.integrations.hubspot.update_ticket(...)

Update an existing HubSpot ticket. Commonly used to change ticket status/pipeline stage.

local result = app.integrations.hubspot.update_ticket({
  id = "55555",
  properties = {
    hs_pipeline_stage = "4",
    content = "Issue resolved. Fix deployed in v2.3.1."
  }
})
-- result.id          → "55555"
-- result.properties  → { hs_pipeline_stage = "4", content = "...", ... }
ParameterTypeRequiredDescription
idstringyesHubSpot ticket ID
propertiesobjectyesProperties to update as key-value pairs

Associations & Metadata

app.integrations.hubspot.create_association(...)

Associate two HubSpot CRM objects. Both objects must already exist.

local result = app.integrations.hubspot.create_association({
  from_type = "contacts",
  from_id = "12345",
  to_type = "companies",
  to_id = "67890",
  association_type = "contact_to_company"
})
-- result.from_type        → "contacts"
-- result.from_id          → "12345"
-- result.to_type          → "companies"
-- result.to_id            → "67890"
-- result.association_type → "contact_to_company"
-- result.created          → true

Associate a deal to a company:

app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = "99999",
  to_type = "companies",
  to_id = "67890",
  association_type = "deal_to_company"
})

Associate a ticket to a contact:

app.integrations.hubspot.create_association({
  from_type = "tickets",
  from_id = "55555",
  to_type = "contacts",
  to_id = "12345",
  association_type = "ticket_to_contact"
})

Common association types:

  • contact_to_company
  • company_to_contact
  • deal_to_company
  • deal_to_contact
  • ticket_to_contact
  • ticket_to_company
ParameterTypeRequiredDescription
from_typestringyesSource object type (contacts, companies, deals, tickets)
from_idstringyesSource object ID
to_typestringyesTarget object type
to_idstringyesTarget object ID
association_typestringyesAssociation type name (e.g., contact_to_company)

app.integrations.hubspot.list_associations(...)

List all associations from an object to a specific target type.

local result = app.integrations.hubspot.list_associations({
  from_type = "contacts",
  from_id = "12345",
  to_type = "companies"
})
-- result.from_type  → "contacts"
-- result.from_id    → "12345"
-- result.to_type    → "companies"
-- result.results    → { { id = "67890", type = "contact_to_company" }, ... }

List deals associated with a company:

local result = app.integrations.hubspot.list_associations({
  from_type = "companies",
  from_id = "67890",
  to_type = "deals"
})
ParameterTypeRequiredDescription
from_typestringyesSource object type
from_idstringyesSource object ID
to_typestringyesTarget object type to find associations for

app.integrations.hubspot.list_owners(...)

List HubSpot CRM owners (users). Useful for assigning owners to contacts, deals, and tickets.

local result = app.integrations.hubspot.list_owners({})
-- result.results  → {
--   { id = "101", email = "[email protected]", first_name = "Sarah", last_name = "Jones", user_id = 201 },
--   { id = "102", email = "[email protected]", first_name = "Mike", last_name = "Chen", user_id = 202 },
-- }

Paginate through all owners:

local page = app.integrations.hubspot.list_owners({ limit = 100 })
if page.after then
  local page2 = app.integrations.hubspot.list_owners({ limit = 100, after = page.after })
end
ParameterTypeRequiredDescription
limitintegernoMax owners to return (default 100)
afterstringnoPagination cursor from previous response

app.integrations.hubspot.create_engagement(...)

Create a note, task, or meeting engagement in HubSpot CRM.

Create a note:

local note = app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Customer called to discuss pricing for enterprise plan.</p>",
  owner_id = "101"
})
-- note.id          → "77777"
-- note.type        → "notes"
-- note.properties  → { hs_note_body = "...", hubspot_owner_id = "101", ... }

Create a task:

local task = app.integrations.hubspot.create_engagement({
  type = "tasks",
  body = "Send follow-up proposal to the client",
  owner_id = "101",
  properties = {
    hs_task_subject = "Follow-up proposal",
    hs_task_status = "NOT_STARTED",
    hs_task_priority = "HIGH"
  }
})

Create a meeting:

local meeting = app.integrations.hubspot.create_engagement({
  type = "meetings",
  body = "Quarterly review meeting",
  timestamp = "2026-04-15T10:00:00",
  owner_id = "101",
  properties = {
    hs_meeting_title = "Q2 Review",
    hs_meeting_start_time = "2026-04-15T10:00:00",
    hs_meeting_end_time = "2026-04-15T11:00:00"
  }
})
ParameterTypeRequiredDescription
typestringyesEngagement type: "notes", "tasks", or "meetings"
bodystringnoBody content (HTML for notes, plain text for tasks/meetings)
timestampstringnoEngagement timestamp in ISO 8601 format
owner_idstringnoHubSpot owner ID to assign the engagement to
propertiesobjectnoAdditional properties (see task/meeting-specific props above)

app.integrations.hubspot.list_pipelines(...)

List all CRM pipelines and their stages for a given object type. Use this to discover pipeline and stage IDs.

local result = app.integrations.hubspot.list_pipelines({
  object_type = "deals"
})
-- result.results  → {
--   {
--     id = "default",
--     label = "Sales Pipeline",
--     stages = {
--       { id = "appointmentscheduled", label = "Appointment Scheduled" },
--       { id = "qualifiedtobuy", label = "Qualified to Buy" },
--       { id = "presentationscheduled", label = "Presentation Scheduled" },
--       { id = "contractsent", label = "Contract Sent" },
--       { id = "closedwon", label = "Closed Won" },
--       { id = "closedlost", label = "Closed Lost" },
--     }
--   }
-- }

List ticket pipelines:

local result = app.integrations.hubspot.list_pipelines({
  object_type = "tickets"
})
ParameterTypeRequiredDescription
object_typestringyesObject type: "deals" or "tickets"

app.integrations.hubspot.list_properties(...)

List all property definitions for a given CRM object type. Useful to discover available property names, types, and options.

local result = app.integrations.hubspot.list_properties({
  object_type = "contacts"
})
-- result.results  → {
--   { name = "firstname", label = "First Name", type = "string", ... },
--   { name = "lastname", label = "Last Name", type = "string", ... },
--   { name = "email", label = "Email", type = "string", ... },
--   { name = "lifecyclestage", label = "Lifecycle Stage", type = "enumeration",
--     options = { { value = "lead", label = "Lead" }, ... } },
--   ...
-- }

Discover deal properties:

local result = app.integrations.hubspot.list_properties({
  object_type = "deals"
})
ParameterTypeRequiredDescription
object_typestringyesObject type: "contacts", "companies", "deals", or "tickets"

app.integrations.hubspot.add_contact_to_list(...)

Add contacts to a HubSpot marketing list by ID or email. Contacts already in the list are silently skipped.

local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  contact_ids = { "12345", "12346" }
})
-- result.list_id        → "42"
-- result.updated        → { "12345", "12346" }
-- result.discarded      → {}
-- result.invalid_vids   → {}
-- result.invalid_emails → {}

Add by email addresses:

local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  emails = { "[email protected]", "[email protected]" }
})

Mix IDs and emails:

local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  contact_ids = { "12345" },
  emails = { "[email protected]" }
})
ParameterTypeRequiredDescription
list_idstringyesHubSpot list ID to add contacts to
contact_idsarrayno*Array of HubSpot contact IDs
emailsarrayno*Array of email addresses

At least one of contact_ids or emails is required.


app.integrations.hubspot.list_forms(...)

List HubSpot marketing forms. Returns form IDs, names, types, and timestamps.

local result = app.integrations.hubspot.list_forms({})
-- result.results  → {
--   { id = "abc123", name = "Contact Us", type = "HUBSPOT", created_at = 1712000000000, updated_at = 1712100000000 },
--   { id = "def456", name = "Newsletter Signup", type = "HUBSPOT", created_at = 1712200000000, updated_at = nil },
-- }

With pagination:

local page1 = app.integrations.hubspot.list_forms({ limit = 50 })
if page1.after then
  local page2 = app.integrations.hubspot.list_forms({ limit = 50, after = page1.after })
end
ParameterTypeRequiredDescription
limitintegernoMax forms to return (default 50)
afterstringnoPagination cursor from previous response

Common Workflows

Create a deal with contact and company

-- Step 1: Create the company
local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com"
})

-- Step 2: Create the contact
local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  last_name = "Smith",
  email = "[email protected]",
  phone = "+1-555-0123"
})

-- Step 3: Associate contact to company
app.integrations.hubspot.create_association({
  from_type = "contacts",
  from_id = contact.id,
  to_type = "companies",
  to_id = company.id,
  association_type = "contact_to_company"
})

-- Step 4: Create the deal
local deal = app.integrations.hubspot.create_deal({
  dealname = "Acme Enterprise License",
  amount = "50000",
  pipeline = "default",
  dealstage = "appointmentscheduled"
})

-- Step 5: Associate deal to company and contact
app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = deal.id,
  to_type = "companies",
  to_id = company.id,
  association_type = "deal_to_company"
})
app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = deal.id,
  to_type = "contacts",
  to_id = contact.id,
  association_type = "deal_to_contact"
})

-- Step 6: Add a note to the contact
app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Initial meeting scheduled for next week. Enterprise deal opportunity.</p>",
  owner_id = "101"
})

Search and update a contact

-- Find a contact by email
local results = app.integrations.hubspot.search_contacts({
  query = "[email protected]",
  properties = { "firstname", "lastname", "email", "phone", "lifecyclestage" },
  limit = 1
})

if #results.results > 0 then
  local contact = results.results[1]
  -- Update lifecycle stage
  app.integrations.hubspot.update_contact({
    id = contact.id,
    properties = {
      lifecyclestage = "salesqualifiedlead",
      phone = "+1-555-9999"
    }
  })
end

Move a deal through the pipeline

-- Step 1: Get available pipelines and stages
local pipelines = app.integrations.hubspot.list_pipelines({ object_type = "deals" })

-- Step 2: Find the target stage ID
local target_stage_id = nil
for _, pipeline in ipairs(pipelines.results) do
  if pipeline.id == "default" then
    for _, stage in ipairs(pipeline.stages) do
      if stage.label == "Contract Sent" then
        target_stage_id = stage.id
      end
    end
  end
end

-- Step 3: Move the deal
if target_stage_id then
  app.integrations.hubspot.update_deal({
    id = "99999",
    properties = { dealstage = target_stage_id }
  })
end
-- Create the ticket
local ticket = app.integrations.hubspot.create_ticket({
  subject = "Cannot access billing portal",
  content = "Customer reports 403 error when navigating to /billing.",
  hs_pipeline = "0",
  hs_pipeline_stage = "1"
})

-- Associate with the reporting contact
app.integrations.hubspot.create_association({
  from_type = "tickets",
  from_id = ticket.id,
  to_type = "contacts",
  to_id = "12345",
  association_type = "ticket_to_contact"
})

-- Add an internal note
app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Verified billing portal permissions. escalated to engineering.</p>"
})

Idempotent contact sync (create or update)

-- Sync contact data from an external source
local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  last_name = "Smith",
  properties = {
    jobtitle = "VP Engineering",
    city = "Berlin",
    lifecyclestage = "opportunity"
  }
})

-- result.action is "created" or "updated" so you can log what happened
print("Contact " .. result.id .. " was " .. result.action)

Discover CRM schema

-- List all contact properties to find available field names
local props = app.integrations.hubspot.list_properties({ object_type = "contacts" })
for _, prop in ipairs(props.results) do
  print(prop.name .. " (" .. prop.label .. ") — " .. prop.type)
end

-- List owners to find IDs for assignment
local owners = app.integrations.hubspot.list_owners({})
for _, owner in ipairs(owners.results) do
  print(owner.id .. ": " .. owner.first_name .. " " .. owner.last_name .. " <" .. owner.email .. ">")
end

-- List pipelines to find stage IDs
local pipelines = app.integrations.hubspot.list_pipelines({ object_type = "deals" })
for _, pipeline in ipairs(pipelines.results) do
  print("Pipeline: " .. pipeline.label)
  for _, stage in ipairs(pipeline.stages) do
    print("  " .. stage.id .. " → " .. stage.label)
  end
end

Notes

  • Property names are case-sensitive — HubSpot uses lowercase with underscores internally (e.g., firstname, not firstName or FirstName).
  • The properties parameter in create/update tools accepts an object with string keys and string values. Pass custom fields the same way as standard ones.
  • IDs are strings — HubSpot object IDs are always passed and returned as strings.
  • Timestamps — HubSpot stores timestamps as milliseconds since epoch. ISO 8601 date strings are accepted where noted and converted automatically.
  • Associations are separate from creation — After creating a contact, company, or deal, you must explicitly call create_association to link them. Associations are not set through properties.
  • Use list_properties to discover available fields, and list_pipelines to find valid pipeline and stage IDs — these vary per HubSpot account.
  • Use create_or_update_contact instead of create_contact when you want idempotent upserts by email — this avoids duplicates.
  • All list endpoints return a results array. Check for the after field to determine if more pages are available.
  • Engagements require explicit association — After creating a note, task, or meeting, you may need to associate it with contacts/deals/companies separately depending on your workflow.

Multi-Account Usage

If you have multiple hubspot accounts configured, use account-specific namespaces:

-- Default account (always works)
app.integrations.hubspot.function_name({...})

-- Explicit default (portable across setups)
app.integrations.hubspot.default.function_name({...})

-- Named accounts
app.integrations.hubspot.work.function_name({...})
app.integrations.hubspot.personal.function_name({...})

All functions are identical across accounts — only the credentials differ.

Raw agent markdown
# HubSpot — Lua API Reference

## Overview

The HubSpot integration provides 26 tools for managing contacts, companies, deals, tickets, associations, engagements, and CRM metadata. All calls go through `app.integrations.hubspot.<tool_name>({ ... })` and return a Lua table.

## Authentication

HubSpot uses a **Private App Access Token** (format: `pat-...`) with Bearer token authentication. The token is configured once in the integration settings and sent automatically on every request — you never pass it in Lua calls.

Create a token: **HubSpot → Settings → Integrations → Private Apps → Create a private app**

## Object Types

HubSpot CRM objects are referenced by type string throughout the API:

| Type String   | Object   |
|---------------|----------|
| `contacts`    | Contact  |
| `companies`   | Company  |
| `deals`       | Deal     |
| `tickets`     | Ticket   |

These type strings are used in associations, pipelines, and properties endpoints.

## Pagination

List endpoints (`list_deals`, `list_owners`, `list_forms`) use **cursor-based pagination**. Pass `after` from a previous response to fetch the next page:

```lua
-- First page
local page1 = app.integrations.hubspot.list_deals({ limit = 50 })

-- Next page
if page1.after then
  local page2 = app.integrations.hubspot.list_deals({ limit = 50, after = page1.after })
end
```

The `after` field is `nil` when there are no more results.

## Common Properties

HubSpot uses internal property names. The most common ones:

| Object  | Property Name        | Description              |
|---------|----------------------|--------------------------|
| Contact | `firstname`          | First name               |
| Contact | `lastname`           | Last name                |
| Contact | `email`              | Email address            |
| Contact | `phone`              | Phone number             |
| Contact | `company`            | Company name             |
| Company | `name`               | Company name             |
| Company | `domain`             | Website domain           |
| Deal    | `dealname`           | Deal name                |
| Deal    | `amount`             | Deal amount              |
| Deal    | `pipeline`           | Pipeline ID              |
| Deal    | `dealstage`          | Stage ID within pipeline |
| Deal    | `closedate`          | Expected close date      |
| Deal    | `hubspot_owner_id`   | Assigned owner ID        |
| Ticket  | `subject`            | Ticket subject/title     |
| Ticket  | `content`            | Ticket body/description  |
| Ticket  | `hs_pipeline`        | Ticket pipeline ID       |
| Ticket  | `hs_pipeline_stage`  | Ticket pipeline stage ID |

Use `list_properties` to discover all available properties for any object type.

---

## Contacts

### `app.integrations.hubspot.create_contact(...)`

Create a new contact in HubSpot CRM. Returns the contact's HubSpot ID and properties.

```lua
local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  last_name = "Smith",
  email = "[email protected]",
  phone = "+1-555-0123",
  company = "Acme Corp"
})
-- contact.id          → "12345"
-- contact.properties  → { firstname = "Jane", lastname = "Smith", email = "[email protected]", ... }
```

Pass custom properties via the `properties` table:

```lua
local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  email = "[email protected]",
  properties = {
    jobtitle = "CTO",
    city = "San Francisco",
    lifecyclestage = "lead"
  }
})
```

| Parameter     | Type   | Required | Description                                      |
|---------------|--------|----------|--------------------------------------------------|
| `first_name`  | string | no       | Contact first name (maps to `firstname`)         |
| `last_name`   | string | no       | Contact last name (maps to `lastname`)           |
| `email`       | string | no       | Contact email address                            |
| `phone`       | string | no       | Contact phone number                             |
| `company`     | string | no       | Contact company name                             |
| `properties`  | object | no       | Additional custom properties as key-value pairs  |

> At least one property is required.

---

### `app.integrations.hubspot.get_contact(...)`

Retrieve a HubSpot contact by ID. Optionally specify which properties to return.

```lua
local contact = app.integrations.hubspot.get_contact({
  id = "12345"
})
-- contact.id          → "12345"
-- contact.properties  → { firstname = "Jane", email = "[email protected]", ... }
```

Fetch only specific properties to reduce response size:

```lua
local contact = app.integrations.hubspot.get_contact({
  id = "12345",
  properties = { "firstname", "lastname", "email" }
})
```

| Parameter    | Type          | Required | Description                                      |
|--------------|---------------|----------|--------------------------------------------------|
| `id`         | string        | yes      | HubSpot contact ID                               |
| `properties` | array<string> | no       | List of property names to include                |

---

### `app.integrations.hubspot.update_contact(...)`

Update an existing HubSpot contact. Only the properties you pass are changed.

```lua
local result = app.integrations.hubspot.update_contact({
  id = "12345",
  properties = {
    phone = "+1-555-9999",
    jobtitle = "VP Engineering"
  }
})
-- result.id          → "12345"
-- result.properties  → { phone = "+1-555-9999", jobtitle = "VP Engineering", ... }
```

| Parameter    | Type   | Required | Description                                      |
|--------------|--------|----------|--------------------------------------------------|
| `id`         | string | yes      | HubSpot contact ID                               |
| `properties` | object | yes      | Properties to update as key-value pairs          |

---

### `app.integrations.hubspot.search_contacts(...)`

Search HubSpot contacts with filters. Pass a query string for text search or structured filter groups.

```lua
local results = app.integrations.hubspot.search_contacts({
  query = "[email protected]",
  properties = { "firstname", "lastname", "email" },
  limit = 10
})
-- results.results  → { { id = "12345", properties = { ... } }, ... }
```

Search with structured filters:

```lua
local results = app.integrations.hubspot.search_contacts({
  properties = { "firstname", "lastname", "email", "lifecyclestage" },
  limit = 25,
  filter_groups = {
    {
      filters = {
        {
          property_name = "lifecyclestage",
          operator = "EQ",
          value = "marketingqualifiedlead"
        }
      }
    }
  }
})
```

| Parameter       | Type          | Required | Description                                  |
|-----------------|---------------|----------|----------------------------------------------|
| `query`         | string        | no       | Text search across contact properties        |
| `properties`    | array<string> | no       | Property names to include in results         |
| `limit`         | integer       | no       | Max results to return                        |
| `filter_groups` | array         | no       | Structured filter groups for advanced search |

---

### `app.integrations.hubspot.delete_contact(...)`

Permanently delete a HubSpot contact by ID. **This action cannot be undone.**

```lua
local result = app.integrations.hubspot.delete_contact({
  id = "12345"
})
-- result.id      → "12345"
-- result.deleted → true
```

| Parameter | Type   | Required | Description              |
|-----------|--------|----------|--------------------------|
| `id`      | string | yes      | HubSpot contact ID       |

---

### `app.integrations.hubspot.create_or_update_contact(...)`

Create or update a contact matched by email. If a contact with that email exists, it is updated; otherwise a new one is created. The response includes an `action` field indicating what happened.

```lua
local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  last_name = "Smith",
  phone = "+1-555-0123",
  company = "Acme Corp"
})
-- result.id       → "12345"
-- result.action   → "created"  (or "updated")
-- result.properties  → { firstname = "Jane", email = "[email protected]", ... }
```

Include custom properties:

```lua
local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  properties = {
    jobtitle = "CTO",
    city = "Berlin"
  }
})
```

| Parameter    | Type   | Required | Description                                      |
|--------------|--------|----------|--------------------------------------------------|
| `email`      | string | yes      | Email address used for lookup and matching        |
| `first_name` | string | no       | Contact first name                               |
| `last_name`  | string | no       | Contact last name                                |
| `phone`      | string | no       | Contact phone number                             |
| `company`    | string | no       | Contact company name                             |
| `properties` | object | no       | Additional custom properties as key-value pairs   |

---

## Companies

### `app.integrations.hubspot.create_company(...)`

Create a new company in HubSpot CRM.

```lua
local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com"
})
-- company.id          → "67890"
-- company.properties  → { name = "Acme Corp", domain = "acme.com", ... }
```

With custom properties:

```lua
local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com",
  properties = {
    industry = "TECHNOLOGY",
    city = "New York",
    description = "A technology company"
  }
})
```

| Parameter    | Type   | Required | Description                                      |
|--------------|--------|----------|--------------------------------------------------|
| `name`       | string | no       | Company name                                      |
| `domain`     | string | no       | Company website domain                            |
| `properties` | object | no       | Additional custom properties as key-value pairs   |

> At least one company property is required.

---

### `app.integrations.hubspot.get_company(...)`

Retrieve a HubSpot company by ID.

```lua
local company = app.integrations.hubspot.get_company({
  id = "67890"
})
-- company.id          → "67890"
-- company.properties  → { name = "Acme Corp", domain = "acme.com", ... }
```

With specific properties:

```lua
local company = app.integrations.hubspot.get_company({
  id = "67890",
  properties = { "name", "domain", "industry" }
})
```

| Parameter    | Type          | Required | Description                         |
|--------------|---------------|----------|-------------------------------------|
| `id`         | string        | yes      | HubSpot company ID                  |
| `properties` | array<string> | no       | List of property names to include   |

---

### `app.integrations.hubspot.update_company(...)`

Update an existing HubSpot company.

```lua
local result = app.integrations.hubspot.update_company({
  id = "67890",
  properties = {
    industry = "FINANCE",
    city = "London"
  }
})
-- result.id          → "67890"
-- result.properties  → { industry = "FINANCE", city = "London", ... }
```

| Parameter    | Type   | Required | Description                             |
|--------------|--------|----------|-----------------------------------------|
| `id`         | string | yes      | HubSpot company ID                      |
| `properties` | object | yes      | Properties to update as key-value pairs |

---

### `app.integrations.hubspot.search_companies(...)`

Search HubSpot companies with filters. Pass a query string for text search or structured filter groups.

```lua
local results = app.integrations.hubspot.search_companies({
  query = "Acme",
  properties = { "name", "domain", "industry" },
  limit = 10
})
-- results.results  → { { id = "67890", properties = { ... } }, ... }
```

| Parameter       | Type          | Required | Description                                  |
|-----------------|---------------|----------|----------------------------------------------|
| `query`         | string        | no       | Text search across company properties        |
| `properties`    | array<string> | no       | Property names to include in results         |
| `limit`         | integer       | no       | Max results to return                        |
| `filter_groups` | array         | no       | Structured filter groups for advanced search |

---

## Deals

### `app.integrations.hubspot.create_deal(...)`

Create a new deal in HubSpot CRM.

```lua
local deal = app.integrations.hubspot.create_deal({
  dealname = "Enterprise License Deal",
  amount = "50000",
  pipeline = "default",
  dealstage = "appointmentscheduled",
  closedate = "2026-06-30"
})
-- deal.id          → "99999"
-- deal.properties  → { dealname = "Enterprise License Deal", amount = "50000", ... }
```

With custom properties:

```lua
local deal = app.integrations.hubspot.create_deal({
  dealname = "Partner Deal",
  amount = "25000",
  properties = {
    deal_type = "newbusiness",
    description = "Partner referral"
  }
})
```

| Parameter    | Type   | Required | Description                                            |
|--------------|--------|----------|--------------------------------------------------------|
| `dealname`   | string | no       | Deal name                                              |
| `amount`     | string | no       | Deal amount                                            |
| `pipeline`   | string | no       | Pipeline ID or internal name                           |
| `dealstage`  | string | no       | Deal stage ID or internal name                         |
| `closedate`  | string | no       | Expected close date (ISO 8601 or milliseconds)         |
| `properties` | object | no       | Additional custom properties as key-value pairs        |

> At least one deal property is required.

---

### `app.integrations.hubspot.get_deal(...)`

Retrieve a HubSpot deal by ID.

```lua
local deal = app.integrations.hubspot.get_deal({
  id = "99999"
})
-- deal.id          → "99999"
-- deal.properties  → { dealname = "Enterprise License Deal", amount = "50000", ... }
```

With specific properties:

```lua
local deal = app.integrations.hubspot.get_deal({
  id = "99999",
  properties = { "dealname", "amount", "dealstage", "pipeline" }
})
```

| Parameter    | Type          | Required | Description                         |
|--------------|---------------|----------|-------------------------------------|
| `id`         | string        | yes      | HubSpot deal ID                     |
| `properties` | array<string> | no       | List of property names to include   |

---

### `app.integrations.hubspot.update_deal(...)`

Update an existing HubSpot deal. Commonly used to move deals through pipeline stages.

```lua
local result = app.integrations.hubspot.update_deal({
  id = "99999",
  properties = {
    dealstage = "closedwon",
    amount = "55000",
    closedate = "2026-04-05"
  }
})
-- result.id          → "99999"
-- result.properties  → { dealstage = "closedwon", amount = "55000", ... }
```

| Parameter    | Type   | Required | Description                             |
|--------------|--------|----------|-----------------------------------------|
| `id`         | string | yes      | HubSpot deal ID                         |
| `properties` | object | yes      | Properties to update as key-value pairs |

---

### `app.integrations.hubspot.list_deals(...)`

List deals with cursor-based pagination.

```lua
local page = app.integrations.hubspot.list_deals({
  limit = 50,
  properties = { "dealname", "amount", "dealstage", "closedate" }
})
-- page.results  → { { id = "99999", properties = { ... } }, ... }
-- page.total    → 50
-- page.after    → "eyJuZXh0IjoiMTAwIn0"  (cursor for next page, nil if last page)
```

Fetch the next page:

```lua
if page.after then
  local page2 = app.integrations.hubspot.list_deals({
    limit = 50,
    after = page.after,
    properties = { "dealname", "amount", "dealstage" }
  })
end
```

| Parameter    | Type          | Required | Description                              |
|--------------|---------------|----------|------------------------------------------|
| `limit`      | integer       | no       | Max deals to return (default 10, max 100)|
| `after`      | string        | no       | Pagination cursor from previous response |
| `properties` | array<string> | no       | Property names to include in results     |

---

## Tickets

### `app.integrations.hubspot.create_ticket(...)`

Create a new ticket in HubSpot CRM.

```lua
local ticket = app.integrations.hubspot.create_ticket({
  subject = "Login page not loading",
  content = "Customer reports that the login page returns a 500 error since yesterday.",
  hs_pipeline = "0",
  hs_pipeline_stage = "1"
})
-- ticket.id          → "55555"
-- ticket.properties  → { subject = "Login page not loading", content = "...", ... }
```

With custom properties:

```lua
local ticket = app.integrations.hubspot.create_ticket({
  subject = "Billing inquiry",
  content = "Customer wants to upgrade their plan.",
  properties = {
    priority = "HIGH",
    source_type = "PHONE"
  }
})
```

| Parameter            | Type   | Required | Description                                   |
|----------------------|--------|----------|-----------------------------------------------|
| `subject`            | string | yes      | Ticket subject / title                        |
| `content`            | string | no       | Ticket body content / description             |
| `hs_pipeline`        | string | no       | Pipeline ID for the ticket                    |
| `hs_pipeline_stage`  | string | no       | Pipeline stage ID for the ticket              |
| `properties`         | object | no       | Additional custom properties as key-value pairs|

---

### `app.integrations.hubspot.get_ticket(...)`

Retrieve a HubSpot ticket by ID.

```lua
local ticket = app.integrations.hubspot.get_ticket({
  id = "55555"
})
-- ticket.id          → "55555"
-- ticket.properties  → { subject = "Login page not loading", content = "...", ... }
```

With specific properties:

```lua
local ticket = app.integrations.hubspot.get_ticket({
  id = "55555",
  properties = { "subject", "content", "hs_pipeline_stage", "priority" }
})
```

| Parameter    | Type          | Required | Description                         |
|--------------|---------------|----------|-------------------------------------|
| `id`         | string        | yes      | HubSpot ticket ID                   |
| `properties` | array<string> | no       | List of property names to include   |

---

### `app.integrations.hubspot.update_ticket(...)`

Update an existing HubSpot ticket. Commonly used to change ticket status/pipeline stage.

```lua
local result = app.integrations.hubspot.update_ticket({
  id = "55555",
  properties = {
    hs_pipeline_stage = "4",
    content = "Issue resolved. Fix deployed in v2.3.1."
  }
})
-- result.id          → "55555"
-- result.properties  → { hs_pipeline_stage = "4", content = "...", ... }
```

| Parameter    | Type   | Required | Description                             |
|--------------|--------|----------|-----------------------------------------|
| `id`         | string | yes      | HubSpot ticket ID                       |
| `properties` | object | yes      | Properties to update as key-value pairs |

---

## Associations & Metadata

### `app.integrations.hubspot.create_association(...)`

Associate two HubSpot CRM objects. Both objects must already exist.

```lua
local result = app.integrations.hubspot.create_association({
  from_type = "contacts",
  from_id = "12345",
  to_type = "companies",
  to_id = "67890",
  association_type = "contact_to_company"
})
-- result.from_type        → "contacts"
-- result.from_id          → "12345"
-- result.to_type          → "companies"
-- result.to_id            → "67890"
-- result.association_type → "contact_to_company"
-- result.created          → true
```

Associate a deal to a company:

```lua
app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = "99999",
  to_type = "companies",
  to_id = "67890",
  association_type = "deal_to_company"
})
```

Associate a ticket to a contact:

```lua
app.integrations.hubspot.create_association({
  from_type = "tickets",
  from_id = "55555",
  to_type = "contacts",
  to_id = "12345",
  association_type = "ticket_to_contact"
})
```

Common association types:
- `contact_to_company`
- `company_to_contact`
- `deal_to_company`
- `deal_to_contact`
- `ticket_to_contact`
- `ticket_to_company`

| Parameter          | Type   | Required | Description                                            |
|--------------------|--------|----------|--------------------------------------------------------|
| `from_type`        | string | yes      | Source object type (`contacts`, `companies`, `deals`, `tickets`) |
| `from_id`          | string | yes      | Source object ID                                       |
| `to_type`          | string | yes      | Target object type                                     |
| `to_id`            | string | yes      | Target object ID                                       |
| `association_type` | string | yes      | Association type name (e.g., `contact_to_company`)    |

---

### `app.integrations.hubspot.list_associations(...)`

List all associations from an object to a specific target type.

```lua
local result = app.integrations.hubspot.list_associations({
  from_type = "contacts",
  from_id = "12345",
  to_type = "companies"
})
-- result.from_type  → "contacts"
-- result.from_id    → "12345"
-- result.to_type    → "companies"
-- result.results    → { { id = "67890", type = "contact_to_company" }, ... }
```

List deals associated with a company:

```lua
local result = app.integrations.hubspot.list_associations({
  from_type = "companies",
  from_id = "67890",
  to_type = "deals"
})
```

| Parameter   | Type   | Required | Description                                            |
|-------------|--------|----------|--------------------------------------------------------|
| `from_type` | string | yes      | Source object type                                     |
| `from_id`   | string | yes      | Source object ID                                       |
| `to_type`   | string | yes      | Target object type to find associations for            |

---

### `app.integrations.hubspot.list_owners(...)`

List HubSpot CRM owners (users). Useful for assigning owners to contacts, deals, and tickets.

```lua
local result = app.integrations.hubspot.list_owners({})
-- result.results  → {
--   { id = "101", email = "[email protected]", first_name = "Sarah", last_name = "Jones", user_id = 201 },
--   { id = "102", email = "[email protected]", first_name = "Mike", last_name = "Chen", user_id = 202 },
-- }
```

Paginate through all owners:

```lua
local page = app.integrations.hubspot.list_owners({ limit = 100 })
if page.after then
  local page2 = app.integrations.hubspot.list_owners({ limit = 100, after = page.after })
end
```

| Parameter | Type    | Required | Description                               |
|-----------|---------|----------|-------------------------------------------|
| `limit`   | integer | no       | Max owners to return (default 100)        |
| `after`   | string  | no       | Pagination cursor from previous response  |

---

### `app.integrations.hubspot.create_engagement(...)`

Create a note, task, or meeting engagement in HubSpot CRM.

**Create a note:**

```lua
local note = app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Customer called to discuss pricing for enterprise plan.</p>",
  owner_id = "101"
})
-- note.id          → "77777"
-- note.type        → "notes"
-- note.properties  → { hs_note_body = "...", hubspot_owner_id = "101", ... }
```

**Create a task:**

```lua
local task = app.integrations.hubspot.create_engagement({
  type = "tasks",
  body = "Send follow-up proposal to the client",
  owner_id = "101",
  properties = {
    hs_task_subject = "Follow-up proposal",
    hs_task_status = "NOT_STARTED",
    hs_task_priority = "HIGH"
  }
})
```

**Create a meeting:**

```lua
local meeting = app.integrations.hubspot.create_engagement({
  type = "meetings",
  body = "Quarterly review meeting",
  timestamp = "2026-04-15T10:00:00",
  owner_id = "101",
  properties = {
    hs_meeting_title = "Q2 Review",
    hs_meeting_start_time = "2026-04-15T10:00:00",
    hs_meeting_end_time = "2026-04-15T11:00:00"
  }
})
```

| Parameter    | Type   | Required | Description                                                       |
|--------------|--------|----------|-------------------------------------------------------------------|
| `type`       | string | yes      | Engagement type: `"notes"`, `"tasks"`, or `"meetings"`            |
| `body`       | string | no       | Body content (HTML for notes, plain text for tasks/meetings)      |
| `timestamp`  | string | no       | Engagement timestamp in ISO 8601 format                           |
| `owner_id`   | string | no       | HubSpot owner ID to assign the engagement to                      |
| `properties` | object | no       | Additional properties (see task/meeting-specific props above)     |

---

### `app.integrations.hubspot.list_pipelines(...)`

List all CRM pipelines and their stages for a given object type. Use this to discover pipeline and stage IDs.

```lua
local result = app.integrations.hubspot.list_pipelines({
  object_type = "deals"
})
-- result.results  → {
--   {
--     id = "default",
--     label = "Sales Pipeline",
--     stages = {
--       { id = "appointmentscheduled", label = "Appointment Scheduled" },
--       { id = "qualifiedtobuy", label = "Qualified to Buy" },
--       { id = "presentationscheduled", label = "Presentation Scheduled" },
--       { id = "contractsent", label = "Contract Sent" },
--       { id = "closedwon", label = "Closed Won" },
--       { id = "closedlost", label = "Closed Lost" },
--     }
--   }
-- }
```

List ticket pipelines:

```lua
local result = app.integrations.hubspot.list_pipelines({
  object_type = "tickets"
})
```

| Parameter      | Type   | Required | Description                                     |
|----------------|--------|----------|-------------------------------------------------|
| `object_type`  | string | yes      | Object type: `"deals"` or `"tickets"`           |

---

### `app.integrations.hubspot.list_properties(...)`

List all property definitions for a given CRM object type. Useful to discover available property names, types, and options.

```lua
local result = app.integrations.hubspot.list_properties({
  object_type = "contacts"
})
-- result.results  → {
--   { name = "firstname", label = "First Name", type = "string", ... },
--   { name = "lastname", label = "Last Name", type = "string", ... },
--   { name = "email", label = "Email", type = "string", ... },
--   { name = "lifecyclestage", label = "Lifecycle Stage", type = "enumeration",
--     options = { { value = "lead", label = "Lead" }, ... } },
--   ...
-- }
```

Discover deal properties:

```lua
local result = app.integrations.hubspot.list_properties({
  object_type = "deals"
})
```

| Parameter      | Type   | Required | Description                                              |
|----------------|--------|----------|----------------------------------------------------------|
| `object_type`  | string | yes      | Object type: `"contacts"`, `"companies"`, `"deals"`, or `"tickets"` |

---

### `app.integrations.hubspot.add_contact_to_list(...)`

Add contacts to a HubSpot marketing list by ID or email. Contacts already in the list are silently skipped.

```lua
local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  contact_ids = { "12345", "12346" }
})
-- result.list_id        → "42"
-- result.updated        → { "12345", "12346" }
-- result.discarded      → {}
-- result.invalid_vids   → {}
-- result.invalid_emails → {}
```

Add by email addresses:

```lua
local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  emails = { "[email protected]", "[email protected]" }
})
```

Mix IDs and emails:

```lua
local result = app.integrations.hubspot.add_contact_to_list({
  list_id = "42",
  contact_ids = { "12345" },
  emails = { "[email protected]" }
})
```

| Parameter     | Type          | Required | Description                                         |
|---------------|---------------|----------|-----------------------------------------------------|
| `list_id`     | string        | yes      | HubSpot list ID to add contacts to                  |
| `contact_ids` | array<string> | no*      | Array of HubSpot contact IDs                        |
| `emails`      | array<string> | no*      | Array of email addresses                            |

> *At least one of `contact_ids` or `emails` is required.*

---

### `app.integrations.hubspot.list_forms(...)`

List HubSpot marketing forms. Returns form IDs, names, types, and timestamps.

```lua
local result = app.integrations.hubspot.list_forms({})
-- result.results  → {
--   { id = "abc123", name = "Contact Us", type = "HUBSPOT", created_at = 1712000000000, updated_at = 1712100000000 },
--   { id = "def456", name = "Newsletter Signup", type = "HUBSPOT", created_at = 1712200000000, updated_at = nil },
-- }
```

With pagination:

```lua
local page1 = app.integrations.hubspot.list_forms({ limit = 50 })
if page1.after then
  local page2 = app.integrations.hubspot.list_forms({ limit = 50, after = page1.after })
end
```

| Parameter | Type    | Required | Description                              |
|-----------|---------|----------|------------------------------------------|
| `limit`   | integer | no       | Max forms to return (default 50)         |
| `after`   | string  | no       | Pagination cursor from previous response |

---

## Common Workflows

### Create a deal with contact and company

```lua
-- Step 1: Create the company
local company = app.integrations.hubspot.create_company({
  name = "Acme Corp",
  domain = "acme.com"
})

-- Step 2: Create the contact
local contact = app.integrations.hubspot.create_contact({
  first_name = "Jane",
  last_name = "Smith",
  email = "[email protected]",
  phone = "+1-555-0123"
})

-- Step 3: Associate contact to company
app.integrations.hubspot.create_association({
  from_type = "contacts",
  from_id = contact.id,
  to_type = "companies",
  to_id = company.id,
  association_type = "contact_to_company"
})

-- Step 4: Create the deal
local deal = app.integrations.hubspot.create_deal({
  dealname = "Acme Enterprise License",
  amount = "50000",
  pipeline = "default",
  dealstage = "appointmentscheduled"
})

-- Step 5: Associate deal to company and contact
app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = deal.id,
  to_type = "companies",
  to_id = company.id,
  association_type = "deal_to_company"
})
app.integrations.hubspot.create_association({
  from_type = "deals",
  from_id = deal.id,
  to_type = "contacts",
  to_id = contact.id,
  association_type = "deal_to_contact"
})

-- Step 6: Add a note to the contact
app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Initial meeting scheduled for next week. Enterprise deal opportunity.</p>",
  owner_id = "101"
})
```

### Search and update a contact

```lua
-- Find a contact by email
local results = app.integrations.hubspot.search_contacts({
  query = "[email protected]",
  properties = { "firstname", "lastname", "email", "phone", "lifecyclestage" },
  limit = 1
})

if #results.results > 0 then
  local contact = results.results[1]
  -- Update lifecycle stage
  app.integrations.hubspot.update_contact({
    id = contact.id,
    properties = {
      lifecyclestage = "salesqualifiedlead",
      phone = "+1-555-9999"
    }
  })
end
```

### Move a deal through the pipeline

```lua
-- Step 1: Get available pipelines and stages
local pipelines = app.integrations.hubspot.list_pipelines({ object_type = "deals" })

-- Step 2: Find the target stage ID
local target_stage_id = nil
for _, pipeline in ipairs(pipelines.results) do
  if pipeline.id == "default" then
    for _, stage in ipairs(pipeline.stages) do
      if stage.label == "Contract Sent" then
        target_stage_id = stage.id
      end
    end
  end
end

-- Step 3: Move the deal
if target_stage_id then
  app.integrations.hubspot.update_deal({
    id = "99999",
    properties = { dealstage = target_stage_id }
  })
end
```

### Create a ticket and link it to a contact

```lua
-- Create the ticket
local ticket = app.integrations.hubspot.create_ticket({
  subject = "Cannot access billing portal",
  content = "Customer reports 403 error when navigating to /billing.",
  hs_pipeline = "0",
  hs_pipeline_stage = "1"
})

-- Associate with the reporting contact
app.integrations.hubspot.create_association({
  from_type = "tickets",
  from_id = ticket.id,
  to_type = "contacts",
  to_id = "12345",
  association_type = "ticket_to_contact"
})

-- Add an internal note
app.integrations.hubspot.create_engagement({
  type = "notes",
  body = "<p>Verified billing portal permissions. escalated to engineering.</p>"
})
```

### Idempotent contact sync (create or update)

```lua
-- Sync contact data from an external source
local result = app.integrations.hubspot.create_or_update_contact({
  email = "[email protected]",
  first_name = "Jane",
  last_name = "Smith",
  properties = {
    jobtitle = "VP Engineering",
    city = "Berlin",
    lifecyclestage = "opportunity"
  }
})

-- result.action is "created" or "updated" so you can log what happened
print("Contact " .. result.id .. " was " .. result.action)
```

### Discover CRM schema

```lua
-- List all contact properties to find available field names
local props = app.integrations.hubspot.list_properties({ object_type = "contacts" })
for _, prop in ipairs(props.results) do
  print(prop.name .. " (" .. prop.label .. ") — " .. prop.type)
end

-- List owners to find IDs for assignment
local owners = app.integrations.hubspot.list_owners({})
for _, owner in ipairs(owners.results) do
  print(owner.id .. ": " .. owner.first_name .. " " .. owner.last_name .. " <" .. owner.email .. ">")
end

-- List pipelines to find stage IDs
local pipelines = app.integrations.hubspot.list_pipelines({ object_type = "deals" })
for _, pipeline in ipairs(pipelines.results) do
  print("Pipeline: " .. pipeline.label)
  for _, stage in ipairs(pipeline.stages) do
    print("  " .. stage.id .. " → " .. stage.label)
  end
end
```

## Notes

- **Property names are case-sensitive** — HubSpot uses lowercase with underscores internally (e.g., `firstname`, not `firstName` or `FirstName`).
- **The `properties` parameter** in create/update tools accepts an object with string keys and string values. Pass custom fields the same way as standard ones.
- **IDs are strings** — HubSpot object IDs are always passed and returned as strings.
- **Timestamps** — HubSpot stores timestamps as milliseconds since epoch. ISO 8601 date strings are accepted where noted and converted automatically.
- **Associations are separate from creation** — After creating a contact, company, or deal, you must explicitly call `create_association` to link them. Associations are not set through properties.
- **Use `list_properties`** to discover available fields, and **`list_pipelines`** to find valid pipeline and stage IDs — these vary per HubSpot account.
- **Use `create_or_update_contact`** instead of `create_contact` when you want idempotent upserts by email — this avoids duplicates.
- **All list endpoints** return a `results` array. Check for the `after` field to determine if more pages are available.
- **Engagements require explicit association** — After creating a note, task, or meeting, you may need to associate it with contacts/deals/companies separately depending on your workflow.

---

## Multi-Account Usage

If you have multiple hubspot accounts configured, use account-specific namespaces:

```lua
-- Default account (always works)
app.integrations.hubspot.function_name({...})

-- Explicit default (portable across setups)
app.integrations.hubspot.default.function_name({...})

-- Named accounts
app.integrations.hubspot.work.function_name({...})
app.integrations.hubspot.personal.function_name({...})
```

All functions are identical across accounts — only the credentials differ.

Metadata-Derived Lua Example

local result = app.integrations.hubspot.hubspot_create_contact({
  first_name = "example_first_name",
  last_name = "example_last_name",
  email = "example_email",
  phone = "example_phone",
  company = "example_company",
  properties = "example_properties"
})
print(result)

Functions

hubspot_create_contact

Create a new contact in HubSpot CRM. Supports firstname, lastname, email, phone, company, and any additional custom properties. Returns the created contact with its HubSpot ID and properties.

Operation
Write write
Full name
hubspot.hubspot_create_contact
ParameterTypeRequiredDescription
first_name string no Contact first name.
last_name string no Contact last name.
email string no Contact email address.
phone string no Contact phone number.
company string no Contact company name.
properties object no Additional custom properties as key-value pairs.

hubspot_get_contact

Retrieve a HubSpot contact by its ID. Returns the contact's ID, properties, and associated data. Optionally specify which properties to include.

Operation
Read read
Full name
hubspot.hubspot_get_contact
ParameterTypeRequiredDescription
id string yes HubSpot contact ID.
properties array no List of property names to include (e.g., ["firstname","email"]).

hubspot_update_contact

Update an existing HubSpot contact by ID. Provide a properties object with the fields to update (e.g., {"firstname": "Jane", "phone": "555-0100"}). Returns the updated contact.

Operation
Write write
Full name
hubspot.hubspot_update_contact
ParameterTypeRequiredDescription
id string yes HubSpot contact ID to update.
properties object yes Key-value map of properties to update (e.g., {"firstname": "Jane"}).

hubspot_search_contacts

Search HubSpot contacts using filter groups and/or a text query. Use filterGroups for structured queries (e.g., email equals "[email protected]"). Use query for full-text search across searchable properties. Supports pagination with limit and after parameters.

Operation
Read read
Full name
hubspot.hubspot_search_contacts
ParameterTypeRequiredDescription
query string no Full-text search query.
filter_groups array no Array of filter groups, each containing filters with propertyName, operator, and value.
properties array no List of property names to include in results.
limit integer no Maximum number of results to return (default 10, max 100).
after string no Pagination cursor from a previous response.

hubspot_delete_contact

Delete a HubSpot contact by ID. Permanently removes the contact from HubSpot CRM. This action cannot be undone.

Operation
Write write
Full name
hubspot.hubspot_delete_contact
ParameterTypeRequiredDescription
id string yes HubSpot contact ID to delete.

hubspot_create_or_update_contact

Create or update a HubSpot contact by email. First searches for an existing contact matching the email. If found, updates it. If not found, creates a new contact. Supports firstname, lastname, phone, company, and any additional custom properties.

Operation
Write write
Full name
hubspot.hubspot_create_or_update_contact
ParameterTypeRequiredDescription
email string yes Contact email address used for lookup.
first_name string no Contact first name.
last_name string no Contact last name.
phone string no Contact phone number.
company string no Contact company name.
properties object no Additional custom properties as key-value pairs.

hubspot_create_company

Create a new company in HubSpot CRM. Supports name, domain, and any additional custom properties. Returns the created company with its HubSpot ID and properties.

Operation
Write write
Full name
hubspot.hubspot_create_company
ParameterTypeRequiredDescription
name string no Company name.
domain string no Company website domain.
properties object no Additional custom properties as key-value pairs.

hubspot_get_company

Retrieve a HubSpot company by its ID. Returns the company's ID, properties, and associated data. Optionally specify which properties to include.

Operation
Read read
Full name
hubspot.hubspot_get_company
ParameterTypeRequiredDescription
id string yes HubSpot company ID.
properties array no List of property names to include.

hubspot_update_company

Update an existing HubSpot company by ID. Provide a properties object with the fields to update (e.g., {"name": "Acme Corp", "domain": "acme.com"}). Returns the updated company.

Operation
Write write
Full name
hubspot.hubspot_update_company
ParameterTypeRequiredDescription
id string yes HubSpot company ID to update.
properties object yes Key-value map of properties to update.

hubspot_search_companies

Search HubSpot companies using filter groups and/or a text query. Use filterGroups for structured queries (e.g., domain equals "acme.com"). Use query for full-text search across searchable properties. Supports pagination with limit and after parameters.

Operation
Read read
Full name
hubspot.hubspot_search_companies
ParameterTypeRequiredDescription
query string no Full-text search query.
filter_groups array no Array of filter groups, each containing filters with propertyName, operator, and value.
properties array no List of property names to include in results.
limit integer no Maximum number of results to return (default 10, max 100).
after string no Pagination cursor from a previous response.

hubspot_create_deal

Create a new deal in HubSpot CRM. Supports dealname, amount, pipeline, dealstage, closedate, and any additional custom properties. Returns the created deal with its HubSpot ID and properties.

Operation
Write write
Full name
hubspot.hubspot_create_deal
ParameterTypeRequiredDescription
dealname string no Deal name.
amount string no Deal amount.
pipeline string no Pipeline ID or internal name.
dealstage string no Deal stage ID or internal name.
closedate string no Expected close date (ISO 8601 or milliseconds timestamp).
properties object no Additional custom properties as key-value pairs.

hubspot_get_deal

Retrieve a HubSpot deal by its ID. Returns the deal's ID, properties, and associated data. Optionally specify which properties to include.

Operation
Read read
Full name
hubspot.hubspot_get_deal
ParameterTypeRequiredDescription
id string yes HubSpot deal ID.
properties array no List of property names to include.

hubspot_update_deal

Update an existing HubSpot deal by ID. Provide a properties object with the fields to update (e.g., {"dealstage": "closedwon", "amount": "5000"}). Returns the updated deal.

Operation
Write write
Full name
hubspot.hubspot_update_deal
ParameterTypeRequiredDescription
id string yes HubSpot deal ID to update.
properties object yes Key-value map of properties to update.

hubspot_list_deals

List deals in HubSpot CRM with cursor-based pagination. Optionally specify properties to include and control page size. Use the "after" cursor from a previous response to fetch the next page.

Operation
Read read
Full name
hubspot.hubspot_list_deals
ParameterTypeRequiredDescription
limit integer no Maximum number of deals to return (default 10, max 100).
after string no Pagination cursor from a previous response.
properties array no List of property names to include in results.

hubspot_create_ticket

Create a new ticket in HubSpot CRM. Supports subject, content, hs_pipeline, hs_pipeline_stage, and any additional custom properties. Returns the created ticket with its HubSpot ID and properties.

Operation
Write write
Full name
hubspot.hubspot_create_ticket
ParameterTypeRequiredDescription
subject string yes Ticket subject / title.
content string no Ticket body content / description.
hs_pipeline string no Pipeline ID for the ticket.
hs_pipeline_stage string no Pipeline stage ID for the ticket.
properties object no Additional custom properties as key-value pairs.

hubspot_get_ticket

Retrieve a HubSpot ticket by its ID. Returns the ticket's ID, properties, and associated data. Optionally specify which properties to include.

Operation
Read read
Full name
hubspot.hubspot_get_ticket
ParameterTypeRequiredDescription
id string yes HubSpot ticket ID.
properties array no List of property names to include.

hubspot_update_ticket

Update an existing HubSpot ticket by ID. Provide a properties object with the fields to update (e.g., {"subject": "New subject", "hs_pipeline_stage": "2"}). Returns the updated ticket.

Operation
Write write
Full name
hubspot.hubspot_update_ticket
ParameterTypeRequiredDescription
id string yes HubSpot ticket ID to update.
properties object yes Key-value map of properties to update.

hubspot_create_association

Create an association between two HubSpot CRM objects. For example, associate a contact to a company, or a deal to a company. Specify the from/to object types (contacts, companies, deals, tickets), their IDs, and the association type. Common association types: contact_to_company, company_to_contact, deal_to_company, ticket_to_contact.

Operation
Write write
Full name
hubspot.hubspot_create_association
ParameterTypeRequiredDescription
from_type string yes Source object type (e.g., "contacts", "companies", "deals", "tickets").
from_id string yes Source object ID.
to_type string yes Target object type (e.g., "contacts", "companies", "deals", "tickets").
to_id string yes Target object ID.
association_type string yes Association type name (e.g., "contact_to_company", "deal_to_company").

hubspot_list_associations

List associations from a HubSpot CRM object to another object type. For example, list all companies associated with a specific contact. Returns the associated object IDs and association types.

Operation
Read read
Full name
hubspot.hubspot_list_associations
ParameterTypeRequiredDescription
from_type string yes Source object type (e.g., "contacts", "companies", "deals").
from_id string yes Source object ID.
to_type string yes Target object type to list associations for (e.g., "companies", "contacts").

hubspot_list_owners

List HubSpot CRM owners (users). Returns owner IDs, names, and emails. Useful for assigning owners to contacts, deals, and tickets. Supports pagination with limit and after parameters.

Operation
Read read
Full name
hubspot.hubspot_list_owners
ParameterTypeRequiredDescription
limit integer no Maximum number of owners to return (default 100).
after string no Pagination cursor from a previous response.

hubspot_create_engagement

Create an engagement in HubSpot CRM (note, task, or meeting). Specify the type and provide the relevant properties. For notes: body (HTML content). For tasks: hs_task_body, hs_task_subject, hs_task_status. For meetings: hs_meeting_title, hs_meeting_body, hs_meeting_start_time, hs_meeting_end_time.

Operation
Write write
Full name
hubspot.hubspot_create_engagement
ParameterTypeRequiredDescription
type string yes Engagement type: "notes", "tasks", or "meetings".
body string no Engagement body content (HTML for notes, plain text for tasks).
timestamp string no Engagement timestamp in ISO 8601 format.
owner_id string no HubSpot owner ID to assign the engagement to.
properties object no Additional custom properties as key-value pairs.

hubspot_list_pipelines

List HubSpot CRM pipelines for a given object type. Returns all pipelines and their stages. Commonly used for deals and tickets. Use the object type "deals" or "tickets" to get the respective pipelines.

Operation
Read read
Full name
hubspot.hubspot_list_pipelines
ParameterTypeRequiredDescription
object_type string yes Object type (e.g., "deals", "tickets").

hubspot_list_properties

List HubSpot CRM property definitions for a given object type. Returns all properties including their name, label, type, and field type. Useful for discovering available properties for contacts, companies, deals, or tickets.

Operation
Read read
Full name
hubspot.hubspot_list_properties
ParameterTypeRequiredDescription
object_type string yes Object type (e.g., "contacts", "companies", "deals", "tickets").

hubspot_add_contact_to_list

Add contacts to a HubSpot marketing list. Provide either contact_ids (HubSpot vid IDs) or emails (email addresses), or both. Contacts that are already in the list are silently skipped.

Operation
Write write
Full name
hubspot.hubspot_add_contact_to_list
ParameterTypeRequiredDescription
list_id string yes HubSpot list ID to add contacts to.
contact_ids array no Array of HubSpot contact IDs (vids) to add.
emails array no Array of email addresses to add.

hubspot_list_forms

List HubSpot marketing forms. Returns form IDs, names, types, and creation timestamps. Supports pagination with limit and after parameters.

Operation
Read read
Full name
hubspot.hubspot_list_forms
ParameterTypeRequiredDescription
limit integer no Maximum number of forms to return (default 50).
after string no Pagination cursor from a previous response.