diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 6a2a66183fb..cbc3b5edd85 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -2132,7 +2132,15 @@ export function Mem0Icon(props: SVGProps) { export function ExtendIcon(props: SVGProps) { return ( - + + + ) { ) } +export function SixtyfourIcon(props: SVGProps) { + return ( + + + + + + + + ) +} + export function SimilarwebIcon(props: SVGProps) { return ( = { sharepoint: MicrosoftSharepointIcon, shopify: ShopifyIcon, similarweb: SimilarwebIcon, + sixtyfour: SixtyfourIcon, slack: SlackIcon, smtp: SmtpIcon, sqs: SQSIcon, diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index cc194da1f25..a0f99bf6616 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -150,6 +150,7 @@ "sharepoint", "shopify", "similarweb", + "sixtyfour", "slack", "smtp", "sqs", diff --git a/apps/docs/content/docs/en/tools/sixtyfour.mdx b/apps/docs/content/docs/en/tools/sixtyfour.mdx new file mode 100644 index 00000000000..41a55c58463 --- /dev/null +++ b/apps/docs/content/docs/en/tools/sixtyfour.mdx @@ -0,0 +1,128 @@ +--- +title: Sixtyfour AI +description: Enrich leads and companies with AI-powered research +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Find emails, phone numbers, and enrich lead or company data with contact information, social profiles, and detailed research using Sixtyfour AI. + + + +## Tools + +### `sixtyfour_find_phone` + +Find phone numbers for a lead using Sixtyfour AI. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Sixtyfour API key | +| `name` | string | Yes | Full name of the person | +| `company` | string | No | Company name | +| `linkedinUrl` | string | No | LinkedIn profile URL | +| `domain` | string | No | Company website domain | +| `email` | string | No | Email address | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `name` | string | Name of the person | +| `company` | string | Company name | +| `phone` | string | Phone number\(s\) found | +| `linkedinUrl` | string | LinkedIn profile URL | + +### `sixtyfour_find_email` + +Find email addresses for a lead using Sixtyfour AI. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Sixtyfour API key | +| `name` | string | Yes | Full name of the person | +| `company` | string | No | Company name | +| `linkedinUrl` | string | No | LinkedIn profile URL | +| `domain` | string | No | Company website domain | +| `phone` | string | No | Phone number | +| `title` | string | No | Job title | +| `mode` | string | No | Email discovery mode: PROFESSIONAL \(default\) or PERSONAL | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `name` | string | Name of the person | +| `company` | string | Company name | +| `title` | string | Job title | +| `phone` | string | Phone number | +| `linkedinUrl` | string | LinkedIn profile URL | +| `emails` | json | Professional email addresses found | +| ↳ `address` | string | Email address | +| ↳ `status` | string | Validation status \(OK or UNKNOWN\) | +| ↳ `type` | string | Email type \(COMPANY or PERSONAL\) | +| `personalEmails` | json | Personal email addresses found \(only in PERSONAL mode\) | +| ↳ `address` | string | Email address | +| ↳ `status` | string | Validation status \(OK or UNKNOWN\) | +| ↳ `type` | string | Email type \(COMPANY or PERSONAL\) | + +### `sixtyfour_enrich_lead` + +Enrich lead information with contact details, social profiles, and company data using Sixtyfour AI. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Sixtyfour API key | +| `leadInfo` | string | Yes | Lead information as JSON object with key-value pairs \(e.g. name, company, title, linkedin\) | +| `struct` | string | Yes | Fields to collect as JSON object. Keys are field names, values are descriptions \(e.g. \{"email": "The individual\'s email address", "phone": "Phone number"\}\) | +| `researchPlan` | string | No | Optional research plan to guide enrichment strategy | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `notes` | string | Research notes about the lead | +| `structuredData` | json | Enriched lead data matching the requested struct fields | +| `references` | json | Source URLs and descriptions used for enrichment | +| `confidenceScore` | number | Quality score for the returned data \(0-10\) | + +### `sixtyfour_enrich_company` + +Enrich company data with additional information and find associated people using Sixtyfour AI. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Sixtyfour API key | +| `targetCompany` | string | Yes | Company data as JSON object \(e.g. \{"name": "Acme Inc", "domain": "acme.com"\}\) | +| `struct` | string | Yes | Fields to collect as JSON object. Keys are field names, values are descriptions \(e.g. \{"website": "Company website URL", "num_employees": "Employee count"\}\) | +| `findPeople` | boolean | No | Whether to find people associated with the company | +| `fullOrgChart` | boolean | No | Whether to retrieve the full organizational chart | +| `researchPlan` | string | No | Optional strategy describing how the agent should search for information | +| `peopleFocusPrompt` | string | No | Description of people to find \(roles, responsibilities\) | +| `leadStruct` | string | No | Custom schema for returned lead data as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `notes` | string | Research notes about the company | +| `structuredData` | json | Enriched company data matching the requested struct fields | +| `references` | json | Source URLs and descriptions used for enrichment | +| `confidenceScore` | number | Quality score for the returned data \(0-10\) | + + diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index ee5f8c95a5b..603fecd3633 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -154,6 +154,7 @@ import { SftpIcon, ShopifyIcon, SimilarwebIcon, + SixtyfourIcon, SlackIcon, SmtpIcon, SQSIcon, @@ -340,6 +341,7 @@ export const blockTypeToIconMap: Record = { sharepoint: MicrosoftSharepointIcon, shopify: ShopifyIcon, similarweb: SimilarwebIcon, + sixtyfour: SixtyfourIcon, slack: SlackIcon, smtp: SmtpIcon, sqs: SQSIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 9db82b6d349..55ab9caad0d 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -324,7 +324,7 @@ "longDescription": "Search across your synced data sources using Airweave. Supports semantic search with hybrid, neural, or keyword retrieval strategies. Optionally generate AI-powered answers from search results.", "bgColor": "#6366F1", "iconName": "AirweaveIcon", - "docsUrl": "https://docs.airweave.ai", + "docsUrl": "https://docs.sim.ai/tools/airweave", "operations": [], "operationCount": 0, "triggers": [], @@ -10639,6 +10639,41 @@ "integrationType": "analytics", "tags": ["marketing", "data-analytics", "seo"] }, + { + "type": "sixtyfour", + "slug": "sixtyfour-ai", + "name": "Sixtyfour AI", + "description": "Enrich leads and companies with AI-powered research", + "longDescription": "Find emails, phone numbers, and enrich lead or company data with contact information, social profiles, and detailed research using Sixtyfour AI.", + "bgColor": "#000000", + "iconName": "SixtyfourIcon", + "docsUrl": "https://docs.sim.ai/tools/sixtyfour", + "operations": [ + { + "name": "Find Phone", + "description": "Find phone numbers for a lead using Sixtyfour AI." + }, + { + "name": "Find Email", + "description": "Find email addresses for a lead using Sixtyfour AI." + }, + { + "name": "Enrich Lead", + "description": "Enrich lead information with contact details, social profiles, and company data using Sixtyfour AI." + }, + { + "name": "Enrich Company", + "description": "Enrich company data with additional information and find associated people using Sixtyfour AI." + } + ], + "operationCount": 4, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationType": "sales-intelligence", + "tags": ["enrichment", "sales-engagement"] + }, { "type": "slack", "slug": "slack", diff --git a/apps/sim/blocks/blocks/airweave.ts b/apps/sim/blocks/blocks/airweave.ts index caa9c4097f6..6948351d9a6 100644 --- a/apps/sim/blocks/blocks/airweave.ts +++ b/apps/sim/blocks/blocks/airweave.ts @@ -10,7 +10,7 @@ export const AirweaveBlock: BlockConfig = { authMode: AuthMode.ApiKey, longDescription: 'Search across your synced data sources using Airweave. Supports semantic search with hybrid, neural, or keyword retrieval strategies. Optionally generate AI-powered answers from search results.', - docsLink: 'https://docs.airweave.ai', + docsLink: 'https://docs.sim.ai/tools/airweave', category: 'tools', integrationType: IntegrationType.Search, tags: ['vector-search', 'knowledge-base'], diff --git a/apps/sim/blocks/blocks/sixtyfour.ts b/apps/sim/blocks/blocks/sixtyfour.ts new file mode 100644 index 00000000000..30389839a86 --- /dev/null +++ b/apps/sim/blocks/blocks/sixtyfour.ts @@ -0,0 +1,292 @@ +import { SixtyfourIcon } from '@/components/icons' +import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types' + +export const SixtyfourBlock: BlockConfig = { + type: 'sixtyfour', + name: 'Sixtyfour AI', + description: 'Enrich leads and companies with AI-powered research', + longDescription: + 'Find emails, phone numbers, and enrich lead or company data with contact information, social profiles, and detailed research using Sixtyfour AI.', + docsLink: 'https://docs.sim.ai/tools/sixtyfour', + category: 'tools', + integrationType: IntegrationType.SalesIntelligence, + tags: ['enrichment', 'sales-engagement'], + bgColor: '#000000', + icon: SixtyfourIcon, + authMode: AuthMode.ApiKey, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Find Phone', id: 'find_phone' }, + { label: 'Find Email', id: 'find_email' }, + { label: 'Enrich Lead', id: 'enrich_lead' }, + { label: 'Enrich Company', id: 'enrich_company' }, + ], + value: () => 'find_phone', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + required: true, + placeholder: 'Enter your Sixtyfour API key', + password: true, + }, + { + id: 'name', + title: 'Name', + type: 'short-input', + placeholder: 'Full name of the person', + required: { field: 'operation', value: ['find_phone', 'find_email'] }, + condition: { field: 'operation', value: ['find_phone', 'find_email'] }, + }, + { + id: 'company', + title: 'Company', + type: 'short-input', + placeholder: 'Company name', + condition: { field: 'operation', value: ['find_phone', 'find_email'] }, + }, + { + id: 'linkedinUrl', + title: 'LinkedIn URL', + type: 'short-input', + placeholder: 'https://linkedin.com/in/johndoe', + condition: { field: 'operation', value: ['find_phone', 'find_email'] }, + mode: 'advanced', + }, + { + id: 'domain', + title: 'Domain', + type: 'short-input', + placeholder: 'example.com', + condition: { field: 'operation', value: ['find_phone', 'find_email'] }, + mode: 'advanced', + }, + { + id: 'emailInput', + title: 'Email', + type: 'short-input', + placeholder: 'Email address', + condition: { field: 'operation', value: 'find_phone' }, + mode: 'advanced', + }, + { + id: 'phoneInput', + title: 'Phone', + type: 'short-input', + placeholder: 'Phone number', + condition: { field: 'operation', value: 'find_email' }, + mode: 'advanced', + }, + { + id: 'title', + title: 'Job Title', + type: 'short-input', + placeholder: 'Job title', + condition: { field: 'operation', value: 'find_email' }, + mode: 'advanced', + }, + { + id: 'mode', + title: 'Mode', + type: 'dropdown', + options: [ + { label: 'Professional', id: 'PROFESSIONAL' }, + { label: 'Personal', id: 'PERSONAL' }, + ], + value: () => 'PROFESSIONAL', + condition: { field: 'operation', value: 'find_email' }, + }, + { + id: 'leadInfo', + title: 'Lead Info', + type: 'long-input', + placeholder: + '{"name": "John Doe", "company": "Acme Inc", "title": "CEO", "linkedin": "https://linkedin.com/in/johndoe"}', + required: { field: 'operation', value: 'enrich_lead' }, + condition: { field: 'operation', value: 'enrich_lead' }, + }, + { + id: 'leadStruct', + title: 'Fields to Collect', + type: 'long-input', + placeholder: + '{"email": "Email address", "phone": "Phone number", "company": "Company name", "title": "Job title"}', + required: { field: 'operation', value: 'enrich_lead' }, + condition: { field: 'operation', value: 'enrich_lead' }, + }, + { + id: 'leadResearchPlan', + title: 'Research Plan', + type: 'long-input', + placeholder: 'Optional guidance for the enrichment agent', + condition: { field: 'operation', value: 'enrich_lead' }, + mode: 'advanced', + }, + { + id: 'targetCompany', + title: 'Company Info', + type: 'long-input', + placeholder: '{"name": "Acme Inc", "domain": "acme.com", "industry": "Technology"}', + required: { field: 'operation', value: 'enrich_company' }, + condition: { field: 'operation', value: 'enrich_company' }, + }, + { + id: 'companyStruct', + title: 'Fields to Collect', + type: 'long-input', + placeholder: + '{"website": "Company website URL", "num_employees": "Employee count", "address": "Company address"}', + required: { field: 'operation', value: 'enrich_company' }, + condition: { field: 'operation', value: 'enrich_company' }, + }, + { + id: 'findPeople', + title: 'Find People', + type: 'switch', + condition: { field: 'operation', value: 'enrich_company' }, + }, + { + id: 'peopleFocusPrompt', + title: 'People Focus', + type: 'short-input', + placeholder: 'e.g. Find the VP of Marketing and the CTO', + condition: { field: 'operation', value: 'enrich_company' }, + mode: 'advanced', + }, + { + id: 'fullOrgChart', + title: 'Full Org Chart', + type: 'switch', + condition: { field: 'operation', value: 'enrich_company' }, + mode: 'advanced', + }, + { + id: 'companyLeadStruct', + title: 'Lead Schema', + type: 'long-input', + placeholder: '{"name": "Full name", "email": "Email", "title": "Job title"}', + condition: { field: 'operation', value: 'enrich_company' }, + mode: 'advanced', + }, + { + id: 'companyResearchPlan', + title: 'Research Plan', + type: 'long-input', + placeholder: 'Optional guidance for the enrichment agent', + condition: { field: 'operation', value: 'enrich_company' }, + mode: 'advanced', + }, + ], + + tools: { + access: [ + 'sixtyfour_find_phone', + 'sixtyfour_find_email', + 'sixtyfour_enrich_lead', + 'sixtyfour_enrich_company', + ], + config: { + tool: (params) => `sixtyfour_${params.operation}`, + params: (params) => { + const result: Record = {} + + if (params.operation === 'find_phone') { + if (params.emailInput) result.email = params.emailInput + } else if (params.operation === 'find_email') { + if (params.phoneInput) result.phone = params.phoneInput + } else if (params.operation === 'enrich_lead') { + result.leadInfo = params.leadInfo + result.struct = params.leadStruct + if (params.leadResearchPlan) result.researchPlan = params.leadResearchPlan + } else if (params.operation === 'enrich_company') { + result.targetCompany = params.targetCompany + result.struct = params.companyStruct + if (params.findPeople !== undefined) result.findPeople = Boolean(params.findPeople) + if (params.fullOrgChart !== undefined) result.fullOrgChart = Boolean(params.fullOrgChart) + if (params.peopleFocusPrompt) result.peopleFocusPrompt = params.peopleFocusPrompt + if (params.companyLeadStruct) result.leadStruct = params.companyLeadStruct + if (params.companyResearchPlan) result.researchPlan = params.companyResearchPlan + } + + return result + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Sixtyfour API key' }, + name: { type: 'string', description: 'Person name' }, + company: { type: 'string', description: 'Company name' }, + linkedinUrl: { type: 'string', description: 'LinkedIn URL' }, + domain: { type: 'string', description: 'Company domain' }, + emailInput: { type: 'string', description: 'Email address (find phone)' }, + phoneInput: { type: 'string', description: 'Phone number (find email)' }, + title: { type: 'string', description: 'Job title' }, + mode: { type: 'string', description: 'Email mode (PROFESSIONAL or PERSONAL)' }, + leadInfo: { type: 'string', description: 'Lead information JSON' }, + leadStruct: { type: 'string', description: 'Fields to collect for lead' }, + leadResearchPlan: { type: 'string', description: 'Research plan for lead enrichment' }, + targetCompany: { type: 'string', description: 'Company information JSON' }, + companyStruct: { type: 'string', description: 'Fields to collect for company' }, + findPeople: { type: 'boolean', description: 'Find associated people' }, + fullOrgChart: { type: 'boolean', description: 'Retrieve full org chart' }, + peopleFocusPrompt: { type: 'string', description: 'People focus description' }, + companyLeadStruct: { type: 'string', description: 'Lead schema for company enrichment' }, + companyResearchPlan: { type: 'string', description: 'Research plan for company enrichment' }, + }, + + outputs: { + name: { + type: 'string', + description: 'Name of the person (find_phone, find_email)', + }, + company: { + type: 'string', + description: 'Company name (find_phone, find_email)', + }, + phone: { + type: 'string', + description: 'Phone number(s) found (find_phone)', + }, + linkedinUrl: { + type: 'string', + description: 'LinkedIn profile URL (find_phone, find_email)', + }, + title: { + type: 'string', + description: 'Job title (find_email)', + }, + emails: { + type: 'json', + description: 'Email addresses found with validation status and type (find_email)', + }, + personalEmails: { + type: 'json', + description: 'Personal email addresses found in PERSONAL mode (find_email)', + }, + notes: { + type: 'string', + description: 'Research notes (enrich_lead, enrich_company)', + }, + structuredData: { + type: 'json', + description: + 'Enriched data matching the requested struct fields (enrich_lead, enrich_company)', + }, + references: { + type: 'json', + description: 'Source URLs and descriptions used for enrichment (enrich_lead, enrich_company)', + }, + confidenceScore: { + type: 'number', + description: 'Quality score for the returned data, 0-10 (enrich_lead, enrich_company)', + }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 8b21cebea1a..59a385df18c 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -171,6 +171,7 @@ import { SftpBlock } from '@/blocks/blocks/sftp' import { SharepointBlock } from '@/blocks/blocks/sharepoint' import { ShopifyBlock } from '@/blocks/blocks/shopify' import { SimilarwebBlock } from '@/blocks/blocks/similarweb' +import { SixtyfourBlock } from '@/blocks/blocks/sixtyfour' import { SlackBlock } from '@/blocks/blocks/slack' import { SmtpBlock } from '@/blocks/blocks/smtp' import { SpotifyBlock } from '@/blocks/blocks/spotify' @@ -407,6 +408,7 @@ export const registry: Record = { sharepoint: SharepointBlock, shopify: ShopifyBlock, similarweb: SimilarwebBlock, + sixtyfour: SixtyfourBlock, slack: SlackBlock, smtp: SmtpBlock, spotify: SpotifyBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 6a2a66183fb..cbc3b5edd85 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -2132,7 +2132,15 @@ export function Mem0Icon(props: SVGProps) { export function ExtendIcon(props: SVGProps) { return ( - + + + ) { ) } +export function SixtyfourIcon(props: SVGProps) { + return ( + + + + + + + + ) +} + export function SimilarwebIcon(props: SVGProps) { return ( = { servicenow_read_record: servicenowReadRecordTool, servicenow_update_record: servicenowUpdateRecordTool, servicenow_delete_record: servicenowDeleteRecordTool, + sixtyfour_find_phone: sixtyfourFindPhoneTool, + sixtyfour_find_email: sixtyfourFindEmailTool, + sixtyfour_enrich_lead: sixtyfourEnrichLeadTool, + sixtyfour_enrich_company: sixtyfourEnrichCompanyTool, tavily_search: tavilySearchTool, tavily_extract: tavilyExtractTool, tavily_crawl: tavilyCrawlTool, diff --git a/apps/sim/tools/sixtyfour/enrich_company.ts b/apps/sim/tools/sixtyfour/enrich_company.ts new file mode 100644 index 00000000000..b296dbca1fb --- /dev/null +++ b/apps/sim/tools/sixtyfour/enrich_company.ts @@ -0,0 +1,143 @@ +import type { + SixtyfourEnrichCompanyParams, + SixtyfourEnrichCompanyResponse, +} from '@/tools/sixtyfour/types' +import type { ToolConfig } from '@/tools/types' + +export const sixtyfourEnrichCompanyTool: ToolConfig< + SixtyfourEnrichCompanyParams, + SixtyfourEnrichCompanyResponse +> = { + id: 'sixtyfour_enrich_company', + name: 'Sixtyfour Enrich Company', + description: + 'Enrich company data with additional information and find associated people using Sixtyfour AI.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Sixtyfour API key', + }, + targetCompany: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Company data as JSON object (e.g. {"name": "Acme Inc", "domain": "acme.com"})', + }, + struct: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Fields to collect as JSON object. Keys are field names, values are descriptions (e.g. {"website": "Company website URL", "num_employees": "Employee count"})', + }, + findPeople: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to find people associated with the company', + }, + fullOrgChart: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to retrieve the full organizational chart', + }, + researchPlan: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional strategy describing how the agent should search for information', + }, + peopleFocusPrompt: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Description of people to find (roles, responsibilities)', + }, + leadStruct: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Custom schema for returned lead data as JSON object', + }, + }, + + request: { + url: 'https://api.sixtyfour.ai/enrich-company', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'x-api-key': params.apiKey, + }), + body: (params) => { + let targetCompany: unknown + try { + targetCompany = + typeof params.targetCompany === 'string' + ? JSON.parse(params.targetCompany) + : params.targetCompany + } catch { + throw new Error('targetCompany must be valid JSON') + } + let struct: unknown + try { + struct = typeof params.struct === 'string' ? JSON.parse(params.struct) : params.struct + } catch { + throw new Error('struct must be valid JSON') + } + let leadStruct: Record | undefined + try { + leadStruct = + params.leadStruct && typeof params.leadStruct === 'string' + ? (JSON.parse(params.leadStruct) as Record) + : (params.leadStruct as Record | undefined) + } catch { + throw new Error('leadStruct must be valid JSON') + } + return { + target_company: targetCompany, + struct, + ...(params.findPeople !== undefined && { find_people: params.findPeople }), + ...(params.fullOrgChart !== undefined && { full_org_chart: params.fullOrgChart }), + ...(params.researchPlan && { research_plan: params.researchPlan }), + ...(params.peopleFocusPrompt && { people_focus_prompt: params.peopleFocusPrompt }), + ...(leadStruct && { lead_struct: leadStruct }), + } + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || data.message || data.detail || `API error: ${response.status}`) + } + + return { + success: true, + output: { + notes: data.notes ?? null, + structuredData: data.structured_data ?? {}, + references: data.references ?? {}, + confidenceScore: data.confidence_score ?? null, + }, + } + }, + + outputs: { + notes: { type: 'string', description: 'Research notes about the company', optional: true }, + structuredData: { + type: 'json', + description: 'Enriched company data matching the requested struct fields', + }, + references: { type: 'json', description: 'Source URLs and descriptions used for enrichment' }, + confidenceScore: { + type: 'number', + description: 'Quality score for the returned data (0-10)', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/sixtyfour/enrich_lead.ts b/apps/sim/tools/sixtyfour/enrich_lead.ts new file mode 100644 index 00000000000..424a69a383b --- /dev/null +++ b/apps/sim/tools/sixtyfour/enrich_lead.ts @@ -0,0 +1,105 @@ +import type { + SixtyfourEnrichLeadParams, + SixtyfourEnrichLeadResponse, +} from '@/tools/sixtyfour/types' +import type { ToolConfig } from '@/tools/types' + +export const sixtyfourEnrichLeadTool: ToolConfig< + SixtyfourEnrichLeadParams, + SixtyfourEnrichLeadResponse +> = { + id: 'sixtyfour_enrich_lead', + name: 'Sixtyfour Enrich Lead', + description: + 'Enrich lead information with contact details, social profiles, and company data using Sixtyfour AI.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Sixtyfour API key', + }, + leadInfo: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Lead information as JSON object with key-value pairs (e.g. name, company, title, linkedin)', + }, + struct: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'Fields to collect as JSON object. Keys are field names, values are descriptions (e.g. {"email": "The individual\'s email address", "phone": "Phone number"})', + }, + researchPlan: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional research plan to guide enrichment strategy', + }, + }, + + request: { + url: 'https://api.sixtyfour.ai/enrich-lead', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'x-api-key': params.apiKey, + }), + body: (params) => { + let leadInfo: unknown + try { + leadInfo = + typeof params.leadInfo === 'string' ? JSON.parse(params.leadInfo) : params.leadInfo + } catch { + throw new Error('leadInfo must be valid JSON') + } + let struct: unknown + try { + struct = typeof params.struct === 'string' ? JSON.parse(params.struct) : params.struct + } catch { + throw new Error('struct must be valid JSON') + } + return { + lead_info: leadInfo, + struct, + ...(params.researchPlan && { research_plan: params.researchPlan }), + } + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || data.message || data.detail || `API error: ${response.status}`) + } + + return { + success: true, + output: { + notes: data.notes ?? null, + structuredData: data.structured_data ?? {}, + references: data.references ?? {}, + confidenceScore: data.confidence_score ?? null, + }, + } + }, + + outputs: { + notes: { type: 'string', description: 'Research notes about the lead', optional: true }, + structuredData: { + type: 'json', + description: 'Enriched lead data matching the requested struct fields', + }, + references: { type: 'json', description: 'Source URLs and descriptions used for enrichment' }, + confidenceScore: { + type: 'number', + description: 'Quality score for the returned data (0-10)', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/sixtyfour/find_email.ts b/apps/sim/tools/sixtyfour/find_email.ts new file mode 100644 index 00000000000..9f993ae40c7 --- /dev/null +++ b/apps/sim/tools/sixtyfour/find_email.ts @@ -0,0 +1,144 @@ +import type { SixtyfourFindEmailParams, SixtyfourFindEmailResponse } from '@/tools/sixtyfour/types' +import type { ToolConfig } from '@/tools/types' + +function parseEmails(emailField: unknown): { address: string; status: string; type: string }[] { + if (!Array.isArray(emailField)) return [] + return emailField.map((entry: unknown) => { + if (Array.isArray(entry)) { + return { + address: entry[0] ?? '', + status: entry[1] ?? 'UNKNOWN', + type: entry[2] ?? 'UNKNOWN', + } + } + return { address: String(entry), status: 'UNKNOWN', type: 'UNKNOWN' } + }) +} + +export const sixtyfourFindEmailTool: ToolConfig< + SixtyfourFindEmailParams, + SixtyfourFindEmailResponse +> = { + id: 'sixtyfour_find_email', + name: 'Sixtyfour Find Email', + description: 'Find email addresses for a lead using Sixtyfour AI.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Sixtyfour API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Full name of the person', + }, + company: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name', + }, + linkedinUrl: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'LinkedIn profile URL', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company website domain', + }, + phone: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Phone number', + }, + title: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Job title', + }, + mode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Email discovery mode: PROFESSIONAL (default) or PERSONAL', + }, + }, + + request: { + url: 'https://api.sixtyfour.ai/find-email', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'x-api-key': params.apiKey, + }), + body: (params) => ({ + lead: { + name: params.name, + ...(params.company && { company: params.company }), + ...(params.linkedinUrl && { linkedin: params.linkedinUrl }), + ...(params.domain && { domain: params.domain }), + ...(params.phone && { phone: params.phone }), + ...(params.title && { title: params.title }), + }, + ...(params.mode && { mode: params.mode }), + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || data.message || data.detail || `API error: ${response.status}`) + } + + return { + success: true, + output: { + name: data.name ?? null, + company: data.company ?? null, + title: data.title ?? null, + phone: data.phone ?? null, + linkedinUrl: data.linkedin ?? null, + emails: parseEmails(data.email), + personalEmails: parseEmails(data.personal_email), + }, + } + }, + + outputs: { + name: { type: 'string', description: 'Name of the person', optional: true }, + company: { type: 'string', description: 'Company name', optional: true }, + title: { type: 'string', description: 'Job title', optional: true }, + phone: { type: 'string', description: 'Phone number', optional: true }, + linkedinUrl: { type: 'string', description: 'LinkedIn profile URL', optional: true }, + emails: { + type: 'json', + description: 'Professional email addresses found', + properties: { + address: { type: 'string', description: 'Email address' }, + status: { type: 'string', description: 'Validation status (OK or UNKNOWN)' }, + type: { type: 'string', description: 'Email type (COMPANY or PERSONAL)' }, + }, + }, + personalEmails: { + type: 'json', + description: 'Personal email addresses found (only in PERSONAL mode)', + optional: true, + properties: { + address: { type: 'string', description: 'Email address' }, + status: { type: 'string', description: 'Validation status (OK or UNKNOWN)' }, + type: { type: 'string', description: 'Email type (COMPANY or PERSONAL)' }, + }, + }, + }, +} diff --git a/apps/sim/tools/sixtyfour/find_phone.ts b/apps/sim/tools/sixtyfour/find_phone.ts new file mode 100644 index 00000000000..14e68bffe4f --- /dev/null +++ b/apps/sim/tools/sixtyfour/find_phone.ts @@ -0,0 +1,100 @@ +import type { SixtyfourFindPhoneParams, SixtyfourFindPhoneResponse } from '@/tools/sixtyfour/types' +import type { ToolConfig } from '@/tools/types' + +export const sixtyfourFindPhoneTool: ToolConfig< + SixtyfourFindPhoneParams, + SixtyfourFindPhoneResponse +> = { + id: 'sixtyfour_find_phone', + name: 'Sixtyfour Find Phone', + description: 'Find phone numbers for a lead using Sixtyfour AI.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Sixtyfour API key', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Full name of the person', + }, + company: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name', + }, + linkedinUrl: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'LinkedIn profile URL', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company website domain', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Email address', + }, + }, + + request: { + url: 'https://api.sixtyfour.ai/find-phone', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'x-api-key': params.apiKey, + }), + body: (params) => ({ + lead: { + name: params.name, + ...(params.company && { company: params.company }), + ...(params.linkedinUrl && { linkedin_url: params.linkedinUrl }), + ...(params.domain && { domain: params.domain }), + ...(params.email && { email: params.email }), + }, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!response.ok) { + throw new Error(data.error || data.message || data.detail || `API error: ${response.status}`) + } + + let phone: string | null = null + if (typeof data.phone === 'string') { + phone = data.phone || null + } else if (Array.isArray(data.phone)) { + phone = data.phone.map((p: { number: string; region?: string }) => p.number).join(', ') + } + + return { + success: true, + output: { + name: data.name ?? null, + company: data.company ?? null, + phone, + linkedinUrl: data.linkedin_url ?? null, + }, + } + }, + + outputs: { + name: { type: 'string', description: 'Name of the person', optional: true }, + company: { type: 'string', description: 'Company name', optional: true }, + phone: { type: 'string', description: 'Phone number(s) found', optional: true }, + linkedinUrl: { type: 'string', description: 'LinkedIn profile URL', optional: true }, + }, +} diff --git a/apps/sim/tools/sixtyfour/index.ts b/apps/sim/tools/sixtyfour/index.ts new file mode 100644 index 00000000000..2f12d580636 --- /dev/null +++ b/apps/sim/tools/sixtyfour/index.ts @@ -0,0 +1,4 @@ +export { sixtyfourEnrichCompanyTool } from '@/tools/sixtyfour/enrich_company' +export { sixtyfourEnrichLeadTool } from '@/tools/sixtyfour/enrich_lead' +export { sixtyfourFindEmailTool } from '@/tools/sixtyfour/find_email' +export { sixtyfourFindPhoneTool } from '@/tools/sixtyfour/find_phone' diff --git a/apps/sim/tools/sixtyfour/types.ts b/apps/sim/tools/sixtyfour/types.ts new file mode 100644 index 00000000000..e50afcab7fb --- /dev/null +++ b/apps/sim/tools/sixtyfour/types.ts @@ -0,0 +1,78 @@ +import type { ToolResponse } from '@/tools/types' + +export interface SixtyfourFindPhoneParams { + apiKey: string + name: string + company?: string + linkedinUrl?: string + domain?: string + email?: string +} + +export interface SixtyfourFindEmailParams { + apiKey: string + name: string + company?: string + linkedinUrl?: string + domain?: string + phone?: string + title?: string + mode?: string +} + +export interface SixtyfourEnrichLeadParams { + apiKey: string + leadInfo: string + struct: string + researchPlan?: string +} + +export interface SixtyfourEnrichCompanyParams { + apiKey: string + targetCompany: string + struct: string + findPeople?: boolean + fullOrgChart?: boolean + researchPlan?: string + peopleFocusPrompt?: string + leadStruct?: string +} + +export interface SixtyfourFindPhoneResponse extends ToolResponse { + output: { + name: string | null + company: string | null + phone: string | null + linkedinUrl: string | null + } +} + +export interface SixtyfourFindEmailResponse extends ToolResponse { + output: { + name: string | null + company: string | null + title: string | null + phone: string | null + linkedinUrl: string | null + emails: { address: string; status: string; type: string }[] + personalEmails: { address: string; status: string; type: string }[] + } +} + +export interface SixtyfourEnrichLeadResponse extends ToolResponse { + output: { + notes: string | null + structuredData: Record + references: Record + confidenceScore: number | null + } +} + +export interface SixtyfourEnrichCompanyResponse extends ToolResponse { + output: { + notes: string | null + structuredData: Record + references: Record + confidenceScore: number | null + } +} diff --git a/turbo.json b/turbo.json index 5d00f7846da..386c5c0de3c 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://turbo.build/schema.json", + "$schema": "https://v2-9-4.turborepo.dev/schema.json", "envMode": "loose", "tasks": { "transit": {