improvement(hubspot): align tools with API docs and expand coverage with delete, list membership, and search tools (#5635)

This commit is contained in:
Waleed
2026-07-13 11:30:18 -07:00
committed by GitHub
parent b1bd2b5cab
commit 0640c0bbac
28 changed files with 1614 additions and 40 deletions
+172 -6
View File
@@ -28,18 +28,22 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
{ label: 'Create Contact', id: 'create_contact' },
{ label: 'Update Contact', id: 'update_contact' },
{ label: 'Search Contacts', id: 'search_contacts' },
{ label: 'Delete Contact', id: 'delete_contact' },
{ label: 'Get Companies', id: 'get_companies' },
{ label: 'Create Company', id: 'create_company' },
{ label: 'Update Company', id: 'update_company' },
{ label: 'Search Companies', id: 'search_companies' },
{ label: 'Delete Company', id: 'delete_company' },
{ label: 'Get Deals', id: 'get_deals' },
{ label: 'Create Deal', id: 'create_deal' },
{ label: 'Update Deal', id: 'update_deal' },
{ label: 'Search Deals', id: 'search_deals' },
{ label: 'Delete Deal', id: 'delete_deal' },
{ label: 'Get Tickets', id: 'get_tickets' },
{ label: 'Create Ticket', id: 'create_ticket' },
{ label: 'Update Ticket', id: 'update_ticket' },
{ label: 'Search Tickets', id: 'search_tickets' },
{ label: 'Delete Ticket', id: 'delete_ticket' },
{ label: 'Get Notes', id: 'get_notes' },
{ label: 'Create Note', id: 'create_note' },
{ label: 'Search Notes', id: 'search_notes' },
@@ -49,10 +53,15 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
{ label: 'Get Properties', id: 'get_properties' },
{ label: 'List Associations', id: 'list_associations' },
{ label: 'Create Association', id: 'create_association' },
{ label: 'Delete Association', id: 'delete_association' },
{ label: 'Get Association Labels', id: 'get_association_labels' },
{ label: 'Get Line Items', id: 'get_line_items' },
{ label: 'Create Line Item', id: 'create_line_item' },
{ label: 'Update Line Item', id: 'update_line_item' },
{ label: 'Search Line Items', id: 'search_line_items' },
{ label: 'Delete Line Item', id: 'delete_line_item' },
{ label: 'Get Quotes', id: 'get_quotes' },
{ label: 'Search Quotes', id: 'search_quotes' },
{ label: 'Get Appointments', id: 'get_appointments' },
{ label: 'Create Appointment', id: 'create_appointment' },
{ label: 'Update Appointment', id: 'update_appointment' },
@@ -61,6 +70,9 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
{ label: 'Get Marketing Events', id: 'get_marketing_events' },
{ label: 'Get Lists', id: 'get_lists' },
{ label: 'Create List', id: 'create_list' },
{ label: 'Get List Members', id: 'get_list_memberships' },
{ label: 'Add List Members', id: 'add_list_memberships' },
{ label: 'Remove List Members', id: 'remove_list_memberships' },
{ label: 'Get Users', id: 'get_users' },
],
value: () => 'get_contacts',
@@ -100,6 +112,14 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
condition: { field: 'operation', value: 'update_contact' },
required: true,
},
{
id: 'contactId',
title: 'Contact ID',
type: 'short-input',
placeholder: 'Numeric ID of the contact to delete',
condition: { field: 'operation', value: 'delete_contact' },
required: true,
},
{
id: 'companyId',
title: 'Company ID or Domain',
@@ -115,6 +135,14 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
condition: { field: 'operation', value: 'update_company' },
required: true,
},
{
id: 'companyId',
title: 'Company ID',
type: 'short-input',
placeholder: 'Numeric ID of the company to delete',
condition: { field: 'operation', value: 'delete_company' },
required: true,
},
{
id: 'dealId',
title: 'Deal ID',
@@ -130,6 +158,14 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
condition: { field: 'operation', value: 'update_deal' },
required: true,
},
{
id: 'dealId',
title: 'Deal ID',
type: 'short-input',
placeholder: 'Numeric ID of the deal to delete',
condition: { field: 'operation', value: 'delete_deal' },
required: true,
},
{
id: 'ticketId',
title: 'Ticket ID',
@@ -145,6 +181,14 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
condition: { field: 'operation', value: 'update_ticket' },
required: true,
},
{
id: 'ticketId',
title: 'Ticket ID',
type: 'short-input',
placeholder: 'Numeric ID of the ticket to delete',
condition: { field: 'operation', value: 'delete_ticket' },
required: true,
},
{
id: 'noteId',
title: 'Note ID',
@@ -166,7 +210,13 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
placeholder: 'e.g., "contacts", "companies", "deals", "tickets"',
condition: {
field: 'operation',
value: ['get_properties', 'list_associations', 'create_association'],
value: [
'get_properties',
'list_associations',
'create_association',
'delete_association',
'get_association_labels',
],
},
required: true,
},
@@ -194,7 +244,10 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
title: 'Record ID',
type: 'short-input',
placeholder: 'ID of the source record',
condition: { field: 'operation', value: ['list_associations', 'create_association'] },
condition: {
field: 'operation',
value: ['list_associations', 'create_association', 'delete_association'],
},
required: true,
},
{
@@ -202,7 +255,15 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
title: 'To Object Type',
type: 'short-input',
placeholder: 'e.g., "emails", "notes", "contacts"',
condition: { field: 'operation', value: ['list_associations', 'create_association'] },
condition: {
field: 'operation',
value: [
'list_associations',
'create_association',
'delete_association',
'get_association_labels',
],
},
required: true,
},
{
@@ -210,7 +271,7 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
title: 'To Record ID',
type: 'short-input',
placeholder: 'ID of the target record',
condition: { field: 'operation', value: 'create_association' },
condition: { field: 'operation', value: ['create_association', 'delete_association'] },
required: true,
},
{
@@ -249,6 +310,14 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
condition: { field: 'operation', value: 'update_line_item' },
required: true,
},
{
id: 'lineItemId',
title: 'Line Item ID',
type: 'short-input',
placeholder: 'Numeric ID of the line item to delete',
condition: { field: 'operation', value: 'delete_line_item' },
required: true,
},
{
id: 'quoteId',
title: 'Quote ID',
@@ -292,6 +361,28 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
placeholder: 'Leave empty to search all lists',
condition: { field: 'operation', value: 'get_lists' },
},
{
id: 'listId',
title: 'List ID',
type: 'short-input',
placeholder: 'ID of the list',
condition: {
field: 'operation',
value: ['get_list_memberships', 'add_list_memberships', 'remove_list_memberships'],
},
required: true,
},
{
id: 'recordIds',
title: 'Record IDs',
type: 'short-input',
placeholder: 'Comma-separated record IDs (e.g., "123,456") or JSON array',
condition: {
field: 'operation',
value: ['add_list_memberships', 'remove_list_memberships'],
},
required: true,
},
{
id: 'listName',
title: 'List Name',
@@ -347,6 +438,7 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
id: 'propertiesToSet',
title: 'Properties',
type: 'long-input',
required: true,
placeholder:
'JSON object with properties (e.g., {"email": "test@example.com", "firstname": "John"})',
condition: {
@@ -567,12 +659,15 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
'list_associations',
'get_marketing_events',
'get_lists',
'get_list_memberships',
'search_contacts',
'search_companies',
'search_deals',
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
],
},
},
@@ -600,12 +695,15 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
'get_users',
'get_marketing_events',
'get_lists',
'get_list_memberships',
'search_contacts',
'search_companies',
'search_deals',
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
],
},
},
@@ -623,6 +721,8 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
'get_lists',
],
},
@@ -642,6 +742,8 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
],
},
wandConfig: {
@@ -853,6 +955,8 @@ Return ONLY the JSON array of filter groups - no explanations, no markdown, no e
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
],
},
wandConfig: {
@@ -985,6 +1089,8 @@ Return ONLY the JSON array of sort objects - no explanations, no markdown, no ex
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
],
},
wandConfig: {
@@ -1149,6 +1255,18 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
'hubspot_get_properties',
'hubspot_list_associations',
'hubspot_create_association',
'hubspot_delete_association',
'hubspot_get_association_labels',
'hubspot_delete_contact',
'hubspot_delete_company',
'hubspot_delete_deal',
'hubspot_delete_ticket',
'hubspot_delete_line_item',
'hubspot_search_line_items',
'hubspot_search_quotes',
'hubspot_get_list_memberships',
'hubspot_add_list_memberships',
'hubspot_remove_list_memberships',
'hubspot_list_line_items',
'hubspot_get_line_item',
'hubspot_create_line_item',
@@ -1181,6 +1299,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return 'hubspot_update_contact'
case 'search_contacts':
return 'hubspot_search_contacts'
case 'delete_contact':
return 'hubspot_delete_contact'
case 'get_companies':
return params.companyId ? 'hubspot_get_company' : 'hubspot_list_companies'
case 'create_company':
@@ -1189,6 +1309,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return 'hubspot_update_company'
case 'search_companies':
return 'hubspot_search_companies'
case 'delete_company':
return 'hubspot_delete_company'
case 'get_deals':
return params.dealId ? 'hubspot_get_deal' : 'hubspot_list_deals'
case 'create_deal':
@@ -1197,6 +1319,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return 'hubspot_update_deal'
case 'search_deals':
return 'hubspot_search_deals'
case 'delete_deal':
return 'hubspot_delete_deal'
case 'get_tickets':
return params.ticketId ? 'hubspot_get_ticket' : 'hubspot_list_tickets'
case 'create_ticket':
@@ -1205,6 +1329,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return 'hubspot_update_ticket'
case 'search_tickets':
return 'hubspot_search_tickets'
case 'delete_ticket':
return 'hubspot_delete_ticket'
case 'get_notes':
return params.noteId ? 'hubspot_get_note' : 'hubspot_list_notes'
case 'create_note':
@@ -1223,14 +1349,24 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return 'hubspot_list_associations'
case 'create_association':
return 'hubspot_create_association'
case 'delete_association':
return 'hubspot_delete_association'
case 'get_association_labels':
return 'hubspot_get_association_labels'
case 'get_line_items':
return params.lineItemId ? 'hubspot_get_line_item' : 'hubspot_list_line_items'
case 'create_line_item':
return 'hubspot_create_line_item'
case 'update_line_item':
return 'hubspot_update_line_item'
case 'search_line_items':
return 'hubspot_search_line_items'
case 'delete_line_item':
return 'hubspot_delete_line_item'
case 'get_quotes':
return params.quoteId ? 'hubspot_get_quote' : 'hubspot_list_quotes'
case 'search_quotes':
return 'hubspot_search_quotes'
case 'get_appointments':
return params.appointmentId ? 'hubspot_get_appointment' : 'hubspot_list_appointments'
case 'create_appointment':
@@ -1247,6 +1383,12 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
return params.listId ? 'hubspot_get_list' : 'hubspot_list_lists'
case 'create_list':
return 'hubspot_create_list'
case 'get_list_memberships':
return 'hubspot_get_list_memberships'
case 'add_list_memberships':
return 'hubspot_add_list_memberships'
case 'remove_list_memberships':
return 'hubspot_remove_list_memberships'
default:
throw new Error(`Unknown operation: ${params.operation}`)
}
@@ -1315,6 +1457,8 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
'search_tickets',
'search_notes',
'search_emails',
'search_line_items',
'search_quotes',
]
if (searchProperties && searchOps.includes(operation as string)) {
cleanParams.properties = searchProperties
@@ -1426,6 +1570,10 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
sorts: { type: 'json', description: 'Sort order (JSON array of strings or objects)' },
searchProperties: { type: 'json', description: 'Properties to return in search (JSON array)' },
listName: { type: 'string', description: 'Name for new list' },
recordIds: {
type: 'json',
description: 'Record IDs for list membership changes (JSON array or comma-separated)',
},
objectTypeId: { type: 'string', description: 'Object type ID for list' },
processingType: { type: 'string', description: 'List processing type (MANUAL or DYNAMIC)' },
},
@@ -1469,12 +1617,23 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
type: 'string',
description: 'Associated target record ID (for create association)',
},
labels: { type: 'json', description: 'Association labels (for create association)' },
labels: {
type: 'json',
description: 'Association labels (for create association and get association labels)',
},
deleted: { type: 'boolean', description: 'Whether the record was archived (for delete)' },
memberships: {
type: 'json',
description: 'Array of list membership records (recordId, membershipTimestamp)',
},
recordIdsAdded: { type: 'json', description: 'Record IDs added to the list' },
recordIdsRemoved: { type: 'json', description: 'Record IDs removed from the list' },
recordIdsMissing: { type: 'json', description: 'Requested record IDs that were not found' },
total: { type: 'number', description: 'Total number of matching results (for search)' },
paging: { type: 'json', description: 'Pagination info with next/prev cursors' },
metadata: { type: 'json', description: 'Operation metadata' },
success: { type: 'boolean', description: 'Operation success status' },
} as any,
},
triggerAllowed: true,
triggers: {
enabled: true,
@@ -1612,6 +1771,13 @@ export const HubSpotBlockMeta = {
content:
'# Audit Contacts Missing Activity\n\nSurface leads with no recorded email history.\n\n## Steps\n1. Get properties for contacts to read the hs_lead_status options and confirm the target stage value.\n2. Search contacts filtered to that lead status, paginating through all results.\n3. For each contact, list associations to emails and flag those with zero associated emails.\n4. Collect the contacts that need follow-up.\n\n## Output\nReturn the list of contact IDs with no logged email activity, ready for backfill.',
},
{
name: 'maintain-static-list',
description:
'Search HubSpot records and keep a manual list in sync by adding and removing members.',
content:
'# Maintain Static List\n\nKeep a manual (static) list aligned with a search criterion.\n\n## Steps\n1. Search contacts (or other records) matching the target criteria, paginating through all results.\n2. Get list members to read the current membership of the list.\n3. Add list members for matching records that are missing.\n4. Remove list members for records that no longer match.\n\n## Output\nReturn the list ID with counts of records added and removed.',
},
{
name: 'inspect-property-options',
description: 'Read the enumeration (picklist) values for a HubSpot property.',
@@ -0,0 +1,112 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotAddListMembershipsParams,
HubSpotAddListMembershipsResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotAddListMemberships')
export const hubspotAddListMembershipsTool: ToolConfig<
HubSpotAddListMembershipsParams,
HubSpotAddListMembershipsResponse
> = {
id: 'hubspot_add_list_memberships',
name: 'Add List Members in HubSpot',
description: 'Add records to a manual (static) HubSpot list by record ID',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
listId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the list to add records to (MANUAL or SNAPSHOT lists only)',
},
recordIds: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description:
'Record IDs to add to the list, as a JSON array (e.g., ["123","456"]) or comma-separated string',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/lists/${params.listId.trim()}/memberships/add`,
method: 'PUT',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}
},
body: (params) => {
let recordIds = params.recordIds
if (typeof recordIds === 'string') {
const trimmed = recordIds.trim()
if (trimmed.startsWith('[')) {
try {
recordIds = JSON.parse(trimmed)
} catch (e) {
throw new Error(`Invalid JSON for recordIds: ${(e as Error).message}`)
}
} else {
recordIds = trimmed.split(',')
}
}
if (!Array.isArray(recordIds)) {
throw new Error('recordIds must be an array of record IDs')
}
const ids = recordIds.map((id) => String(id).trim()).filter(Boolean)
if (ids.length === 0) {
throw new Error('At least one record ID is required')
}
return ids
},
},
transformResponse: async (response: Response) => {
const data = await response.json().catch(() => ({}))
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to add records to HubSpot list')
}
return {
success: true,
output: {
recordIdsAdded: data.recordIdsAdded ?? [],
recordIdsMissing: data.recordIdsMissing ?? [],
success: true,
},
}
},
outputs: {
recordIdsAdded: {
type: 'array',
description: 'IDs of the records that were added to the list',
items: { type: 'string' },
},
recordIdsMissing: {
type: 'array',
description: 'IDs of the requested records that were not found',
items: { type: 'string' },
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+1 -1
View File
@@ -34,7 +34,7 @@ export const hubspotCreateAppointmentTool: ToolConfig<
required: true,
visibility: 'user-or-llm',
description:
'Appointment properties as JSON object (e.g., {"hs_appointment_name": "Discovery Call", "hs_appointment_start": "2024-01-15T10:00:00Z", "hs_appointment_end": "2024-01-15T11:00:00Z"})',
'Appointment properties as JSON object. Must include hs_appointment_start (e.g., {"hs_appointment_name": "Discovery Call", "hs_appointment_start": "2024-01-15T10:00:00Z", "hs_appointment_end": "2024-01-15T11:00:00Z"})',
},
associations: {
type: 'array',
+1 -1
View File
@@ -30,7 +30,7 @@ export const hubspotCreateDealTool: ToolConfig<HubSpotCreateDealParams, HubSpotC
required: true,
visibility: 'user-or-llm',
description:
'Deal properties as JSON object. Must include dealname (e.g., {"dealname": "New Deal", "amount": "5000", "dealstage": "appointmentscheduled"})',
'Deal properties as JSON object. Should include dealname and dealstage, plus pipeline when the account has multiple pipelines (e.g., {"dealname": "New Deal", "amount": "5000", "dealstage": "appointmentscheduled"})',
},
associations: {
type: 'array',
@@ -0,0 +1,97 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotDeleteAssociationParams,
HubSpotDeleteAssociationResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteAssociation')
export const hubspotDeleteAssociationTool: ToolConfig<
HubSpotDeleteAssociationParams,
HubSpotDeleteAssociationResponse
> = {
id: 'hubspot_delete_association',
name: 'Delete Association in HubSpot',
description: 'Remove all associations between two HubSpot records',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
objectType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The source object type (e.g., "contacts", "companies", "deals")',
},
objectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the source record',
},
toObjectType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The target object type (e.g., "emails", "notes", "contacts")',
},
toObjectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the target record',
},
},
request: {
url: (params) => {
const from = `${encodeURIComponent(params.objectType.trim())}/${encodeURIComponent(params.objectId.trim())}`
const to = `${encodeURIComponent(params.toObjectType.trim())}/${encodeURIComponent(params.toObjectId.trim())}`
return `https://api.hubapi.com/crm/v4/objects/${from}/associations/${to}`
},
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete association in HubSpot')
}
return {
success: true,
output: {
fromObjectId: params?.objectId ?? '',
toObjectId: params?.toObjectId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
fromObjectId: { type: 'string', description: 'Source record ID' },
toObjectId: { type: 'string', description: 'Target record ID' },
deleted: { type: 'boolean', description: 'Whether the associations were removed' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+73
View File
@@ -0,0 +1,73 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotDeleteCompanyParams,
HubSpotDeleteCompanyResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteCompany')
export const hubspotDeleteCompanyTool: ToolConfig<
HubSpotDeleteCompanyParams,
HubSpotDeleteCompanyResponse
> = {
id: 'hubspot_delete_company',
name: 'Delete Company from HubSpot',
description: 'Archive a company in HubSpot by ID (moves it to the recycling bin)',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
companyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The numeric ID of the company to delete',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/objects/companies/${params.companyId.trim()}`,
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete company from HubSpot')
}
return {
success: true,
output: {
companyId: params?.companyId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
companyId: { type: 'string', description: 'ID of the deleted company' },
deleted: { type: 'boolean', description: 'Whether the company was archived' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+73
View File
@@ -0,0 +1,73 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotDeleteContactParams,
HubSpotDeleteContactResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteContact')
export const hubspotDeleteContactTool: ToolConfig<
HubSpotDeleteContactParams,
HubSpotDeleteContactResponse
> = {
id: 'hubspot_delete_contact',
name: 'Delete Contact from HubSpot',
description: 'Archive a contact in HubSpot by ID (moves it to the recycling bin)',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The numeric ID of the contact to delete',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/objects/contacts/${params.contactId.trim()}`,
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete contact from HubSpot')
}
return {
success: true,
output: {
contactId: params?.contactId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
contactId: { type: 'string', description: 'ID of the deleted contact' },
deleted: { type: 'boolean', description: 'Whether the contact was archived' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+68
View File
@@ -0,0 +1,68 @@
import { createLogger } from '@sim/logger'
import type { HubSpotDeleteDealParams, HubSpotDeleteDealResponse } from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteDeal')
export const hubspotDeleteDealTool: ToolConfig<HubSpotDeleteDealParams, HubSpotDeleteDealResponse> =
{
id: 'hubspot_delete_deal',
name: 'Delete Deal from HubSpot',
description: 'Archive a deal in HubSpot by ID (moves it to the recycling bin)',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
dealId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The numeric ID of the deal to delete',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/objects/deals/${params.dealId.trim()}`,
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete deal from HubSpot')
}
return {
success: true,
output: {
dealId: params?.dealId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
dealId: { type: 'string', description: 'ID of the deleted deal' },
deleted: { type: 'boolean', description: 'Whether the deal was archived' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
@@ -0,0 +1,73 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotDeleteLineItemParams,
HubSpotDeleteLineItemResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteLineItem')
export const hubspotDeleteLineItemTool: ToolConfig<
HubSpotDeleteLineItemParams,
HubSpotDeleteLineItemResponse
> = {
id: 'hubspot_delete_line_item',
name: 'Delete Line Item from HubSpot',
description: 'Archive a line item in HubSpot by ID (moves it to the recycling bin)',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
lineItemId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The numeric ID of the line item to delete',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/objects/line_items/${params.lineItemId.trim()}`,
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete line item from HubSpot')
}
return {
success: true,
output: {
lineItemId: params?.lineItemId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
lineItemId: { type: 'string', description: 'ID of the deleted line item' },
deleted: { type: 'boolean', description: 'Whether the line item was archived' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
+70
View File
@@ -0,0 +1,70 @@
import { createLogger } from '@sim/logger'
import type { HubSpotDeleteTicketParams, HubSpotDeleteTicketResponse } from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotDeleteTicket')
export const hubspotDeleteTicketTool: ToolConfig<
HubSpotDeleteTicketParams,
HubSpotDeleteTicketResponse
> = {
id: 'hubspot_delete_ticket',
name: 'Delete Ticket from HubSpot',
description: 'Archive a ticket in HubSpot by ID (moves it to the recycling bin)',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
ticketId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The numeric ID of the ticket to delete',
},
},
request: {
url: (params) => `https://api.hubapi.com/crm/v3/objects/tickets/${params.ticketId.trim()}`,
method: 'DELETE',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json().catch(() => ({}))
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to delete ticket from HubSpot')
}
return {
success: true,
output: {
ticketId: params?.ticketId ?? '',
deleted: true,
success: true,
},
}
},
outputs: {
ticketId: { type: 'string', description: 'ID of the deleted ticket' },
deleted: { type: 'boolean', description: 'Whether the ticket was archived' },
success: { type: 'boolean', description: 'Operation success status' },
},
}
@@ -0,0 +1,100 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotGetAssociationLabelsParams,
HubSpotGetAssociationLabelsResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotGetAssociationLabels')
export const hubspotGetAssociationLabelsTool: ToolConfig<
HubSpotGetAssociationLabelsParams,
HubSpotGetAssociationLabelsResponse
> = {
id: 'hubspot_get_association_labels',
name: 'Get Association Labels from HubSpot',
description:
'Retrieve the association types (category, typeId, label) defined between two HubSpot object types',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
objectType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The source object type (e.g., "contacts", "companies", "deals")',
},
toObjectType: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The target object type (e.g., "emails", "notes", "contacts")',
},
},
request: {
url: (params) => {
const from = encodeURIComponent(params.objectType.trim())
const to = encodeURIComponent(params.toObjectType.trim())
return `https://api.hubapi.com/crm/v4/associations/${from}/${to}/labels`
},
method: 'GET',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to get association labels from HubSpot')
}
return {
success: true,
output: {
labels: data.results || [],
success: true,
},
}
},
outputs: {
labels: {
type: 'array',
description: 'Association types defined between the two object types',
items: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Association category (HUBSPOT_DEFINED or USER_DEFINED)',
},
typeId: { type: 'number', description: 'Association type ID' },
label: {
type: 'string',
description: 'Human-readable label (null for unlabeled defaults)',
optional: true,
},
},
},
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
@@ -0,0 +1,113 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotGetListMembershipsParams,
HubSpotGetListMembershipsResponse,
} from '@/tools/hubspot/types'
import { METADATA_OUTPUT, PAGING_OUTPUT } from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotGetListMemberships')
export const hubspotGetListMembershipsTool: ToolConfig<
HubSpotGetListMembershipsParams,
HubSpotGetListMembershipsResponse
> = {
id: 'hubspot_get_list_memberships',
name: 'Get List Members from HubSpot',
description: 'Retrieve the record IDs that are members of a HubSpot list, ordered by record ID',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
listId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the list to read members from',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results per page (max 250, default 100)',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page of results (from previous response)',
},
},
request: {
url: (params) => {
const baseUrl = `https://api.hubapi.com/crm/v3/lists/${params.listId.trim()}/memberships`
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit)
if (params.after) queryParams.append('after', params.after)
const queryString = queryParams.toString()
return queryString ? `${baseUrl}?${queryString}` : baseUrl
},
method: 'GET',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
}
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to get list members from HubSpot')
}
const results = data.results || []
return {
success: true,
output: {
memberships: results,
paging: data.paging ?? null,
metadata: {
totalReturned: results.length,
hasMore: !!data.paging?.next,
},
success: true,
},
}
},
outputs: {
memberships: {
type: 'array',
description: 'Records that are members of the list',
items: {
type: 'object',
properties: {
recordId: { type: 'string', description: 'ID of the member record' },
membershipTimestamp: {
type: 'string',
description: 'When the record was added to the list',
optional: true,
},
},
},
},
paging: PAGING_OUTPUT,
metadata: METADATA_OUTPUT,
success: { type: 'boolean', description: 'Operation success status' },
},
}
+12
View File
@@ -1,3 +1,4 @@
export { hubspotAddListMembershipsTool } from './add_list_memberships'
export { hubspotCreateAppointmentTool } from './create_appointment'
export { hubspotCreateAssociationTool } from './create_association'
export { hubspotCreateCompanyTool } from './create_company'
@@ -8,7 +9,14 @@ export { hubspotCreateLineItemTool } from './create_line_item'
export { hubspotCreateListTool } from './create_list'
export { hubspotCreateNoteTool } from './create_note'
export { hubspotCreateTicketTool } from './create_ticket'
export { hubspotDeleteAssociationTool } from './delete_association'
export { hubspotDeleteCompanyTool } from './delete_company'
export { hubspotDeleteContactTool } from './delete_contact'
export { hubspotDeleteDealTool } from './delete_deal'
export { hubspotDeleteLineItemTool } from './delete_line_item'
export { hubspotDeleteTicketTool } from './delete_ticket'
export { hubspotGetAppointmentTool } from './get_appointment'
export { hubspotGetAssociationLabelsTool } from './get_association_labels'
export { hubspotGetCartTool } from './get_cart'
export { hubspotGetCompanyTool } from './get_company'
export { hubspotGetContactTool } from './get_contact'
@@ -16,6 +24,7 @@ export { hubspotGetDealTool } from './get_deal'
export { hubspotGetEmailTool } from './get_email'
export { hubspotGetLineItemTool } from './get_line_item'
export { hubspotGetListTool } from './get_list'
export { hubspotGetListMembershipsTool } from './get_list_memberships'
export { hubspotGetMarketingEventTool } from './get_marketing_event'
export { hubspotGetNoteTool } from './get_note'
export { hubspotGetPropertiesTool } from './get_properties'
@@ -36,11 +45,14 @@ export { hubspotListNotesTool } from './list_notes'
export { hubspotListOwnersTool } from './list_owners'
export { hubspotListQuotesTool } from './list_quotes'
export { hubspotListTicketsTool } from './list_tickets'
export { hubspotRemoveListMembershipsTool } from './remove_list_memberships'
export { hubspotSearchCompaniesTool } from './search_companies'
export { hubspotSearchContactsTool } from './search_contacts'
export { hubspotSearchDealsTool } from './search_deals'
export { hubspotSearchEmailsTool } from './search_emails'
export { hubspotSearchLineItemsTool } from './search_line_items'
export { hubspotSearchNotesTool } from './search_notes'
export { hubspotSearchQuotesTool } from './search_quotes'
export { hubspotSearchTicketsTool } from './search_tickets'
export { hubspotUpdateAppointmentTool } from './update_appointment'
export { hubspotUpdateCompanyTool } from './update_company'
+1 -1
View File
@@ -84,7 +84,7 @@ export const hubspotListListsTool: ToolConfig<HubSpotListListsParams, HubSpotLis
paging:
data.hasMore === true && data.offset != null
? { next: { after: String(data.offset) } }
: undefined,
: null,
metadata: {
totalReturned: lists.length,
total: data.total ?? null,
@@ -49,7 +49,7 @@ export const hubspotListMarketingEventsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = 'https://api.hubapi.com/marketing/marketing-events/v3'
const baseUrl = 'https://api.hubapi.com/marketing/v3/marketing-events'
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit)
if (params.after) queryParams.append('after', params.after)
@@ -0,0 +1,113 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotRemoveListMembershipsParams,
HubSpotRemoveListMembershipsResponse,
} from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotRemoveListMemberships')
export const hubspotRemoveListMembershipsTool: ToolConfig<
HubSpotRemoveListMembershipsParams,
HubSpotRemoveListMembershipsResponse
> = {
id: 'hubspot_remove_list_memberships',
name: 'Remove List Members in HubSpot',
description: 'Remove records from a manual (static) HubSpot list by record ID',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
listId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the list to remove records from (MANUAL or SNAPSHOT lists only)',
},
recordIds: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description:
'Record IDs to remove from the list, as a JSON array (e.g., ["123","456"]) or comma-separated string',
},
},
request: {
url: (params) =>
`https://api.hubapi.com/crm/v3/lists/${params.listId.trim()}/memberships/remove`,
method: 'PUT',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}
},
body: (params) => {
let recordIds = params.recordIds
if (typeof recordIds === 'string') {
const trimmed = recordIds.trim()
if (trimmed.startsWith('[')) {
try {
recordIds = JSON.parse(trimmed)
} catch (e) {
throw new Error(`Invalid JSON for recordIds: ${(e as Error).message}`)
}
} else {
recordIds = trimmed.split(',')
}
}
if (!Array.isArray(recordIds)) {
throw new Error('recordIds must be an array of record IDs')
}
const ids = recordIds.map((id) => String(id).trim()).filter(Boolean)
if (ids.length === 0) {
throw new Error('At least one record ID is required')
}
return ids
},
},
transformResponse: async (response: Response) => {
const data = await response.json().catch(() => ({}))
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to remove records from HubSpot list')
}
return {
success: true,
output: {
recordIdsRemoved: data.recordIdsRemoved ?? [],
recordIdsMissing: data.recordIdsMissing ?? [],
success: true,
},
}
},
outputs: {
recordIdsRemoved: {
type: 'array',
description: 'IDs of the records that were removed from the list',
items: { type: 'string' },
},
recordIdsMissing: {
type: 'array',
description: 'IDs of the requested records that were not found',
items: { type: 'string' },
},
success: { type: 'boolean', description: 'Operation success status' },
},
}
+1 -1
View File
@@ -61,7 +61,7 @@ export const hubspotSearchCompaniesTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 100)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+1 -1
View File
@@ -61,7 +61,7 @@ export const hubspotSearchContactsTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 100)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+1 -1
View File
@@ -57,7 +57,7 @@ export const hubspotSearchDealsTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 200)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+1 -1
View File
@@ -57,7 +57,7 @@ export const hubspotSearchEmailsTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 100)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+155
View File
@@ -0,0 +1,155 @@
import { createLogger } from '@sim/logger'
import type {
HubSpotSearchLineItemsParams,
HubSpotSearchLineItemsResponse,
} from '@/tools/hubspot/types'
import { LINE_ITEMS_ARRAY_OUTPUT, METADATA_OUTPUT, PAGING_OUTPUT } from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotSearchLineItems')
export const hubspotSearchLineItemsTool: ToolConfig<
HubSpotSearchLineItemsParams,
HubSpotSearchLineItemsResponse
> = {
id: 'hubspot_search_line_items',
name: 'Search Line Items in HubSpot',
description: 'Search for line items in HubSpot using filters, sorting, and queries',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
filterGroups: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" (e.g., "EQ", "NEQ", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN"), and "value"',
},
sorts: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of sort objects as JSON with "propertyName" and "direction" ("ASCENDING" or "DESCENDING")',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search query string to match against line item name and other text fields',
},
properties: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of HubSpot property names to return (e.g., ["name", "quantity", "price"])',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page (from previous response)',
},
},
request: {
url: () => 'https://api.hubapi.com/crm/v3/objects/line_items/search',
method: 'POST',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}
},
body: (params) => {
const body: Record<string, unknown> = {}
if (params.filterGroups) {
let parsed = params.filterGroups
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for filterGroups: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.filterGroups = parsed
}
if (params.sorts) {
let parsed = params.sorts
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for sorts: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.sorts = parsed
}
if (params.query) body.query = params.query
if (params.properties) {
let parsed = params.properties
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for properties: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.properties = parsed
}
if (params.limit) body.limit = params.limit
if (params.after) body.after = params.after
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to search line items in HubSpot')
}
return {
success: true,
output: {
lineItems: data.results || [],
total: data.total ?? 0,
paging: data.paging ?? null,
metadata: {
totalReturned: data.results?.length || 0,
hasMore: !!data.paging?.next,
},
success: true,
},
}
},
outputs: {
lineItems: LINE_ITEMS_ARRAY_OUTPUT,
total: { type: 'number', description: 'Total number of matching line items', optional: true },
paging: PAGING_OUTPUT,
metadata: METADATA_OUTPUT,
success: { type: 'boolean', description: 'Operation success status' },
},
}
+1 -1
View File
@@ -57,7 +57,7 @@ export const hubspotSearchNotesTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 100)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+152
View File
@@ -0,0 +1,152 @@
import { createLogger } from '@sim/logger'
import type { HubSpotSearchQuotesParams, HubSpotSearchQuotesResponse } from '@/tools/hubspot/types'
import { METADATA_OUTPUT, PAGING_OUTPUT, QUOTES_ARRAY_OUTPUT } from '@/tools/hubspot/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('HubSpotSearchQuotes')
export const hubspotSearchQuotesTool: ToolConfig<
HubSpotSearchQuotesParams,
HubSpotSearchQuotesResponse
> = {
id: 'hubspot_search_quotes',
name: 'Search Quotes in HubSpot',
description: 'Search for quotes in HubSpot using filters, sorting, and queries',
version: '1.0.0',
oauth: {
required: true,
provider: 'hubspot',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the HubSpot API',
},
filterGroups: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of filter groups as JSON. Each group contains "filters" array with objects having "propertyName", "operator" (e.g., "EQ", "NEQ", "CONTAINS_TOKEN", "NOT_CONTAINS_TOKEN"), and "value"',
},
sorts: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of sort objects as JSON with "propertyName" and "direction" ("ASCENDING" or "DESCENDING")',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search query string to match against quote title and other text fields',
},
properties: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of HubSpot property names to return (e.g., ["hs_title", "hs_expiration_date"])',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page (from previous response)',
},
},
request: {
url: () => 'https://api.hubapi.com/crm/v3/objects/quotes/search',
method: 'POST',
headers: (params) => {
if (!params.accessToken) {
throw new Error('Access token is required')
}
return {
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}
},
body: (params) => {
const body: Record<string, unknown> = {}
if (params.filterGroups) {
let parsed = params.filterGroups
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for filterGroups: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.filterGroups = parsed
}
if (params.sorts) {
let parsed = params.sorts
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for sorts: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.sorts = parsed
}
if (params.query) body.query = params.query
if (params.properties) {
let parsed = params.properties
if (typeof parsed === 'string') {
try {
parsed = JSON.parse(parsed)
} catch (e) {
throw new Error(`Invalid JSON for properties: ${(e as Error).message}`)
}
}
if (Array.isArray(parsed) && parsed.length > 0) body.properties = parsed
}
if (params.limit) body.limit = params.limit
if (params.after) body.after = params.after
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
logger.error('HubSpot API request failed', { data, status: response.status })
throw new Error(data.message || 'Failed to search quotes in HubSpot')
}
return {
success: true,
output: {
quotes: data.results || [],
total: data.total ?? 0,
paging: data.paging ?? null,
metadata: {
totalReturned: data.results?.length || 0,
hasMore: !!data.paging?.next,
},
success: true,
},
}
},
outputs: {
quotes: QUOTES_ARRAY_OUTPUT,
total: { type: 'number', description: 'Total number of matching quotes', optional: true },
paging: PAGING_OUTPUT,
metadata: METADATA_OUTPUT,
success: { type: 'boolean', description: 'Operation success status' },
},
}
+1 -1
View File
@@ -60,7 +60,7 @@ export const hubspotSearchTicketsTool: ToolConfig<
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of results to return (max 200)',
description: 'Maximum number of results to return (max 200, default 10)',
},
after: {
type: 'string',
+195 -22
View File
@@ -930,7 +930,7 @@ export interface HubSpotGetUsersParams {
export interface HubSpotListContactsResponse extends ToolResponse {
output: {
contacts: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: {
totalReturned: number
hasMore: boolean
@@ -1006,7 +1006,7 @@ export interface HubSpotSearchContactsResponse extends ToolResponse {
output: {
contacts: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: {
totalReturned: number
hasMore: boolean
@@ -1040,7 +1040,7 @@ export type HubSpotListCompaniesParams = HubSpotListContactsParams
export type HubSpotListCompaniesResponse = Omit<HubSpotListContactsResponse, 'output'> & {
output: {
companies: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: {
totalReturned: number
hasMore: boolean
@@ -1048,7 +1048,9 @@ export type HubSpotListCompaniesResponse = Omit<HubSpotListContactsResponse, 'ou
success: boolean
}
}
export type HubSpotGetCompanyParams = HubSpotGetContactParams & { companyId: string }
export type HubSpotGetCompanyParams = Omit<HubSpotGetContactParams, 'contactId'> & {
companyId: string
}
export type HubSpotGetCompanyResponse = Omit<HubSpotGetContactResponse, 'output'> & {
output: {
company: HubSpotContact
@@ -1064,7 +1066,9 @@ export type HubSpotCreateCompanyResponse = Omit<HubSpotCreateContactResponse, 'o
success: boolean
}
}
export type HubSpotUpdateCompanyParams = HubSpotUpdateContactParams & { companyId: string }
export type HubSpotUpdateCompanyParams = Omit<HubSpotUpdateContactParams, 'contactId'> & {
companyId: string
}
export type HubSpotUpdateCompanyResponse = Omit<HubSpotUpdateContactResponse, 'output'> & {
output: {
company: HubSpotContact
@@ -1077,7 +1081,7 @@ export interface HubSpotSearchCompaniesResponse extends ToolResponse {
output: {
companies: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: {
totalReturned: number
hasMore: boolean
@@ -1092,7 +1096,7 @@ export type HubSpotListDealsParams = HubSpotListContactsParams
export type HubSpotListDealsResponse = Omit<HubSpotListContactsResponse, 'output'> & {
output: {
deals: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1116,7 +1120,7 @@ export interface HubSpotSearchDealsResponse extends ToolResponse {
output: {
deals: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1128,7 +1132,7 @@ export type HubSpotListTicketsParams = HubSpotListContactsParams
export type HubSpotListTicketsResponse = ToolResponse & {
output: {
tickets: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1154,7 +1158,7 @@ export interface HubSpotSearchTicketsResponse extends ToolResponse {
output: {
tickets: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1166,7 +1170,7 @@ export type HubSpotListLineItemsParams = HubSpotListContactsParams
export type HubSpotListLineItemsResponse = ToolResponse & {
output: {
lineItems: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1194,7 +1198,7 @@ export type HubSpotListQuotesParams = HubSpotListContactsParams
export type HubSpotListQuotesResponse = ToolResponse & {
output: {
quotes: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1212,7 +1216,7 @@ export type HubSpotListAppointmentsParams = HubSpotListContactsParams
export type HubSpotListAppointmentsResponse = ToolResponse & {
output: {
appointments: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1240,7 +1244,7 @@ export type HubSpotListCartsParams = HubSpotListContactsParams
export type HubSpotListCartsResponse = ToolResponse & {
output: {
carts: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1275,7 +1279,7 @@ export interface HubSpotListOwnersParams {
export interface HubSpotListOwnersResponse extends ToolResponse {
output: {
owners: HubSpotOwner[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1311,7 +1315,7 @@ export interface HubSpotListMarketingEventsParams {
export interface HubSpotListMarketingEventsResponse extends ToolResponse {
output: {
events: HubSpotMarketingEvent[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1351,7 +1355,7 @@ export interface HubSpotListListsParams {
export interface HubSpotListListsResponse extends ToolResponse {
output: {
lists: HubSpotList[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; total: number | null; hasMore: boolean }
success: boolean
}
@@ -1399,7 +1403,7 @@ export type HubSpotListNotesParams = HubSpotListContactsParams
export type HubSpotListNotesResponse = ToolResponse & {
output: {
notes: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1409,7 +1413,7 @@ export interface HubSpotSearchNotesResponse extends ToolResponse {
output: {
notes: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1429,7 +1433,7 @@ export type HubSpotListEmailsParams = HubSpotListContactsParams
export type HubSpotListEmailsResponse = ToolResponse & {
output: {
emails: HubSpotContact[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1439,7 +1443,7 @@ export interface HubSpotSearchEmailsResponse extends ToolResponse {
output: {
emails: HubSpotContact[]
total: number
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1506,7 +1510,7 @@ export interface HubSpotListAssociationsParams {
export interface HubSpotListAssociationsResponse extends ToolResponse {
output: {
results: HubSpotAssociatedObject[]
paging?: HubSpotPaging
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
@@ -1531,6 +1535,163 @@ export interface HubSpotCreateAssociationResponse extends ToolResponse {
}
}
export interface HubSpotDeleteAssociationParams {
accessToken: string
objectType: string
objectId: string
toObjectType: string
toObjectId: string
}
export interface HubSpotDeleteAssociationResponse extends ToolResponse {
output: {
fromObjectId: string
toObjectId: string
deleted: boolean
success: boolean
}
}
export interface HubSpotAssociationLabel {
category: string
typeId: number
label: string | null
}
export interface HubSpotGetAssociationLabelsParams {
accessToken: string
objectType: string
toObjectType: string
}
export interface HubSpotGetAssociationLabelsResponse extends ToolResponse {
output: {
labels: HubSpotAssociationLabel[]
success: boolean
}
}
// Record deletion (archive)
export interface HubSpotDeleteContactParams {
accessToken: string
contactId: string
}
export interface HubSpotDeleteContactResponse extends ToolResponse {
output: { contactId: string; deleted: boolean; success: boolean }
}
export interface HubSpotDeleteCompanyParams {
accessToken: string
companyId: string
}
export interface HubSpotDeleteCompanyResponse extends ToolResponse {
output: { companyId: string; deleted: boolean; success: boolean }
}
export interface HubSpotDeleteDealParams {
accessToken: string
dealId: string
}
export interface HubSpotDeleteDealResponse extends ToolResponse {
output: { dealId: string; deleted: boolean; success: boolean }
}
export interface HubSpotDeleteTicketParams {
accessToken: string
ticketId: string
}
export interface HubSpotDeleteTicketResponse extends ToolResponse {
output: { ticketId: string; deleted: boolean; success: boolean }
}
export interface HubSpotDeleteLineItemParams {
accessToken: string
lineItemId: string
}
export interface HubSpotDeleteLineItemResponse extends ToolResponse {
output: { lineItemId: string; deleted: boolean; success: boolean }
}
// List memberships
export interface HubSpotListMembership {
recordId: string
membershipTimestamp?: string
}
export interface HubSpotAddListMembershipsParams {
accessToken: string
listId: string
recordIds: string[] | string
}
export interface HubSpotAddListMembershipsResponse extends ToolResponse {
output: {
recordIdsAdded: string[]
recordIdsMissing: string[]
success: boolean
}
}
export interface HubSpotRemoveListMembershipsParams {
accessToken: string
listId: string
recordIds: string[] | string
}
export interface HubSpotRemoveListMembershipsResponse extends ToolResponse {
output: {
recordIdsRemoved: string[]
recordIdsMissing: string[]
success: boolean
}
}
export interface HubSpotGetListMembershipsParams {
accessToken: string
listId: string
limit?: string
after?: string
}
export interface HubSpotGetListMembershipsResponse extends ToolResponse {
output: {
memberships: HubSpotListMembership[]
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
}
// Search line items / quotes
export type HubSpotSearchLineItemsParams = HubSpotSearchContactsParams
export interface HubSpotSearchLineItemsResponse extends ToolResponse {
output: {
lineItems: HubSpotLineItem[]
total: number
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
}
export type HubSpotSearchQuotesParams = HubSpotSearchContactsParams
export interface HubSpotSearchQuotesResponse extends ToolResponse {
output: {
quotes: HubSpotQuote[]
total: number
paging: HubSpotPaging | null
metadata: { totalReturned: number; hasMore: boolean }
success: boolean
}
}
// Generic HubSpot response type for the block
export type HubSpotResponse =
| HubSpotGetUsersResponse
@@ -1583,3 +1744,15 @@ export type HubSpotResponse =
| HubSpotGetPropertiesResponse
| HubSpotListAssociationsResponse
| HubSpotCreateAssociationResponse
| HubSpotDeleteAssociationResponse
| HubSpotGetAssociationLabelsResponse
| HubSpotDeleteContactResponse
| HubSpotDeleteCompanyResponse
| HubSpotDeleteDealResponse
| HubSpotDeleteTicketResponse
| HubSpotDeleteLineItemResponse
| HubSpotAddListMembershipsResponse
| HubSpotRemoveListMembershipsResponse
| HubSpotGetListMembershipsResponse
| HubSpotSearchLineItemsResponse
| HubSpotSearchQuotesResponse
+1 -1
View File
@@ -55,7 +55,7 @@ export const hubspotUpdateCompanyTool: ToolConfig<
url: (params) => {
const baseUrl = `https://api.hubapi.com/crm/v3/objects/companies/${params.companyId.trim()}`
if (params.idProperty) {
return `${baseUrl}?idProperty=${params.idProperty}`
return `${baseUrl}?${new URLSearchParams({ idProperty: params.idProperty })}`
}
return baseUrl
},
+1 -1
View File
@@ -55,7 +55,7 @@ export const hubspotUpdateContactTool: ToolConfig<
url: (params) => {
const baseUrl = `https://api.hubapi.com/crm/v3/objects/contacts/${params.contactId.trim()}`
if (params.idProperty) {
return `${baseUrl}?idProperty=${params.idProperty}`
return `${baseUrl}?${new URLSearchParams({ idProperty: params.idProperty })}`
}
return baseUrl
},
+24
View File
@@ -1678,6 +1678,7 @@ import {
} from '@/tools/hex'
import { httpRequestTool, webhookRequestTool } from '@/tools/http'
import {
hubspotAddListMembershipsTool,
hubspotCreateAppointmentTool,
hubspotCreateAssociationTool,
hubspotCreateCompanyTool,
@@ -1688,13 +1689,21 @@ import {
hubspotCreateListTool,
hubspotCreateNoteTool,
hubspotCreateTicketTool,
hubspotDeleteAssociationTool,
hubspotDeleteCompanyTool,
hubspotDeleteContactTool,
hubspotDeleteDealTool,
hubspotDeleteLineItemTool,
hubspotDeleteTicketTool,
hubspotGetAppointmentTool,
hubspotGetAssociationLabelsTool,
hubspotGetCartTool,
hubspotGetCompanyTool,
hubspotGetContactTool,
hubspotGetDealTool,
hubspotGetEmailTool,
hubspotGetLineItemTool,
hubspotGetListMembershipsTool,
hubspotGetListTool,
hubspotGetMarketingEventTool,
hubspotGetNoteTool,
@@ -1716,11 +1725,14 @@ import {
hubspotListOwnersTool,
hubspotListQuotesTool,
hubspotListTicketsTool,
hubspotRemoveListMembershipsTool,
hubspotSearchCompaniesTool,
hubspotSearchContactsTool,
hubspotSearchDealsTool,
hubspotSearchEmailsTool,
hubspotSearchLineItemsTool,
hubspotSearchNotesTool,
hubspotSearchQuotesTool,
hubspotSearchTicketsTool,
hubspotUpdateAppointmentTool,
hubspotUpdateCompanyTool,
@@ -8087,6 +8099,7 @@ export const tools: Record<string, ToolConfig> = {
infisical_create_secret: infisicalCreateSecretTool,
infisical_update_secret: infisicalUpdateSecretTool,
infisical_delete_secret: infisicalDeleteSecretTool,
hubspot_add_list_memberships: hubspotAddListMembershipsTool,
hubspot_create_appointment: hubspotCreateAppointmentTool,
hubspot_create_association: hubspotCreateAssociationTool,
hubspot_create_company: hubspotCreateCompanyTool,
@@ -8097,7 +8110,14 @@ export const tools: Record<string, ToolConfig> = {
hubspot_create_list: hubspotCreateListTool,
hubspot_create_note: hubspotCreateNoteTool,
hubspot_create_ticket: hubspotCreateTicketTool,
hubspot_delete_association: hubspotDeleteAssociationTool,
hubspot_delete_company: hubspotDeleteCompanyTool,
hubspot_delete_contact: hubspotDeleteContactTool,
hubspot_delete_deal: hubspotDeleteDealTool,
hubspot_delete_line_item: hubspotDeleteLineItemTool,
hubspot_delete_ticket: hubspotDeleteTicketTool,
hubspot_get_appointment: hubspotGetAppointmentTool,
hubspot_get_association_labels: hubspotGetAssociationLabelsTool,
hubspot_get_cart: hubspotGetCartTool,
hubspot_get_company: hubspotGetCompanyTool,
hubspot_get_contact: hubspotGetContactTool,
@@ -8105,6 +8125,7 @@ export const tools: Record<string, ToolConfig> = {
hubspot_get_email: hubspotGetEmailTool,
hubspot_get_line_item: hubspotGetLineItemTool,
hubspot_get_list: hubspotGetListTool,
hubspot_get_list_memberships: hubspotGetListMembershipsTool,
hubspot_get_marketing_event: hubspotGetMarketingEventTool,
hubspot_get_note: hubspotGetNoteTool,
hubspot_get_properties: hubspotGetPropertiesTool,
@@ -8125,11 +8146,14 @@ export const tools: Record<string, ToolConfig> = {
hubspot_list_owners: hubspotListOwnersTool,
hubspot_list_quotes: hubspotListQuotesTool,
hubspot_list_tickets: hubspotListTicketsTool,
hubspot_remove_list_memberships: hubspotRemoveListMembershipsTool,
hubspot_search_companies: hubspotSearchCompaniesTool,
hubspot_search_contacts: hubspotSearchContactsTool,
hubspot_search_deals: hubspotSearchDealsTool,
hubspot_search_emails: hubspotSearchEmailsTool,
hubspot_search_line_items: hubspotSearchLineItemsTool,
hubspot_search_notes: hubspotSearchNotesTool,
hubspot_search_quotes: hubspotSearchQuotesTool,
hubspot_search_tickets: hubspotSearchTicketsTool,
hubspot_update_appointment: hubspotUpdateAppointmentTool,
hubspot_update_company: hubspotUpdateCompanyTool,