From 10028ecf677d294568e4812b8667f766d6bdc458 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 13 Jul 2026 10:51:54 -0700 Subject: [PATCH] improvement(hubspot): align tools with API docs and expand coverage with delete, list membership, and search tools --- apps/sim/blocks/blocks/hubspot.ts | 178 +++++++++++++- .../sim/tools/hubspot/add_list_memberships.ts | 112 +++++++++ apps/sim/tools/hubspot/create_appointment.ts | 2 +- apps/sim/tools/hubspot/create_deal.ts | 2 +- apps/sim/tools/hubspot/delete_association.ts | 97 ++++++++ apps/sim/tools/hubspot/delete_company.ts | 73 ++++++ apps/sim/tools/hubspot/delete_contact.ts | 73 ++++++ apps/sim/tools/hubspot/delete_deal.ts | 68 ++++++ apps/sim/tools/hubspot/delete_line_item.ts | 73 ++++++ apps/sim/tools/hubspot/delete_ticket.ts | 70 ++++++ .../tools/hubspot/get_association_labels.ts | 100 ++++++++ .../sim/tools/hubspot/get_list_memberships.ts | 113 +++++++++ apps/sim/tools/hubspot/index.ts | 12 + apps/sim/tools/hubspot/list_lists.ts | 2 +- .../tools/hubspot/list_marketing_events.ts | 2 +- .../tools/hubspot/remove_list_memberships.ts | 113 +++++++++ apps/sim/tools/hubspot/search_companies.ts | 2 +- apps/sim/tools/hubspot/search_contacts.ts | 2 +- apps/sim/tools/hubspot/search_deals.ts | 2 +- apps/sim/tools/hubspot/search_emails.ts | 2 +- apps/sim/tools/hubspot/search_line_items.ts | 155 +++++++++++++ apps/sim/tools/hubspot/search_notes.ts | 2 +- apps/sim/tools/hubspot/search_quotes.ts | 152 ++++++++++++ apps/sim/tools/hubspot/search_tickets.ts | 2 +- apps/sim/tools/hubspot/types.ts | 217 ++++++++++++++++-- apps/sim/tools/hubspot/update_company.ts | 2 +- apps/sim/tools/hubspot/update_contact.ts | 2 +- apps/sim/tools/registry.ts | 24 ++ 28 files changed, 1614 insertions(+), 40 deletions(-) create mode 100644 apps/sim/tools/hubspot/add_list_memberships.ts create mode 100644 apps/sim/tools/hubspot/delete_association.ts create mode 100644 apps/sim/tools/hubspot/delete_company.ts create mode 100644 apps/sim/tools/hubspot/delete_contact.ts create mode 100644 apps/sim/tools/hubspot/delete_deal.ts create mode 100644 apps/sim/tools/hubspot/delete_line_item.ts create mode 100644 apps/sim/tools/hubspot/delete_ticket.ts create mode 100644 apps/sim/tools/hubspot/get_association_labels.ts create mode 100644 apps/sim/tools/hubspot/get_list_memberships.ts create mode 100644 apps/sim/tools/hubspot/remove_list_memberships.ts create mode 100644 apps/sim/tools/hubspot/search_line_items.ts create mode 100644 apps/sim/tools/hubspot/search_quotes.ts diff --git a/apps/sim/blocks/blocks/hubspot.ts b/apps/sim/blocks/blocks/hubspot.ts index 367ee0a4004..06a20defa0f 100644 --- a/apps/sim/blocks/blocks/hubspot.ts +++ b/apps/sim/blocks/blocks/hubspot.ts @@ -28,18 +28,22 @@ export const HubSpotBlock: BlockConfig = { { 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 = { { 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 = { { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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 = { 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.', diff --git a/apps/sim/tools/hubspot/add_list_memberships.ts b/apps/sim/tools/hubspot/add_list_memberships.ts new file mode 100644 index 00000000000..779571b8ba1 --- /dev/null +++ b/apps/sim/tools/hubspot/add_list_memberships.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/create_appointment.ts b/apps/sim/tools/hubspot/create_appointment.ts index 89c96c02d45..c1633f07e43 100644 --- a/apps/sim/tools/hubspot/create_appointment.ts +++ b/apps/sim/tools/hubspot/create_appointment.ts @@ -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', diff --git a/apps/sim/tools/hubspot/create_deal.ts b/apps/sim/tools/hubspot/create_deal.ts index 6b5a60ef336..a2b8dd9ec38 100644 --- a/apps/sim/tools/hubspot/create_deal.ts +++ b/apps/sim/tools/hubspot/create_deal.ts @@ -30,7 +30,7 @@ export const hubspotCreateDealTool: ToolConfig = { + 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' }, + }, +} diff --git a/apps/sim/tools/hubspot/delete_company.ts b/apps/sim/tools/hubspot/delete_company.ts new file mode 100644 index 00000000000..26b7d78bcf7 --- /dev/null +++ b/apps/sim/tools/hubspot/delete_company.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/delete_contact.ts b/apps/sim/tools/hubspot/delete_contact.ts new file mode 100644 index 00000000000..b6476f1c4e5 --- /dev/null +++ b/apps/sim/tools/hubspot/delete_contact.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/delete_deal.ts b/apps/sim/tools/hubspot/delete_deal.ts new file mode 100644 index 00000000000..98850da0b85 --- /dev/null +++ b/apps/sim/tools/hubspot/delete_deal.ts @@ -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 = + { + 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' }, + }, + } diff --git a/apps/sim/tools/hubspot/delete_line_item.ts b/apps/sim/tools/hubspot/delete_line_item.ts new file mode 100644 index 00000000000..e1b71a5b019 --- /dev/null +++ b/apps/sim/tools/hubspot/delete_line_item.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/delete_ticket.ts b/apps/sim/tools/hubspot/delete_ticket.ts new file mode 100644 index 00000000000..6308a6107ed --- /dev/null +++ b/apps/sim/tools/hubspot/delete_ticket.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/get_association_labels.ts b/apps/sim/tools/hubspot/get_association_labels.ts new file mode 100644 index 00000000000..4e9beb4de83 --- /dev/null +++ b/apps/sim/tools/hubspot/get_association_labels.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/get_list_memberships.ts b/apps/sim/tools/hubspot/get_list_memberships.ts new file mode 100644 index 00000000000..c3a8992170e --- /dev/null +++ b/apps/sim/tools/hubspot/get_list_memberships.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/index.ts b/apps/sim/tools/hubspot/index.ts index b85ec565b3d..5f814a228e4 100644 --- a/apps/sim/tools/hubspot/index.ts +++ b/apps/sim/tools/hubspot/index.ts @@ -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' diff --git a/apps/sim/tools/hubspot/list_lists.ts b/apps/sim/tools/hubspot/list_lists.ts index eb0c08fa333..3643f0e5e0d 100644 --- a/apps/sim/tools/hubspot/list_lists.ts +++ b/apps/sim/tools/hubspot/list_lists.ts @@ -84,7 +84,7 @@ export const hubspotListListsTool: ToolConfig { - 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) diff --git a/apps/sim/tools/hubspot/remove_list_memberships.ts b/apps/sim/tools/hubspot/remove_list_memberships.ts new file mode 100644 index 00000000000..a5706f00687 --- /dev/null +++ b/apps/sim/tools/hubspot/remove_list_memberships.ts @@ -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' }, + }, +} diff --git a/apps/sim/tools/hubspot/search_companies.ts b/apps/sim/tools/hubspot/search_companies.ts index e05ddb8c169..c62f72ac39d 100644 --- a/apps/sim/tools/hubspot/search_companies.ts +++ b/apps/sim/tools/hubspot/search_companies.ts @@ -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', diff --git a/apps/sim/tools/hubspot/search_contacts.ts b/apps/sim/tools/hubspot/search_contacts.ts index 44fd75f2687..d48e115d593 100644 --- a/apps/sim/tools/hubspot/search_contacts.ts +++ b/apps/sim/tools/hubspot/search_contacts.ts @@ -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', diff --git a/apps/sim/tools/hubspot/search_deals.ts b/apps/sim/tools/hubspot/search_deals.ts index 873aa52d4cb..63dd627f7b1 100644 --- a/apps/sim/tools/hubspot/search_deals.ts +++ b/apps/sim/tools/hubspot/search_deals.ts @@ -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', diff --git a/apps/sim/tools/hubspot/search_emails.ts b/apps/sim/tools/hubspot/search_emails.ts index 600f47c6dd8..45dfd4fca22 100644 --- a/apps/sim/tools/hubspot/search_emails.ts +++ b/apps/sim/tools/hubspot/search_emails.ts @@ -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', diff --git a/apps/sim/tools/hubspot/search_line_items.ts b/apps/sim/tools/hubspot/search_line_items.ts new file mode 100644 index 00000000000..15a41afb177 --- /dev/null +++ b/apps/sim/tools/hubspot/search_line_items.ts @@ -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 = {} + 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' }, + }, +} diff --git a/apps/sim/tools/hubspot/search_notes.ts b/apps/sim/tools/hubspot/search_notes.ts index bed86d666e0..20703a59dd5 100644 --- a/apps/sim/tools/hubspot/search_notes.ts +++ b/apps/sim/tools/hubspot/search_notes.ts @@ -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', diff --git a/apps/sim/tools/hubspot/search_quotes.ts b/apps/sim/tools/hubspot/search_quotes.ts new file mode 100644 index 00000000000..e0e2f49db19 --- /dev/null +++ b/apps/sim/tools/hubspot/search_quotes.ts @@ -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 = {} + 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' }, + }, +} diff --git a/apps/sim/tools/hubspot/search_tickets.ts b/apps/sim/tools/hubspot/search_tickets.ts index f7d7530f7d4..4c028ef365d 100644 --- a/apps/sim/tools/hubspot/search_tickets.ts +++ b/apps/sim/tools/hubspot/search_tickets.ts @@ -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', diff --git a/apps/sim/tools/hubspot/types.ts b/apps/sim/tools/hubspot/types.ts index b5ff425102d..8a8cc502dda 100644 --- a/apps/sim/tools/hubspot/types.ts +++ b/apps/sim/tools/hubspot/types.ts @@ -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 & { output: { companies: HubSpotContact[] - paging?: HubSpotPaging + paging: HubSpotPaging | null metadata: { totalReturned: number hasMore: boolean @@ -1048,7 +1048,9 @@ export type HubSpotListCompaniesResponse = Omit & { + companyId: string +} export type HubSpotGetCompanyResponse = Omit & { output: { company: HubSpotContact @@ -1064,7 +1066,9 @@ export type HubSpotCreateCompanyResponse = Omit & { + companyId: string +} export type HubSpotUpdateCompanyResponse = Omit & { 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 & { 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 diff --git a/apps/sim/tools/hubspot/update_company.ts b/apps/sim/tools/hubspot/update_company.ts index 949b1ad3b65..7d6759bb41a 100644 --- a/apps/sim/tools/hubspot/update_company.ts +++ b/apps/sim/tools/hubspot/update_company.ts @@ -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 }, diff --git a/apps/sim/tools/hubspot/update_contact.ts b/apps/sim/tools/hubspot/update_contact.ts index 3324d97a955..2b8aae7e1c6 100644 --- a/apps/sim/tools/hubspot/update_contact.ts +++ b/apps/sim/tools/hubspot/update_contact.ts @@ -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 }, diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index e6f33250a0d..d59986bf4e3 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1674,6 +1674,7 @@ import { } from '@/tools/hex' import { httpRequestTool, webhookRequestTool } from '@/tools/http' import { + hubspotAddListMembershipsTool, hubspotCreateAppointmentTool, hubspotCreateAssociationTool, hubspotCreateCompanyTool, @@ -1684,13 +1685,21 @@ import { hubspotCreateListTool, hubspotCreateNoteTool, hubspotCreateTicketTool, + hubspotDeleteAssociationTool, + hubspotDeleteCompanyTool, + hubspotDeleteContactTool, + hubspotDeleteDealTool, + hubspotDeleteLineItemTool, + hubspotDeleteTicketTool, hubspotGetAppointmentTool, + hubspotGetAssociationLabelsTool, hubspotGetCartTool, hubspotGetCompanyTool, hubspotGetContactTool, hubspotGetDealTool, hubspotGetEmailTool, hubspotGetLineItemTool, + hubspotGetListMembershipsTool, hubspotGetListTool, hubspotGetMarketingEventTool, hubspotGetNoteTool, @@ -1712,11 +1721,14 @@ import { hubspotListOwnersTool, hubspotListQuotesTool, hubspotListTicketsTool, + hubspotRemoveListMembershipsTool, hubspotSearchCompaniesTool, hubspotSearchContactsTool, hubspotSearchDealsTool, hubspotSearchEmailsTool, + hubspotSearchLineItemsTool, hubspotSearchNotesTool, + hubspotSearchQuotesTool, hubspotSearchTicketsTool, hubspotUpdateAppointmentTool, hubspotUpdateCompanyTool, @@ -8079,6 +8091,7 @@ export const tools: Record = { 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, @@ -8089,7 +8102,14 @@ export const tools: Record = { 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, @@ -8097,6 +8117,7 @@ export const tools: Record = { 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, @@ -8117,11 +8138,14 @@ export const tools: Record = { 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,