feat(findymail): add Findymail B2B contact data integration (#4654)

* feat(findymail): add Findymail B2B contact data integration

Adds 11 tools covering verified email lookup (by name, LinkedIn, domain
roles), email verification, reverse email lookup with profile enrichment,
company info, employee discovery, phone lookup, technology stack
detection, and credit checks. Single API-key block with operation
dropdown, gradient-rendered icon, and generated docs.

* fix(findymail): handle HTTP errors and surface last_detected_at

- All 11 tools now check response.ok and return success:false with the API error message on non-2xx responses
- search_technologies now maps last_detected_at to match lookup_technologies and the shared output schema
- Restore file_v3 in docs icon-mapping (translated docs still reference it)

* improvement(findymail): exclude operation from params transform

Match the convention used by enrich/apify/box/calendly — destructure out
operation before forwarding the rest to the tool call, so the operation
key doesn't leak into the tool payload.
This commit is contained in:
Waleed
2026-05-18 12:53:32 -07:00
committed by GitHub
parent b276672867
commit f3cf8fc55d
23 changed files with 2035 additions and 0 deletions
+43
View File
@@ -2283,6 +2283,49 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function FindymailIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45.4462 31.2952' fill='none'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M27.7788 8.18066C26.628 8.18066 25.479 8.62026 24.6016 9.49766L22.7235 11.0666L20.8454 9.49766C19.968 8.62026 18.8189 8.18066 17.6681 8.18066C16.5191 8.18066 15.3683 8.62026 14.4909 9.49766C12.7361 11.2525 12.7361 14.0991 14.4909 15.8521L20.9722 22.0922C21.9497 23.0339 23.4972 23.0339 24.4747 22.0922L30.9578 15.8521C32.7126 14.0991 32.7126 11.2525 30.9578 9.49766C30.0804 8.62026 28.9296 8.18066 27.7788 8.18066Z'
fill='url(#findymail_paint0)'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M42.3815 27.2995C42.3815 27.8571 41.9312 28.3074 41.3737 28.3074H4.0725C3.51497 28.3074 3.06644 27.8571 3.06644 27.2995V7.29083C3.06644 6.39914 4.1279 5.9381 4.77835 6.54745L9.53884 11.0042C9.95341 11.3937 10.6182 11.1971 10.7718 10.6485C10.8791 10.2715 11.0167 9.90338 11.1846 9.54956C11.4062 9.08138 11.2579 8.51848 10.8791 8.16466L7.1854 4.70509C6.52958 4.09037 6.96382 2.9896 7.86445 2.9896H37.58C38.4806 2.9896 38.9166 4.09216 38.259 4.70688L34.5671 8.16109C34.1901 8.51491 34.04 9.07959 34.2633 9.54777C34.4295 9.90159 34.5671 10.2679 34.6743 10.645C34.828 11.1918 35.4928 11.3884 35.9073 11.0006L40.6678 6.54567C41.3183 5.93631 42.3815 6.39735 42.3815 7.28726V27.2995ZM42.9141 0H2.53213C1.13294 0 0 1.13294 0 2.53213V28.7631C0 30.1622 1.13294 31.2952 2.53213 31.2952H42.9141C44.3132 31.2952 45.4462 30.1622 45.4462 28.7631V2.53213C45.4462 1.13294 44.3132 0 42.9141 0Z'
fill='url(#findymail_paint1)'
/>
<defs>
<linearGradient
id='findymail_paint0'
x1='20.5769'
y1='8.68821'
x2='24.6922'
y2='23.0919'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#F04249' />
<stop offset='0.289072' stopColor='#DE2337' />
<stop offset='1' stopColor='#B91C1C' />
</linearGradient>
<linearGradient
id='findymail_paint1'
x1='22.7231'
y1='0'
x2='22.7231'
y2='31.2952'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#18181B' />
<stop offset='1' />
</linearGradient>
</defs>
</svg>
)
}
export function FathomIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 1000' fill='none'>
+3
View File
@@ -58,6 +58,7 @@ import {
ExtendIcon,
EyeIcon,
FathomIcon,
FindymailIcon,
FirecrawlIcon,
FirefliesIcon,
GammaIcon,
@@ -262,7 +263,9 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
extend_v2: ExtendIcon,
fathom: FathomIcon,
file: DocumentIcon,
file_v3: DocumentIcon,
file_v4: DocumentIcon,
findymail: FindymailIcon,
firecrawl: FirecrawlIcon,
fireflies: FirefliesIcon,
fireflies_v2: FirefliesIcon,
@@ -0,0 +1,286 @@
---
title: Findymail
description: Find and verify B2B emails, phones, employees, and company data
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="findymail"
color="#FFFFFF"
/>
{/* MANUAL-CONTENT-START:intro */}
[Findymail](https://findymail.com/) is a B2B contact data platform for finding and verifying work emails, phone numbers, and enriched profile data on company employees. It combines real-time email finding, deliverability verification, reverse-lookup, and technology stack detection in a single API.
With Findymail, you can:
- **Find work emails by name and company:** Resolve a verified work email from a person's name plus a company domain or company name.
- **Find emails from LinkedIn:** Look up the verified work email behind any LinkedIn profile URL.
- **Find contacts by role:** Search a company domain for verified emails matching specific target roles (e.g., CEO, Founder).
- **Verify deliverability:** Check whether an email is deliverable and identify the underlying mail provider.
- **Reverse-lookup profiles:** Given an email, return the matching LinkedIn URL and an optional enriched profile (job, education, skills, certificates).
- **Enrich companies and employees:** Look up company metadata by LinkedIn URL, domain, or name, and find employees by website and target job titles.
- **Find phone numbers:** Retrieve a contact's phone number (US-only) from a LinkedIn profile URL.
- **Detect technology stacks:** Search the technology catalog or look up the full tech stack of a company by domain.
In Sim, the Findymail integration lets your agents programmatically build verified contact lists, enrich CRMs, qualify leads, and gather technographic data without leaving your workflow. Use it to automate outbound prospecting, augment incoming form submissions, validate email captures before sending, and trigger downstream actions when a verified contact is found.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Findymail to find verified work emails by name, domain, or LinkedIn URL, verify deliverability, reverse-lookup profiles from emails, enrich company data, find employees by job title, look up phone numbers, search technology stacks, and check credit usage.
## Tools
### `findymail_verify_email`
Verifies the deliverability of an email address. Uses one verifier credit.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `email` | string | Yes | Email address to verify \(e.g., john@example.com\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `email` | string | The verified email address |
| `verified` | boolean | Whether the email is verified as deliverable |
| `provider` | string | Email service provider \(e.g., Google, Microsoft\) |
### `findymail_find_email_from_name`
Find someone
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | Person's full name \(e.g., 'John Doe'\) |
| `domain` | string | Yes | Company domain \(preferred\) or company name \(e.g., stripe.com\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `contact` | object | Contact information |
| ↳ `name` | string | Contact full name |
| ↳ `email` | string | Contact email address |
| ↳ `domain` | string | Email domain |
### `findymail_find_emails_by_domain`
Find verified contacts at a given domain matching one or more target roles (max 3 roles). Limited to 5 concurrent synchronous requests.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Yes | Company domain \(e.g., stripe.com\) |
| `roles` | array | Yes | Target roles at the company \(max 3, e.g., \["CEO", "Founder"\]\) |
| `items` | string | No | No description |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `contacts` | array | List of contacts found |
| ↳ `name` | string | Contact full name |
| ↳ `email` | string | Contact email address |
| ↳ `domain` | string | Email domain |
### `findymail_find_email_from_linkedin`
Find someone
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `linkedin_url` | string | Yes | Person's LinkedIn URL or username \(e.g., 'https://linkedin.com/in/johndoe' or 'johndoe'\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `contact` | object | Contact information |
| ↳ `name` | string | Contact full name |
| ↳ `email` | string | Contact email address |
| ↳ `domain` | string | Email domain |
### `findymail_reverse_email_lookup`
Find a business profile from an email address. Uses 1 finder credit if a profile is found, 2 credits if returning full profile data.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `email` | string | Yes | Work or personal email address to look up |
| `with_profile` | boolean | No | Whether to return enriched profile metadata \(default: false\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `email` | string | The email address that was looked up |
| `linkedin_url` | string | LinkedIn profile URL |
| `fullName` | string | Full name from profile |
| `username` | string | LinkedIn username |
| `headline` | string | Profile headline |
| `jobTitle` | string | Current job title |
| `summary` | string | Profile summary |
| `city` | string | City |
| `region` | string | Region or state |
| `country` | string | Country |
| `companyLinkedinUrl` | string | Current company LinkedIn URL |
| `companyName` | string | Current company name |
| `companyWebsite` | string | Current company website |
| `isPremium` | boolean | Whether the profile has LinkedIn Premium |
| `isOpenProfile` | boolean | Whether the profile is an Open Profile |
| `skills` | array | List of profile skills |
| `jobs` | array | Job history entries |
| `educations` | array | Education history \(school, degree, fieldOfStudy, startDate, endDate\) |
| `certificates` | array | Certifications \(name, issuingOrganization, issueDate, expirationDate\) |
### `findymail_get_company`
Retrieve company information from a LinkedIn URL, domain, or company name. Uses 1 finder credit per successful response.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `linkedin_url` | string | No | Company LinkedIn URL \(e.g., https://www.linkedin.com/company/stripe/\) |
| `domain` | string | No | Company domain \(e.g., stripe.com\) |
| `name` | string | No | Company name \(e.g., Stripe\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `name` | string | Company name |
| `domain` | string | Company domain |
| `company_size` | string | Employee headcount range \(e.g., 1001-5000\) |
| `industry` | string | Industry classification |
| `linkedin_url` | string | Company LinkedIn URL |
| `description` | string | Company description |
### `findymail_find_employees`
Find employees at a company by website and target job titles. Uses 1 credit per found contact. Does not return email addresses.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `website` | string | Yes | Company website or domain \(e.g., google.com\) |
| `job_titles` | array | Yes | Target job titles to search for \(max 10, e.g., \["Software Engineer", "CEO"\]\) |
| `items` | string | No | No description |
| `count` | number | No | Number of contacts to return \(max 5, default 1\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `employees` | array | List of employees matching the search criteria |
| ↳ `name` | string | Employee full name |
| ↳ `linkedinUrl` | string | LinkedIn profile URL |
| ↳ `companyWebsite` | string | Company website |
| ↳ `companyName` | string | Company name |
| ↳ `jobTitle` | string | Job title |
### `findymail_find_phone`
Find someone
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `linkedin_url` | string | Yes | Person's LinkedIn URL or username \(e.g., 'https://linkedin.com/in/johndoe' or 'johndoe'\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `phone` | string | Phone number in E.164 format. Only available for US numbers. |
| `line_type` | string | Phone line type \(e.g., "Mobile", "Landline"\) |
### `findymail_search_technologies`
Search the technology catalog by name. Returns up to 25 technologies. Free endpoint, rate limited to 10 requests per minute.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search term \(min 2 characters, e.g., "React"\) |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `technologies` | array | List of technologies |
| ↳ `name` | string | Technology name |
| ↳ `category` | string | Technology category |
| ↳ `subcategory` | string | Technology subcategory |
| ↳ `last_detected_at` | string | Last detection timestamp \(ISO 8601\) |
### `findymail_lookup_technologies`
Get the technology stack for a company by domain. Optionally filter by technology names. 1 finder credit if technologies are found, free otherwise.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Yes | Company domain to look up \(e.g., stripe.com\) |
| `technologies` | array | No | Filter by technology names, case-insensitive \(e.g., \["React", "TypeScript"\]\) |
| `items` | string | No | No description |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `technologies` | array | List of technologies |
| ↳ `name` | string | Technology name |
| ↳ `category` | string | Technology category |
| ↳ `subcategory` | string | Technology subcategory |
| ↳ `last_detected_at` | string | Last detection timestamp \(ISO 8601\) |
| `domain` | string | The resolved company domain |
### `findymail_get_credits`
Retrieve the remaining finder and verifier credits for the authenticated account.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Findymail API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `credits` | number | Remaining finder credits |
| `verifier_credits` | number | Remaining verifier credits |
@@ -53,6 +53,7 @@
"extend",
"fathom",
"file",
"findymail",
"firecrawl",
"fireflies",
"gamma",
@@ -58,6 +58,7 @@ import {
ExtendIcon,
EyeIcon,
FathomIcon,
FindymailIcon,
FirecrawlIcon,
FirefliesIcon,
GammaIcon,
@@ -259,6 +260,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
extend_v2: ExtendIcon,
fathom: FathomIcon,
file_v4: DocumentIcon,
findymail: FindymailIcon,
firecrawl: FirecrawlIcon,
fireflies_v2: FirefliesIcon,
gamma: GammaIcon,
@@ -4074,6 +4074,69 @@
"integrationTypes": ["file-storage", "documents"],
"tags": ["document-processing"]
},
{
"type": "findymail",
"slug": "findymail",
"name": "Findymail",
"description": "Find and verify B2B emails, phones, employees, and company data",
"longDescription": "Integrate Findymail to find verified work emails by name, domain, or LinkedIn URL, verify deliverability, reverse-lookup profiles from emails, enrich company data, find employees by job title, look up phone numbers, search technology stacks, and check credit usage.",
"bgColor": "#FFFFFF",
"iconName": "FindymailIcon",
"docsUrl": "https://docs.sim.ai/tools/findymail",
"operations": [
{
"name": "Find Email From Name",
"description": "Find someone"
},
{
"name": "Find Email From LinkedIn",
"description": "Find someone"
},
{
"name": "Find Emails By Domain",
"description": "Find verified contacts at a given domain matching one or more target roles (max 3 roles). Limited to 5 concurrent synchronous requests."
},
{
"name": "Verify Email",
"description": "Verifies the deliverability of an email address. Uses one verifier credit."
},
{
"name": "Reverse Email Lookup",
"description": "Find a business profile from an email address. Uses 1 finder credit if a profile is found, 2 credits if returning full profile data."
},
{
"name": "Get Company Info",
"description": "Retrieve company information from a LinkedIn URL, domain, or company name. Uses 1 finder credit per successful response."
},
{
"name": "Find Employees",
"description": "Find employees at a company by website and target job titles. Uses 1 credit per found contact. Does not return email addresses."
},
{
"name": "Find Phone",
"description": "Find someone"
},
{
"name": "Search Technologies",
"description": "Search the technology catalog by name. Returns up to 25 technologies. Free endpoint, rate limited to 10 requests per minute."
},
{
"name": "Lookup Technologies By Domain",
"description": "Get the technology stack for a company by domain. Optionally filter by technology names. 1 finder credit if technologies are found, free otherwise."
},
{
"name": "Get Remaining Credits",
"description": "Retrieve the remaining finder and verifier credits for the authenticated account."
}
],
"operationCount": 11,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
"category": "tools",
"integrationTypes": ["sales"],
"tags": ["enrichment", "sales-engagement"]
},
{
"type": "firecrawl",
"slug": "firecrawl",
+345
View File
@@ -0,0 +1,345 @@
import { FindymailIcon } from '@/components/icons'
import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types'
import type { FindymailResponse } from '@/tools/findymail/types'
export const FindymailBlock: BlockConfig<FindymailResponse> = {
type: 'findymail',
name: 'Findymail',
description: 'Find and verify B2B emails, phones, employees, and company data',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate Findymail to find verified work emails by name, domain, or LinkedIn URL, verify deliverability, reverse-lookup profiles from emails, enrich company data, find employees by job title, look up phone numbers, search technology stacks, and check credit usage.',
docsLink: 'https://docs.sim.ai/tools/findymail',
category: 'tools',
integrationType: IntegrationType.Sales,
tags: ['enrichment', 'sales-engagement'],
bgColor: '#FFFFFF',
icon: FindymailIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Find Email From Name', id: 'findymail_find_email_from_name' },
{ label: 'Find Email From LinkedIn', id: 'findymail_find_email_from_linkedin' },
{ label: 'Find Emails By Domain', id: 'findymail_find_emails_by_domain' },
{ label: 'Verify Email', id: 'findymail_verify_email' },
{ label: 'Reverse Email Lookup', id: 'findymail_reverse_email_lookup' },
{ label: 'Get Company Info', id: 'findymail_get_company' },
{ label: 'Find Employees', id: 'findymail_find_employees' },
{ label: 'Find Phone', id: 'findymail_find_phone' },
{ label: 'Search Technologies', id: 'findymail_search_technologies' },
{ label: 'Lookup Technologies By Domain', id: 'findymail_lookup_technologies' },
{ label: 'Get Remaining Credits', id: 'findymail_get_credits' },
],
value: () => 'findymail_find_email_from_name',
},
// Find Email From Name
{
id: 'name',
title: 'Full Name',
type: 'short-input',
required: true,
placeholder: 'John Doe',
condition: { field: 'operation', value: 'findymail_find_email_from_name' },
},
{
id: 'domain',
title: 'Company Domain or Name',
type: 'short-input',
required: true,
placeholder: 'stripe.com',
condition: { field: 'operation', value: 'findymail_find_email_from_name' },
},
// Find Email From LinkedIn
{
id: 'linkedin_url',
title: 'LinkedIn URL',
type: 'short-input',
required: true,
placeholder: 'https://linkedin.com/in/johndoe',
condition: { field: 'operation', value: 'findymail_find_email_from_linkedin' },
},
// Find Emails By Domain
{
id: 'domain',
title: 'Domain',
type: 'short-input',
required: true,
placeholder: 'stripe.com',
condition: { field: 'operation', value: 'findymail_find_emails_by_domain' },
},
{
id: 'roles',
title: 'Target Roles',
type: 'long-input',
required: true,
placeholder: '["CEO", "Founder"]',
condition: { field: 'operation', value: 'findymail_find_emails_by_domain' },
},
// Verify Email
{
id: 'email',
title: 'Email Address',
type: 'short-input',
required: true,
placeholder: 'john@example.com',
condition: { field: 'operation', value: 'findymail_verify_email' },
},
// Reverse Email Lookup
{
id: 'email',
title: 'Email Address',
type: 'short-input',
required: true,
placeholder: 'john@example.com',
condition: { field: 'operation', value: 'findymail_reverse_email_lookup' },
},
{
id: 'with_profile',
title: 'Return Full Profile',
type: 'switch',
condition: { field: 'operation', value: 'findymail_reverse_email_lookup' },
mode: 'advanced',
},
// Get Company Info — provide at least one of LinkedIn URL, domain, or name
{
id: 'linkedin_url',
title: 'Company LinkedIn URL',
type: 'short-input',
placeholder: 'LinkedIn URL (at least one of URL/domain/name required)',
condition: { field: 'operation', value: 'findymail_get_company' },
},
{
id: 'domain',
title: 'Company Domain',
type: 'short-input',
placeholder: 'stripe.com (at least one of URL/domain/name required)',
condition: { field: 'operation', value: 'findymail_get_company' },
},
{
id: 'name',
title: 'Company Name',
type: 'short-input',
placeholder: 'Stripe (at least one of URL/domain/name required)',
condition: { field: 'operation', value: 'findymail_get_company' },
},
// Find Employees
{
id: 'website',
title: 'Company Website',
type: 'short-input',
required: true,
placeholder: 'google.com',
condition: { field: 'operation', value: 'findymail_find_employees' },
},
{
id: 'job_titles',
title: 'Job Titles',
type: 'long-input',
required: true,
placeholder: '["Software Engineer", "CEO"]',
condition: { field: 'operation', value: 'findymail_find_employees' },
},
{
id: 'count',
title: 'Number of Contacts',
type: 'short-input',
placeholder: '1',
condition: { field: 'operation', value: 'findymail_find_employees' },
mode: 'advanced',
},
// Find Phone
{
id: 'linkedin_url',
title: 'LinkedIn URL',
type: 'short-input',
required: true,
placeholder: 'https://linkedin.com/in/johndoe',
condition: { field: 'operation', value: 'findymail_find_phone' },
},
// Search Technologies
{
id: 'q',
title: 'Search Term',
type: 'short-input',
required: true,
placeholder: 'React',
condition: { field: 'operation', value: 'findymail_search_technologies' },
},
// Lookup Technologies By Domain
{
id: 'domain',
title: 'Company Domain',
type: 'short-input',
required: true,
placeholder: 'stripe.com',
condition: { field: 'operation', value: 'findymail_lookup_technologies' },
},
{
id: 'technologies',
title: 'Filter Technologies',
type: 'long-input',
placeholder: '["React", "TypeScript"]',
condition: { field: 'operation', value: 'findymail_lookup_technologies' },
mode: 'advanced',
},
// API Key
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
required: true,
placeholder: 'Enter your Findymail API key',
password: true,
},
],
tools: {
access: [
'findymail_verify_email',
'findymail_find_email_from_name',
'findymail_find_emails_by_domain',
'findymail_find_email_from_linkedin',
'findymail_reverse_email_lookup',
'findymail_get_company',
'findymail_find_employees',
'findymail_find_phone',
'findymail_search_technologies',
'findymail_lookup_technologies',
'findymail_get_credits',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'findymail_verify_email':
case 'findymail_find_email_from_name':
case 'findymail_find_emails_by_domain':
case 'findymail_find_email_from_linkedin':
case 'findymail_reverse_email_lookup':
case 'findymail_get_company':
case 'findymail_find_employees':
case 'findymail_find_phone':
case 'findymail_search_technologies':
case 'findymail_lookup_technologies':
case 'findymail_get_credits':
return params.operation
default:
return 'findymail_find_email_from_name'
}
},
params: (params) => {
const { operation: _operation, ...rest } = params
const result: Record<string, unknown> = {}
for (const [key, value] of Object.entries(rest)) {
if (value === undefined || value === null || value === '') continue
if (key === 'count') {
const n = Number(value)
if (!Number.isNaN(n)) result[key] = n
} else if (key === 'roles' || key === 'job_titles' || key === 'technologies') {
if (Array.isArray(value)) {
result[key] = value
} else if (typeof value === 'string') {
const trimmed = value.trim()
if (trimmed.startsWith('[')) {
try {
const parsed = JSON.parse(trimmed)
if (Array.isArray(parsed)) {
result[key] = parsed
continue
}
} catch {
// fall through to comma-split
}
}
result[key] = trimmed
.split(',')
.map((s) => s.trim())
.filter(Boolean)
}
} else {
result[key] = value
}
}
return result
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Findymail API key' },
email: { type: 'string', description: 'Email address' },
name: { type: 'string', description: 'Full name or company name' },
domain: { type: 'string', description: 'Company domain' },
linkedin_url: { type: 'string', description: 'LinkedIn profile or company URL' },
roles: { type: 'array', description: 'Target roles (max 3)' },
with_profile: { type: 'boolean', description: 'Return full profile data on reverse lookup' },
website: { type: 'string', description: 'Company website for employee search' },
job_titles: { type: 'array', description: 'Target job titles (max 10)' },
count: { type: 'number', description: 'Number of contacts to return (max 5)' },
q: { type: 'string', description: 'Technology search query' },
technologies: { type: 'array', description: 'Technology names to filter by' },
},
outputs: {
// Verify Email
verified: { type: 'boolean', description: 'Whether the email is deliverable' },
provider: { type: 'string', description: 'Email service provider' },
// Find / Reverse / Company
contact: {
type: 'json',
description: 'Contact found (name, email, domain)',
},
contacts: {
type: 'array',
description: 'Contacts found at the domain (name, email, domain)',
},
linkedin_url: { type: 'string', description: 'LinkedIn URL' },
fullName: { type: 'string', description: 'Full name from LinkedIn profile' },
username: { type: 'string', description: 'LinkedIn username' },
headline: { type: 'string', description: 'Profile headline' },
jobTitle: { type: 'string', description: 'Job title' },
summary: { type: 'string', description: 'Profile summary' },
city: { type: 'string', description: 'City' },
region: { type: 'string', description: 'Region/state' },
country: { type: 'string', description: 'Country' },
companyLinkedinUrl: { type: 'string', description: 'Current company LinkedIn URL' },
companyName: { type: 'string', description: 'Current company name' },
companyWebsite: { type: 'string', description: 'Current company website' },
isPremium: { type: 'boolean', description: 'Whether the profile has LinkedIn Premium' },
isOpenProfile: { type: 'boolean', description: 'Whether the profile is open' },
skills: { type: 'array', description: 'Profile skills' },
jobs: { type: 'array', description: 'Job history entries' },
educations: {
type: 'array',
description: 'Education history (school, degree, fieldOfStudy, startDate, endDate)',
},
certificates: {
type: 'array',
description: 'Certifications (name, issuingOrganization, issueDate, expirationDate)',
},
// Get Company
name: { type: 'string', description: 'Company name (get-company)' },
domain: { type: 'string', description: 'Company domain' },
company_size: { type: 'string', description: 'Headcount range (e.g., 1001-5000)' },
industry: { type: 'string', description: 'Industry classification' },
description: { type: 'string', description: 'Company description' },
// Find Employees
employees: {
type: 'array',
description: 'Employees found (name, linkedinUrl, companyWebsite, companyName, jobTitle)',
},
// Find Phone
phone: { type: 'string', description: 'Phone number in E.164 format (US only)' },
line_type: { type: 'string', description: 'Phone line type (Mobile, Landline)' },
// Technologies
technologies: {
type: 'array',
description: 'Technologies (name, category, subcategory, last_detected_at)',
},
// Get Credits
credits: { type: 'number', description: 'Remaining finder credits' },
verifier_credits: { type: 'number', description: 'Remaining verifier credits' },
// Reverse / Verify shared
email: { type: 'string', description: 'Email address' },
},
}
+2
View File
@@ -56,6 +56,7 @@ import { ExaBlock } from '@/blocks/blocks/exa'
import { ExtendBlock, ExtendV2Block } from '@/blocks/blocks/extend'
import { FathomBlock } from '@/blocks/blocks/fathom'
import { FileBlock, FileV2Block, FileV3Block, FileV4Block } from '@/blocks/blocks/file'
import { FindymailBlock } from '@/blocks/blocks/findymail'
import { FirecrawlBlock } from '@/blocks/blocks/firecrawl'
import { FirefliesBlock, FirefliesV2Block } from '@/blocks/blocks/fireflies'
import { FunctionBlock } from '@/blocks/blocks/function'
@@ -301,6 +302,7 @@ export const registry: Record<string, BlockConfig> = {
file_v2: FileV2Block,
file_v3: FileV3Block,
file_v4: FileV4Block,
findymail: FindymailBlock,
firecrawl: FirecrawlBlock,
fireflies: FirefliesBlock,
fireflies_v2: FirefliesV2Block,
+43
View File
@@ -2283,6 +2283,49 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
)
}
export function FindymailIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45.4462 31.2952' fill='none'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M27.7788 8.18066C26.628 8.18066 25.479 8.62026 24.6016 9.49766L22.7235 11.0666L20.8454 9.49766C19.968 8.62026 18.8189 8.18066 17.6681 8.18066C16.5191 8.18066 15.3683 8.62026 14.4909 9.49766C12.7361 11.2525 12.7361 14.0991 14.4909 15.8521L20.9722 22.0922C21.9497 23.0339 23.4972 23.0339 24.4747 22.0922L30.9578 15.8521C32.7126 14.0991 32.7126 11.2525 30.9578 9.49766C30.0804 8.62026 28.9296 8.18066 27.7788 8.18066Z'
fill='url(#findymail_paint0)'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M42.3815 27.2995C42.3815 27.8571 41.9312 28.3074 41.3737 28.3074H4.0725C3.51497 28.3074 3.06644 27.8571 3.06644 27.2995V7.29083C3.06644 6.39914 4.1279 5.9381 4.77835 6.54745L9.53884 11.0042C9.95341 11.3937 10.6182 11.1971 10.7718 10.6485C10.8791 10.2715 11.0167 9.90338 11.1846 9.54956C11.4062 9.08138 11.2579 8.51848 10.8791 8.16466L7.1854 4.70509C6.52958 4.09037 6.96382 2.9896 7.86445 2.9896H37.58C38.4806 2.9896 38.9166 4.09216 38.259 4.70688L34.5671 8.16109C34.1901 8.51491 34.04 9.07959 34.2633 9.54777C34.4295 9.90159 34.5671 10.2679 34.6743 10.645C34.828 11.1918 35.4928 11.3884 35.9073 11.0006L40.6678 6.54567C41.3183 5.93631 42.3815 6.39735 42.3815 7.28726V27.2995ZM42.9141 0H2.53213C1.13294 0 0 1.13294 0 2.53213V28.7631C0 30.1622 1.13294 31.2952 2.53213 31.2952H42.9141C44.3132 31.2952 45.4462 30.1622 45.4462 28.7631V2.53213C45.4462 1.13294 44.3132 0 42.9141 0Z'
fill='url(#findymail_paint1)'
/>
<defs>
<linearGradient
id='findymail_paint0'
x1='20.5769'
y1='8.68821'
x2='24.6922'
y2='23.0919'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#F04249' />
<stop offset='0.289072' stopColor='#DE2337' />
<stop offset='1' stopColor='#B91C1C' />
</linearGradient>
<linearGradient
id='findymail_paint1'
x1='22.7231'
y1='0'
x2='22.7231'
y2='31.2952'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#18181B' />
<stop offset='1' />
</linearGradient>
</defs>
</svg>
)
}
export function FathomIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 1000' fill='none'>
@@ -0,0 +1,75 @@
import type {
FindymailFindEmailFromLinkedInParams,
FindymailFindEmailFromLinkedInResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACT_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailFromLinkedInTool: ToolConfig<
FindymailFindEmailFromLinkedInParams,
FindymailFindEmailFromLinkedInResponse
> = {
id: 'findymail_find_email_from_linkedin',
name: 'Findymail Find Email From LinkedIn',
description:
"Find someone's email from a LinkedIn profile URL or username. Uses one finder credit when a verified email is found.",
version: '1.0.0',
params: {
linkedin_url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
"Person's LinkedIn URL or username (e.g., 'https://linkedin.com/in/johndoe' or 'johndoe')",
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/business-profile',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ linkedin_url: params.linkedin_url }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contact: null },
}
}
const data = await response.json()
const contact = data.contact ?? data.payload?.contact ?? null
return {
success: true,
output: {
contact: contact
? {
name: contact.name ?? '',
email: contact.email ?? '',
domain: contact.domain ?? '',
}
: null,
},
}
},
outputs: {
contact: { ...FINDYMAIL_CONTACT_OUTPUT, optional: true },
},
}
@@ -0,0 +1,80 @@
import type {
FindymailFindEmailFromNameParams,
FindymailFindEmailFromNameResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACT_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailFromNameTool: ToolConfig<
FindymailFindEmailFromNameParams,
FindymailFindEmailFromNameResponse
> = {
id: 'findymail_find_email_from_name',
name: 'Findymail Find Email From Name',
description:
"Find someone's email from their name and a company domain or company name. Uses one finder credit when a verified email is found.",
version: '1.0.0',
params: {
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Person's full name (e.g., 'John Doe')",
},
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain (preferred) or company name (e.g., stripe.com)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/name',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ name: params.name, domain: params.domain }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contact: null },
}
}
const data = await response.json()
const contact = data.contact ?? data.payload?.contact ?? null
return {
success: true,
output: {
contact: contact
? {
name: contact.name ?? '',
email: contact.email ?? '',
domain: contact.domain ?? '',
}
: null,
},
}
},
outputs: {
contact: { ...FINDYMAIL_CONTACT_OUTPUT, optional: true },
},
}
@@ -0,0 +1,77 @@
import type {
FindymailFindEmailsByDomainParams,
FindymailFindEmailsByDomainResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_CONTACTS_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmailsByDomainTool: ToolConfig<
FindymailFindEmailsByDomainParams,
FindymailFindEmailsByDomainResponse
> = {
id: 'findymail_find_emails_by_domain',
name: 'Findymail Find Emails By Domain',
description:
'Find verified contacts at a given domain matching one or more target roles (max 3 roles). Limited to 5 concurrent synchronous requests.',
version: '1.0.0',
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain (e.g., stripe.com)',
},
roles: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Target roles at the company (max 3, e.g., ["CEO", "Founder"])',
items: { type: 'string' },
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/domain',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ domain: params.domain, roles: params.roles }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { contacts: [] },
}
}
const data = await response.json()
const raw = data.contacts ?? data.payload?.contacts ?? []
const contacts = Array.isArray(raw)
? raw.map((c: { name?: string; email?: string; domain?: string }) => ({
name: c.name ?? '',
email: c.email ?? '',
domain: c.domain ?? '',
}))
: []
return { success: true, output: { contacts } }
},
outputs: {
contacts: FINDYMAIL_CONTACTS_OUTPUT,
},
}
+100
View File
@@ -0,0 +1,100 @@
import type {
FindymailFindEmployeesParams,
FindymailFindEmployeesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_EMPLOYEES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findEmployeesTool: ToolConfig<
FindymailFindEmployeesParams,
FindymailFindEmployeesResponse
> = {
id: 'findymail_find_employees',
name: 'Findymail Find Employees',
description:
'Find employees at a company by website and target job titles. Uses 1 credit per found contact. Does not return email addresses.',
version: '1.0.0',
params: {
website: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company website or domain (e.g., google.com)',
},
job_titles: {
type: 'array',
required: true,
visibility: 'user-or-llm',
description: 'Target job titles to search for (max 10, e.g., ["Software Engineer", "CEO"])',
items: { type: 'string' },
},
count: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of contacts to return (max 5, default 1)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/employees',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
website: params.website,
job_titles: params.job_titles,
}
if (params.count !== undefined) body.count = params.count
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { employees: [] },
}
}
const data = await response.json()
const raw = Array.isArray(data) ? data : (data.data ?? [])
const employees = Array.isArray(raw)
? raw.map(
(e: {
name?: string
linkedinUrl?: string
companyWebsite?: string
companyName?: string
jobTitle?: string
}) => ({
name: e.name ?? '',
linkedinUrl: e.linkedinUrl ?? null,
companyWebsite: e.companyWebsite ?? null,
companyName: e.companyName ?? null,
jobTitle: e.jobTitle ?? null,
})
)
: []
return { success: true, output: { employees } }
},
outputs: {
employees: FINDYMAIL_EMPLOYEES_OUTPUT,
},
}
+71
View File
@@ -0,0 +1,71 @@
import type { FindymailFindPhoneParams, FindymailFindPhoneResponse } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const findPhoneTool: ToolConfig<FindymailFindPhoneParams, FindymailFindPhoneResponse> = {
id: 'findymail_find_phone',
name: 'Findymail Find Phone',
description:
"Find someone's phone number from a LinkedIn profile URL. Uses 10 finder credits if a phone is found. EU citizens are excluded for legal reasons.",
version: '1.0.0',
params: {
linkedin_url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
"Person's LinkedIn URL or username (e.g., 'https://linkedin.com/in/johndoe' or 'johndoe')",
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/phone',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ linkedin_url: params.linkedin_url }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { phone: null, line_type: null },
}
}
const data = await response.json()
return {
success: true,
output: {
phone: data.phone ?? null,
line_type: data.line_type ?? null,
},
}
},
outputs: {
phone: {
type: 'string',
description: 'Phone number in E.164 format. Only available for US numbers.',
optional: true,
},
line_type: {
type: 'string',
description: 'Phone line type (e.g., "Mobile", "Landline")',
optional: true,
},
},
}
+102
View File
@@ -0,0 +1,102 @@
import type {
FindymailGetCompanyParams,
FindymailGetCompanyResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const getCompanyTool: ToolConfig<FindymailGetCompanyParams, FindymailGetCompanyResponse> = {
id: 'findymail_get_company',
name: 'Findymail Get Company',
description:
'Retrieve company information from a LinkedIn URL, domain, or company name. Uses 1 finder credit per successful response.',
version: '1.0.0',
params: {
linkedin_url: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company LinkedIn URL (e.g., https://www.linkedin.com/company/stripe/)',
},
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company domain (e.g., stripe.com)',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Company name (e.g., Stripe)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/company',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, string> = {}
if (params.linkedin_url) body.linkedin_url = params.linkedin_url
if (params.domain) body.domain = params.domain
if (params.name) body.name = params.name
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: {
name: null,
domain: null,
company_size: null,
industry: null,
linkedin_url: null,
description: null,
},
}
}
const data = await response.json()
return {
success: true,
output: {
name: data.name ?? null,
domain: data.domain ?? null,
company_size: data.company_size ?? null,
industry: data.industry ?? null,
linkedin_url: data.linkedin_url ?? null,
description: data.description ?? null,
},
}
},
outputs: {
name: { type: 'string', description: 'Company name', optional: true },
domain: { type: 'string', description: 'Company domain', optional: true },
company_size: {
type: 'string',
description: 'Employee headcount range (e.g., 1001-5000)',
optional: true,
},
industry: { type: 'string', description: 'Industry classification', optional: true },
linkedin_url: { type: 'string', description: 'Company LinkedIn URL', optional: true },
description: { type: 'string', description: 'Company description', optional: true },
},
}
+56
View File
@@ -0,0 +1,56 @@
import type {
FindymailGetCreditsParams,
FindymailGetCreditsResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const getCreditsTool: ToolConfig<FindymailGetCreditsParams, FindymailGetCreditsResponse> = {
id: 'findymail_get_credits',
name: 'Findymail Get Credits',
description: 'Retrieve the remaining finder and verifier credits for the authenticated account.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/credits',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { credits: 0, verifier_credits: 0 },
}
}
const data = await response.json()
return {
success: true,
output: {
credits: data.credits ?? 0,
verifier_credits: data.verifier_credits ?? 0,
},
}
},
outputs: {
credits: { type: 'number', description: 'Remaining finder credits' },
verifier_credits: { type: 'number', description: 'Remaining verifier credits' },
},
}
+23
View File
@@ -0,0 +1,23 @@
import { findEmailFromLinkedInTool } from '@/tools/findymail/find_email_from_linkedin'
import { findEmailFromNameTool } from '@/tools/findymail/find_email_from_name'
import { findEmailsByDomainTool } from '@/tools/findymail/find_emails_by_domain'
import { findEmployeesTool } from '@/tools/findymail/find_employees'
import { findPhoneTool } from '@/tools/findymail/find_phone'
import { getCompanyTool } from '@/tools/findymail/get_company'
import { getCreditsTool } from '@/tools/findymail/get_credits'
import { lookupTechnologiesTool } from '@/tools/findymail/lookup_technologies'
import { reverseEmailLookupTool } from '@/tools/findymail/reverse_email_lookup'
import { searchTechnologiesTool } from '@/tools/findymail/search_technologies'
import { verifyEmailTool } from '@/tools/findymail/verify_email'
export const findymailVerifyEmailTool = verifyEmailTool
export const findymailFindEmailFromNameTool = findEmailFromNameTool
export const findymailFindEmailsByDomainTool = findEmailsByDomainTool
export const findymailFindEmailFromLinkedInTool = findEmailFromLinkedInTool
export const findymailReverseEmailLookupTool = reverseEmailLookupTool
export const findymailGetCompanyTool = getCompanyTool
export const findymailFindEmployeesTool = findEmployeesTool
export const findymailFindPhoneTool = findPhoneTool
export const findymailSearchTechnologiesTool = searchTechnologiesTool
export const findymailLookupTechnologiesTool = lookupTechnologiesTool
export const findymailGetCreditsTool = getCreditsTool
@@ -0,0 +1,98 @@
import type {
FindymailLookupTechnologiesParams,
FindymailLookupTechnologiesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_TECHNOLOGIES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const lookupTechnologiesTool: ToolConfig<
FindymailLookupTechnologiesParams,
FindymailLookupTechnologiesResponse
> = {
id: 'findymail_lookup_technologies',
name: 'Findymail Lookup Technologies',
description:
'Get the technology stack for a company by domain. Optionally filter by technology names. 1 finder credit if technologies are found, free otherwise.',
version: '1.0.0',
params: {
domain: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Company domain to look up (e.g., stripe.com)',
},
technologies: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Filter by technology names, case-insensitive (e.g., ["React", "TypeScript"])',
items: { type: 'string' },
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/technologies',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = { domain: params.domain }
if (params.technologies && params.technologies.length > 0) {
body.technologies = params.technologies
}
return body
},
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { domain: '', technologies: [] },
}
}
const data = await response.json()
const raw = data.technologies ?? []
const technologies = Array.isArray(raw)
? raw.map(
(t: {
name?: string
category?: string
subcategory?: string
last_detected_at?: string
}) => ({
name: t.name ?? '',
category: t.category ?? null,
subcategory: t.subcategory ?? null,
last_detected_at: t.last_detected_at ?? null,
})
)
: []
return {
success: true,
output: {
domain: data.domain ?? '',
technologies,
},
}
},
outputs: {
domain: { type: 'string', description: 'The resolved company domain' },
technologies: FINDYMAIL_TECHNOLOGIES_OUTPUT,
},
}
@@ -0,0 +1,149 @@
import type {
FindymailReverseEmailLookupParams,
FindymailReverseEmailLookupResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const reverseEmailLookupTool: ToolConfig<
FindymailReverseEmailLookupParams,
FindymailReverseEmailLookupResponse
> = {
id: 'findymail_reverse_email_lookup',
name: 'Findymail Reverse Email Lookup',
description:
'Find a business profile from an email address. Uses 1 finder credit if a profile is found, 2 credits if returning full profile data.',
version: '1.0.0',
params: {
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Work or personal email address to look up',
},
with_profile: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to return enriched profile metadata (default: false)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/search/reverse-email',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({
email: params.email,
...(params.with_profile ? { with_profile: true } : {}),
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: {
email: null,
linkedin_url: null,
fullName: null,
username: null,
headline: null,
jobTitle: null,
summary: null,
city: null,
region: null,
country: null,
companyLinkedinUrl: null,
companyName: null,
companyWebsite: null,
isPremium: null,
isOpenProfile: null,
skills: [],
jobs: [],
educations: [],
certificates: [],
},
}
}
const data = await response.json()
return {
success: true,
output: {
email: data.email ?? null,
linkedin_url: data.linkedin_url ?? null,
fullName: data.fullName ?? null,
username: data.username ?? null,
headline: data.headline ?? null,
jobTitle: data.jobTitle ?? null,
summary: data.summary ?? null,
city: data.city ?? null,
region: data.region ?? null,
country: data.country ?? null,
companyLinkedinUrl: data.companyLinkedinUrl ?? null,
companyName: data.companyName ?? null,
companyWebsite: data.companyWebsite ?? null,
isPremium: data.isPremium ?? null,
isOpenProfile: data.isOpenProfile ?? null,
skills: data.skills ?? [],
jobs: data.jobs ?? [],
educations: data.educations ?? [],
certificates: data.certificates ?? [],
},
}
},
outputs: {
email: { type: 'string', description: 'The email address that was looked up', optional: true },
linkedin_url: { type: 'string', description: 'LinkedIn profile URL', optional: true },
fullName: { type: 'string', description: 'Full name from profile', optional: true },
username: { type: 'string', description: 'LinkedIn username', optional: true },
headline: { type: 'string', description: 'Profile headline', optional: true },
jobTitle: { type: 'string', description: 'Current job title', optional: true },
summary: { type: 'string', description: 'Profile summary', optional: true },
city: { type: 'string', description: 'City', optional: true },
region: { type: 'string', description: 'Region or state', optional: true },
country: { type: 'string', description: 'Country', optional: true },
companyLinkedinUrl: {
type: 'string',
description: 'Current company LinkedIn URL',
optional: true,
},
companyName: { type: 'string', description: 'Current company name', optional: true },
companyWebsite: { type: 'string', description: 'Current company website', optional: true },
isPremium: {
type: 'boolean',
description: 'Whether the profile has LinkedIn Premium',
optional: true,
},
isOpenProfile: {
type: 'boolean',
description: 'Whether the profile is an Open Profile',
optional: true,
},
skills: { type: 'array', description: 'List of profile skills' },
jobs: { type: 'array', description: 'Job history entries' },
educations: {
type: 'array',
description: 'Education history (school, degree, fieldOfStudy, startDate, endDate)',
},
certificates: {
type: 'array',
description: 'Certifications (name, issuingOrganization, issueDate, expirationDate)',
},
},
}
@@ -0,0 +1,80 @@
import type {
FindymailSearchTechnologiesParams,
FindymailSearchTechnologiesResponse,
} from '@/tools/findymail/types'
import { FINDYMAIL_TECHNOLOGIES_OUTPUT } from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const searchTechnologiesTool: ToolConfig<
FindymailSearchTechnologiesParams,
FindymailSearchTechnologiesResponse
> = {
id: 'findymail_search_technologies',
name: 'Findymail Search Technologies',
description:
'Search the technology catalog by name. Returns up to 25 technologies. Free endpoint, rate limited to 10 requests per minute.',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Search term (min 2 characters, e.g., "React")',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: (params) => {
const url = new URL('https://app.findymail.com/api/technologies/search')
url.searchParams.append('q', params.q)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { technologies: [] },
}
}
const data = await response.json()
const raw = data.data ?? []
const technologies = Array.isArray(raw)
? raw.map(
(t: {
name?: string
category?: string
subcategory?: string
last_detected_at?: string
}) => ({
name: t.name ?? '',
category: t.category ?? null,
subcategory: t.subcategory ?? null,
last_detected_at: t.last_detected_at ?? null,
})
)
: []
return { success: true, output: { technologies } }
},
outputs: {
technologies: FINDYMAIL_TECHNOLOGIES_OUTPUT,
},
}
+241
View File
@@ -0,0 +1,241 @@
import type { OutputProperty, ToolResponse } from '@/tools/types'
interface FindymailBaseParams {
apiKey: string
}
export const FINDYMAIL_CONTACT_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Contact full name' },
email: { type: 'string', description: 'Contact email address' },
domain: { type: 'string', description: 'Email domain' },
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_CONTACT_OUTPUT: OutputProperty = {
type: 'object',
description: 'Contact information',
properties: FINDYMAIL_CONTACT_OUTPUT_PROPERTIES,
}
export const FINDYMAIL_CONTACTS_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of contacts found',
items: {
type: 'object',
properties: FINDYMAIL_CONTACT_OUTPUT_PROPERTIES,
},
}
export const FINDYMAIL_TECHNOLOGY_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Technology name' },
category: { type: 'string', description: 'Technology category' },
subcategory: { type: 'string', description: 'Technology subcategory' },
last_detected_at: {
type: 'string',
description: 'Last detection timestamp (ISO 8601)',
optional: true,
},
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_TECHNOLOGIES_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of technologies',
items: {
type: 'object',
properties: FINDYMAIL_TECHNOLOGY_OUTPUT_PROPERTIES,
},
}
export const FINDYMAIL_EMPLOYEE_OUTPUT_PROPERTIES = {
name: { type: 'string', description: 'Employee full name' },
linkedinUrl: { type: 'string', description: 'LinkedIn profile URL', optional: true },
companyWebsite: { type: 'string', description: 'Company website', optional: true },
companyName: { type: 'string', description: 'Company name', optional: true },
jobTitle: { type: 'string', description: 'Job title', optional: true },
} as const satisfies Record<string, OutputProperty>
export const FINDYMAIL_EMPLOYEES_OUTPUT: OutputProperty = {
type: 'array',
description: 'List of employees matching the search criteria',
items: {
type: 'object',
properties: FINDYMAIL_EMPLOYEE_OUTPUT_PROPERTIES,
},
}
export interface FindymailContact {
name: string
email: string
domain: string
}
export interface FindymailVerifyEmailParams extends FindymailBaseParams {
email: string
}
export interface FindymailVerifyEmailResponse extends ToolResponse {
output: {
email: string
verified: boolean
provider: string | null
}
}
export interface FindymailFindEmailFromNameParams extends FindymailBaseParams {
name: string
domain: string
}
export interface FindymailFindEmailFromNameResponse extends ToolResponse {
output: {
contact: FindymailContact | null
}
}
export interface FindymailFindEmailsByDomainParams extends FindymailBaseParams {
domain: string
roles: string[]
}
export interface FindymailFindEmailsByDomainResponse extends ToolResponse {
output: {
contacts: FindymailContact[]
}
}
export interface FindymailFindEmailFromLinkedInParams extends FindymailBaseParams {
linkedin_url: string
}
export interface FindymailFindEmailFromLinkedInResponse extends ToolResponse {
output: {
contact: FindymailContact | null
}
}
export interface FindymailReverseEmailLookupParams extends FindymailBaseParams {
email: string
with_profile?: boolean
}
export interface FindymailReverseEmailLookupResponse extends ToolResponse {
output: {
email: string | null
linkedin_url: string | null
fullName: string | null
username: string | null
headline: string | null
jobTitle: string | null
summary: string | null
city: string | null
region: string | null
country: string | null
companyLinkedinUrl: string | null
companyName: string | null
companyWebsite: string | null
isPremium: boolean | null
isOpenProfile: boolean | null
skills: unknown[]
jobs: unknown[]
educations: unknown[]
certificates: unknown[]
}
}
export interface FindymailGetCompanyParams extends FindymailBaseParams {
linkedin_url?: string
domain?: string
name?: string
}
export interface FindymailGetCompanyResponse extends ToolResponse {
output: {
name: string | null
domain: string | null
company_size: string | null
industry: string | null
linkedin_url: string | null
description: string | null
}
}
export interface FindymailFindEmployeesParams extends FindymailBaseParams {
website: string
job_titles: string[]
count?: number
}
export interface FindymailEmployee {
name: string
linkedinUrl: string | null
companyWebsite: string | null
companyName: string | null
jobTitle: string | null
}
export interface FindymailFindEmployeesResponse extends ToolResponse {
output: {
employees: FindymailEmployee[]
}
}
export interface FindymailFindPhoneParams extends FindymailBaseParams {
linkedin_url: string
}
export interface FindymailFindPhoneResponse extends ToolResponse {
output: {
phone: string | null
line_type: string | null
}
}
export interface FindymailSearchTechnologiesParams extends FindymailBaseParams {
q: string
}
export interface FindymailTechnology {
name: string
category: string | null
subcategory: string | null
last_detected_at?: string | null
}
export interface FindymailSearchTechnologiesResponse extends ToolResponse {
output: {
technologies: FindymailTechnology[]
}
}
export interface FindymailLookupTechnologiesParams extends FindymailBaseParams {
domain: string
technologies?: string[]
}
export interface FindymailLookupTechnologiesResponse extends ToolResponse {
output: {
domain: string
technologies: FindymailTechnology[]
}
}
export interface FindymailGetCreditsParams extends FindymailBaseParams {}
export interface FindymailGetCreditsResponse extends ToolResponse {
output: {
credits: number
verifier_credits: number
}
}
export type FindymailResponse =
| FindymailVerifyEmailResponse
| FindymailFindEmailFromNameResponse
| FindymailFindEmailsByDomainResponse
| FindymailFindEmailFromLinkedInResponse
| FindymailReverseEmailLookupResponse
| FindymailGetCompanyResponse
| FindymailFindEmployeesResponse
| FindymailFindPhoneResponse
| FindymailSearchTechnologiesResponse
| FindymailLookupTechnologiesResponse
| FindymailGetCreditsResponse
+71
View File
@@ -0,0 +1,71 @@
import type {
FindymailVerifyEmailParams,
FindymailVerifyEmailResponse,
} from '@/tools/findymail/types'
import type { ToolConfig } from '@/tools/types'
export const verifyEmailTool: ToolConfig<FindymailVerifyEmailParams, FindymailVerifyEmailResponse> =
{
id: 'findymail_verify_email',
name: 'Findymail Verify Email',
description: 'Verifies the deliverability of an email address. Uses one verifier credit.',
version: '1.0.0',
params: {
email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email address to verify (e.g., john@example.com)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Findymail API Key',
},
},
request: {
url: 'https://app.findymail.com/api/verify',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => ({ email: params.email }),
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
return {
success: false,
error:
(errorData as Record<string, string>).message ||
`Findymail API error: ${response.status} ${response.statusText}`,
output: { email: '', verified: false, provider: null },
}
}
const data = await response.json()
return {
success: true,
output: {
email: data.email ?? '',
verified: data.verified ?? false,
provider: data.provider ?? null,
},
}
},
outputs: {
email: { type: 'string', description: 'The verified email address' },
verified: { type: 'boolean', description: 'Whether the email is verified as deliverable' },
provider: {
type: 'string',
description: 'Email service provider (e.g., Google, Microsoft)',
optional: true,
},
},
}
+24
View File
@@ -653,6 +653,19 @@ import {
fileReadTool,
fileWriteTool,
} from '@/tools/file'
import {
findymailFindEmailFromLinkedInTool,
findymailFindEmailFromNameTool,
findymailFindEmailsByDomainTool,
findymailFindEmployeesTool,
findymailFindPhoneTool,
findymailGetCompanyTool,
findymailGetCreditsTool,
findymailLookupTechnologiesTool,
findymailReverseEmailLookupTool,
findymailSearchTechnologiesTool,
findymailVerifyEmailTool,
} from '@/tools/findymail'
import {
firecrawlAgentTool,
firecrawlCrawlTool,
@@ -4823,6 +4836,17 @@ export const tools: Record<string, ToolConfig> = {
fathom_get_transcript: fathomGetTranscriptTool,
fathom_list_team_members: fathomListTeamMembersTool,
fathom_list_teams: fathomListTeamsTool,
findymail_verify_email: findymailVerifyEmailTool,
findymail_find_email_from_name: findymailFindEmailFromNameTool,
findymail_find_emails_by_domain: findymailFindEmailsByDomainTool,
findymail_find_email_from_linkedin: findymailFindEmailFromLinkedInTool,
findymail_reverse_email_lookup: findymailReverseEmailLookupTool,
findymail_get_company: findymailGetCompanyTool,
findymail_find_employees: findymailFindEmployeesTool,
findymail_find_phone: findymailFindPhoneTool,
findymail_search_technologies: findymailSearchTechnologiesTool,
findymail_lookup_technologies: findymailLookupTechnologiesTool,
findymail_get_credits: findymailGetCreditsTool,
stt_whisper: whisperSttTool,
stt_whisper_v2: whisperSttV2Tool,
stt_deepgram: deepgramSttTool,