Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 172 additions & 6 deletions apps/sim/blocks/blocks/hubspot.ts

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions apps/sim/tools/hubspot/add_list_memberships.ts
Original file line number Diff line number Diff line change
@@ -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' },
},
}
2 changes: 1 addition & 1 deletion apps/sim/tools/hubspot/create_appointment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/hubspot/create_deal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
97 changes: 97 additions & 0 deletions apps/sim/tools/hubspot/delete_association.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 73 additions & 0 deletions apps/sim/tools/hubspot/delete_company.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 73 additions & 0 deletions apps/sim/tools/hubspot/delete_contact.ts
Original file line number Diff line number Diff line change
@@ -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' },
},
}
Loading
Loading