chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailFindEmailFromLinkedInParams>((_params, output) => {
|
||||
const contact = output.contact as { email?: string } | null
|
||||
return contact?.email ? 1 : 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,87 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailFindEmailFromNameParams>((_params, output) => {
|
||||
const contact = output.contact as { email?: string } | null
|
||||
return contact?.email ? 1 : 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,88 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailFindEmailsByDomainParams>((_params, output) => {
|
||||
// No contacts array means no verified contacts returned — no charge.
|
||||
if (!Array.isArray(output.contacts)) {
|
||||
return 0
|
||||
}
|
||||
// 1 finder credit per verified contact returned.
|
||||
return output.contacts.length
|
||||
}),
|
||||
|
||||
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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailFindEmployeesParams>((_params, output) => {
|
||||
// No employees array means no contacts found — no charge.
|
||||
if (!Array.isArray(output.employees)) {
|
||||
return 0
|
||||
}
|
||||
// 1 finder credit per contact found.
|
||||
return output.employees.length
|
||||
}),
|
||||
|
||||
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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailFindPhoneParams>((_params, output) => {
|
||||
// Phone lookups consume 10 finder credits, only when a number is found.
|
||||
return output.phone ? 10 : 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailGetCompanyParams>((_params, output) => {
|
||||
// 1 finder credit per successful company match.
|
||||
return output.name || output.domain ? 1 : 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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 },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { ToolHostingConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Env var prefix for Findymail hosted keys. Provide keys as
|
||||
* `FINDYMAIL_API_KEY_COUNT` plus `FINDYMAIL_API_KEY_1..N`.
|
||||
*/
|
||||
export const FINDYMAIL_API_KEY_PREFIX = 'FINDYMAIL_API_KEY'
|
||||
|
||||
/**
|
||||
* Dollar cost of a single Findymail finder credit.
|
||||
*
|
||||
* Findymail charges per verified result: 1 credit per email, 10 credits per
|
||||
* phone, and only when a result is found. Estimated from the $99/month Starter
|
||||
* plan (5,000 credits ≈ $0.0198/credit) — https://www.findymail.com/pricing/.
|
||||
*/
|
||||
export const FINDYMAIL_CREDIT_USD = 0.02
|
||||
|
||||
/**
|
||||
* Build a Findymail `hosting` config. `getCredits` returns the number of
|
||||
* Findymail credits the call consumed, derived from the tool's output (per the
|
||||
* documented per-endpoint credit model at https://www.findymail.com/api/).
|
||||
*/
|
||||
export function findymailHosting<P>(
|
||||
getCredits: (params: P, output: Record<string, unknown>) => number
|
||||
): ToolHostingConfig<P> {
|
||||
return {
|
||||
envKeyPrefix: FINDYMAIL_API_KEY_PREFIX,
|
||||
apiKeyParam: 'apiKey',
|
||||
byokProviderId: 'findymail',
|
||||
pricing: {
|
||||
type: 'custom',
|
||||
getCost: (params, output) => {
|
||||
const credits = getCredits(params, output)
|
||||
return { cost: credits * FINDYMAIL_CREDIT_USD, metadata: { credits } }
|
||||
},
|
||||
},
|
||||
rateLimit: {
|
||||
mode: 'per_request',
|
||||
requestsPerMinute: 60,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export * from './types'
|
||||
|
||||
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,105 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailLookupTechnologiesParams>((_params, output) => {
|
||||
// 1 finder credit when a technology stack is returned, free otherwise.
|
||||
return Array.isArray(output.technologies) && output.technologies.length > 0 ? 1 : 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,158 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailReverseEmailLookupParams>((params, output) => {
|
||||
const found = Boolean(output.email || output.linkedin_url || output.fullName)
|
||||
if (!found) return 0
|
||||
// 1 credit for a match, 2 when full profile enrichment is requested.
|
||||
return params.with_profile ? 2 : 1
|
||||
}),
|
||||
|
||||
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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,87 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailSearchTechnologiesParams>(() => {
|
||||
// Free catalog search — consumes no Findymail credits.
|
||||
return 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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,78 @@
|
||||
import { findymailHosting } from '@/tools/findymail/hosting'
|
||||
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',
|
||||
|
||||
hosting: findymailHosting<FindymailVerifyEmailParams>(() => {
|
||||
// Each verification consumes one verifier credit, billed at the finder-credit rate.
|
||||
return 1
|
||||
}),
|
||||
|
||||
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 ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`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,
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user