chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,54 @@
import type {
InstantlyActivateCampaignParams,
InstantlyCampaignActionResponse,
} from '@/tools/instantly/types'
import {
campaignActionOutputs,
getMessage,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const activateCampaignTool: ToolConfig<
InstantlyActivateCampaignParams,
InstantlyCampaignActionResponse
> = {
id: 'instantly_activate_campaign',
name: 'Instantly Activate Campaign',
description: 'Activates, starts, or resumes an Instantly V2 campaign.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaignId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Campaign ID',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/campaigns/${params.campaignId.trim()}/activate`),
method: 'POST',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaign = mapCampaign(data)
return {
success: true,
output: {
campaign,
id: campaign.id,
name: campaign.name,
status: campaign.status,
message: getMessage(data),
},
}
},
outputs: campaignActionOutputs,
}
+136
View File
@@ -0,0 +1,136 @@
import type {
InstantlyCampaignResponse,
InstantlyCreateCampaignParams,
} from '@/tools/instantly/types'
import {
campaignOutputs,
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const createCampaignTool: ToolConfig<
InstantlyCreateCampaignParams,
InstantlyCampaignResponse
> = {
id: 'instantly_create_campaign',
name: 'Instantly Create Campaign',
description: 'Creates an Instantly V2 campaign using the documented campaign schedule schema.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Campaign name',
},
campaign_schedule: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description: 'Campaign schedule object with schedules array',
},
sequences: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Campaign sequence definitions',
items: { type: 'object', description: 'Sequence object' },
},
email_list: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Sending email accounts',
items: { type: 'string', description: 'Email address' },
},
daily_limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Daily sending limit',
},
daily_max_leads: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Daily maximum new leads to contact',
},
open_tracking: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to track opens',
},
stop_on_reply: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to stop the campaign on reply',
},
link_tracking: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to track links',
},
text_only: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the campaign is text only',
},
email_gap: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Gap between emails in minutes',
},
pl_value: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Value of every positive lead',
},
},
request: {
url: () => instantlyUrl('/api/v2/campaigns'),
method: 'POST',
headers: instantlyHeaders,
body: (params) =>
compactBody({
name: params.name,
campaign_schedule: params.campaign_schedule,
sequences: params.sequences,
pl_value: params.pl_value,
email_gap: params.email_gap,
text_only: params.text_only,
email_list: params.email_list,
daily_limit: params.daily_limit,
stop_on_reply: params.stop_on_reply,
link_tracking: params.link_tracking,
open_tracking: params.open_tracking,
daily_max_leads: params.daily_max_leads,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaign = mapCampaign(data)
return {
success: true,
output: {
campaign,
id: campaign.id,
name: campaign.name,
status: campaign.status,
},
}
},
outputs: campaignOutputs,
}
+187
View File
@@ -0,0 +1,187 @@
import type { InstantlyCreateLeadParams, InstantlyLeadResponse } from '@/tools/instantly/types'
import {
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadOutputs,
mapLead,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const createLeadTool: ToolConfig<InstantlyCreateLeadParams, InstantlyLeadResponse> = {
id: 'instantly_create_lead',
name: 'Instantly Create Lead',
description: 'Creates an Instantly V2 lead in a campaign or lead list.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaign: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign ID associated with the lead',
},
list_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead list ID associated with the lead',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead email address. Required when adding to a campaign.',
},
first_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead first name',
},
last_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead last name',
},
company_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead company name',
},
job_title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead job title',
},
phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead phone number',
},
website: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead website',
},
personalization: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead personalization text',
},
lt_interest_status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Lead interest status value',
},
pl_value_lead: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Potential value of the lead',
},
assigned_to: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Organization user ID assigned to the lead',
},
skip_if_in_workspace: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip if the lead already exists in the workspace',
},
skip_if_in_campaign: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip if the lead already exists in the campaign',
},
skip_if_in_list: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Skip if the lead already exists in the list',
},
blocklist_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Blocklist ID to check for the lead',
},
verify_leads_for_lead_finder: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to verify leads imported from Lead Finder',
},
verify_leads_on_import: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to verify leads on import',
},
custom_variables: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Custom variable object with string, number, boolean, or null values',
},
},
request: {
url: () => instantlyUrl('/api/v2/leads'),
method: 'POST',
headers: instantlyHeaders,
body: (params) =>
compactBody({
campaign: params.campaign,
list_id: params.list_id,
email: params.email,
first_name: params.first_name,
last_name: params.last_name,
company_name: params.company_name,
job_title: params.job_title,
phone: params.phone,
website: params.website,
personalization: params.personalization,
lt_interest_status: params.lt_interest_status,
pl_value_lead: params.pl_value_lead,
assigned_to: params.assigned_to,
skip_if_in_workspace: params.skip_if_in_workspace,
skip_if_in_campaign: params.skip_if_in_campaign,
skip_if_in_list: params.skip_if_in_list,
blocklist_id: params.blocklist_id,
verify_leads_for_lead_finder: params.verify_leads_for_lead_finder,
verify_leads_on_import: params.verify_leads_on_import,
custom_variables: params.custom_variables,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const lead = mapLead(data)
return {
success: true,
output: {
lead,
id: lead.id,
email_address: lead.email,
first_name: lead.first_name,
last_name: lead.last_name,
campaign: lead.campaign,
status: lead.status,
},
}
},
outputs: leadOutputs,
}
@@ -0,0 +1,70 @@
import type {
InstantlyCreateLeadListParams,
InstantlyLeadListResponse,
} from '@/tools/instantly/types'
import {
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadListOutputs,
mapLeadList,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const createLeadListTool: ToolConfig<
InstantlyCreateLeadListParams,
InstantlyLeadListResponse
> = {
id: 'instantly_create_lead_list',
name: 'Instantly Create Lead List',
description: 'Creates an Instantly V2 lead list.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Lead list name',
},
has_enrichment_task: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether this list runs enrichment for every added lead',
},
owned_by: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'User ID of the lead list owner',
},
},
request: {
url: () => instantlyUrl('/api/v2/lead-lists'),
method: 'POST',
headers: instantlyHeaders,
body: (params) =>
compactBody({
name: params.name,
has_enrichment_task: params.has_enrichment_task,
owned_by: params.owned_by,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const leadList = mapLeadList(data)
return {
success: true,
output: {
lead_list: leadList,
id: leadList.id,
name: leadList.name,
},
}
},
outputs: leadListOutputs,
}
@@ -0,0 +1,54 @@
import type {
InstantlyCampaignActionResponse,
InstantlyDeleteCampaignParams,
} from '@/tools/instantly/types'
import {
campaignActionOutputs,
getMessage,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const deleteCampaignTool: ToolConfig<
InstantlyDeleteCampaignParams,
InstantlyCampaignActionResponse
> = {
id: 'instantly_delete_campaign',
name: 'Instantly Delete Campaign',
description: 'Permanently deletes an Instantly V2 campaign.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaignId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Campaign ID',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/campaigns/${params.campaignId.trim()}`),
method: 'DELETE',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaign = mapCampaign(data)
return {
success: true,
output: {
campaign,
id: campaign.id,
name: campaign.name,
status: campaign.status,
message: getMessage(data),
},
}
},
outputs: campaignActionOutputs,
}
+82
View File
@@ -0,0 +1,82 @@
import type {
InstantlyDeleteLeadsParams,
InstantlyDeleteLeadsResponse,
} from '@/tools/instantly/types'
import {
asRecord,
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const deleteLeadsTool: ToolConfig<InstantlyDeleteLeadsParams, InstantlyDeleteLeadsResponse> =
{
id: 'instantly_delete_leads',
name: 'Instantly Delete Leads',
description: 'Deletes Instantly V2 leads in bulk from a campaign or lead list.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaign_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign ID to delete leads from. Required if list_id is not provided.',
},
list_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead list ID to delete leads from. Required if campaign_id is not provided.',
},
status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Optional lead status filter',
},
ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Specific lead IDs to delete',
items: { type: 'string', description: 'Lead ID' },
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of matching leads to delete, up to 10000',
},
},
request: {
url: () => instantlyUrl('/api/v2/leads'),
method: 'DELETE',
headers: instantlyHeaders,
body: (params) =>
compactBody({
campaign_id: params.campaign_id,
list_id: params.list_id,
status: params.status,
ids: params.ids,
limit: params.limit,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const result = asRecord(data)
return {
success: true,
output: {
count: typeof result.count === 'number' ? result.count : null,
},
}
},
outputs: {
count: { type: 'number', description: 'Number of leads deleted', optional: true },
},
}
+49
View File
@@ -0,0 +1,49 @@
import type { InstantlyGetLeadParams, InstantlyLeadResponse } from '@/tools/instantly/types'
import {
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadOutputs,
mapLead,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const getLeadTool: ToolConfig<InstantlyGetLeadParams, InstantlyLeadResponse> = {
id: 'instantly_get_lead',
name: 'Instantly Get Lead',
description: 'Retrieves an Instantly V2 lead by ID.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
leadId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Lead ID',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/leads/${params.leadId.trim()}`),
method: 'GET',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const lead = mapLead(data)
return {
success: true,
output: {
lead,
id: lead.id,
email_address: lead.email,
first_name: lead.first_name,
last_name: lead.last_name,
campaign: lead.campaign,
status: lead.status,
},
}
},
outputs: leadOutputs,
}
+17
View File
@@ -0,0 +1,17 @@
export { activateCampaignTool as instantlyActivateCampaignTool } from '@/tools/instantly/activate_campaign'
export { createCampaignTool as instantlyCreateCampaignTool } from '@/tools/instantly/create_campaign'
export { createLeadTool as instantlyCreateLeadTool } from '@/tools/instantly/create_lead'
export { createLeadListTool as instantlyCreateLeadListTool } from '@/tools/instantly/create_lead_list'
export { deleteCampaignTool as instantlyDeleteCampaignTool } from '@/tools/instantly/delete_campaign'
export { deleteLeadsTool as instantlyDeleteLeadsTool } from '@/tools/instantly/delete_leads'
export { getLeadTool as instantlyGetLeadTool } from '@/tools/instantly/get_lead'
export { listCampaignsTool as instantlyListCampaignsTool } from '@/tools/instantly/list_campaigns'
export { listEmailsTool as instantlyListEmailsTool } from '@/tools/instantly/list_emails'
export { listLeadListsTool as instantlyListLeadListsTool } from '@/tools/instantly/list_lead_lists'
export { listLeadsTool as instantlyListLeadsTool } from '@/tools/instantly/list_leads'
export { patchCampaignTool as instantlyPatchCampaignTool } from '@/tools/instantly/patch_campaign'
export { patchLeadTool as instantlyPatchLeadTool } from '@/tools/instantly/patch_lead'
export { pauseCampaignTool as instantlyPauseCampaignTool } from '@/tools/instantly/pause_campaign'
export { replyToEmailTool as instantlyReplyToEmailTool } from '@/tools/instantly/reply_to_email'
export * from '@/tools/instantly/types'
export { updateLeadInterestStatusTool as instantlyUpdateLeadInterestStatusTool } from '@/tools/instantly/update_lead_interest_status'
@@ -0,0 +1,91 @@
import type {
InstantlyListCampaignsParams,
InstantlyListCampaignsResponse,
} from '@/tools/instantly/types'
import {
campaignsListOutputs,
getItems,
getNextStartingAfter,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const listCampaignsTool: ToolConfig<
InstantlyListCampaignsParams,
InstantlyListCampaignsResponse
> = {
id: 'instantly_list_campaigns',
name: 'Instantly List Campaigns',
description: 'Retrieves Instantly V2 campaigns with search, status, tag, and pagination filters.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of campaigns to return, from 1 to 100',
},
starting_after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from next_starting_after',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search by campaign name',
},
tag_ids: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated campaign tag IDs',
},
ai_sales_agent_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter campaigns by AI Sales Agent ID',
},
status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Campaign status enum value',
},
},
request: {
url: (params) =>
instantlyUrl('/api/v2/campaigns', {
limit: params.limit,
starting_after: params.starting_after,
search: params.search,
tag_ids: params.tag_ids,
ai_sales_agent_id: params.ai_sales_agent_id,
status: params.status,
}),
method: 'GET',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaigns = getItems(data).map(mapCampaign)
return {
success: true,
output: {
campaigns,
count: campaigns.length,
next_starting_after: getNextStartingAfter(data),
},
}
},
outputs: campaignsListOutputs,
}
+109
View File
@@ -0,0 +1,109 @@
import type {
InstantlyListEmailsParams,
InstantlyListEmailsResponse,
} from '@/tools/instantly/types'
import {
emailsListOutputs,
getItems,
getNextStartingAfter,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapEmail,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const listEmailsTool: ToolConfig<InstantlyListEmailsParams, InstantlyListEmailsResponse> = {
id: 'instantly_list_emails',
name: 'Instantly List Emails',
description: 'Retrieves Instantly V2 Unibox emails with search and pagination filters.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of emails to return, from 1 to 100',
},
starting_after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor from next_starting_after',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search query, email address, or thread:<thread-id>',
},
campaign_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign ID filter',
},
list_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead list ID filter',
},
i_status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Email interest status filter',
},
eaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sending email account filter',
},
lead: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead email address filter',
},
is_unread: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Unread status filter',
},
},
request: {
url: (params) =>
instantlyUrl('/api/v2/emails', {
limit: params.limit,
starting_after: params.starting_after,
search: params.search,
campaign_id: params.campaign_id,
list_id: params.list_id,
i_status: params.i_status,
eaccount: params.eaccount,
lead: params.lead,
is_unread: params.is_unread,
}),
method: 'GET',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const emails = getItems(data).map(mapEmail)
return {
success: true,
output: {
emails,
count: emails.length,
next_starting_after: getNextStartingAfter(data),
},
}
},
outputs: emailsListOutputs,
}
@@ -0,0 +1,77 @@
import type {
InstantlyListLeadListsParams,
InstantlyListLeadListsResponse,
} from '@/tools/instantly/types'
import {
getItems,
getNextStartingAfter,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadListsListOutputs,
mapLeadList,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const listLeadListsTool: ToolConfig<
InstantlyListLeadListsParams,
InstantlyListLeadListsResponse
> = {
id: 'instantly_list_lead_lists',
name: 'Instantly List Lead Lists',
description: 'Retrieves Instantly V2 lead lists with search and pagination filters.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of lead lists to return, from 1 to 100',
},
starting_after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Starting-after timestamp cursor',
},
has_enrichment_task: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Filter by enrichment task setting',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search query to filter lead lists by name',
},
},
request: {
url: (params) =>
instantlyUrl('/api/v2/lead-lists', {
limit: params.limit,
starting_after: params.starting_after,
has_enrichment_task: params.has_enrichment_task,
search: params.search,
}),
method: 'GET',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const leadLists = getItems(data).map(mapLeadList)
return {
success: true,
output: {
lead_lists: leadLists,
count: leadLists.length,
next_starting_after: getNextStartingAfter(data),
},
}
},
outputs: leadListsListOutputs,
}
+168
View File
@@ -0,0 +1,168 @@
import type { InstantlyListLeadsParams, InstantlyListLeadsResponse } from '@/tools/instantly/types'
import {
compactBody,
getItems,
getNextStartingAfter,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadsListOutputs,
mapLead,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const listLeadsTool: ToolConfig<InstantlyListLeadsParams, InstantlyListLeadsResponse> = {
id: 'instantly_list_leads',
name: 'Instantly List Leads',
description: 'Retrieves Instantly V2 leads with search, campaign, list, and pagination filters.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search by first name, last name, or email',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Instantly lead filter value, such as FILTER_VAL_CONTACTED or FILTER_VAL_ACTIVE',
},
campaign: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign ID to filter leads',
},
list_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead list ID to filter leads',
},
in_campaign: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the lead is in a campaign',
},
in_list: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the lead is in a list',
},
ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Lead IDs to include',
items: { type: 'string', description: 'Lead ID' },
},
excluded_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Lead IDs to exclude',
items: { type: 'string', description: 'Lead ID' },
},
organization_user_ids: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Organization user IDs to filter leads',
items: { type: 'string', description: 'Organization user ID' },
},
smart_view_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Smart view ID to filter leads',
},
contacts: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Lead email addresses to include',
items: { type: 'string', description: 'Email address' },
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of leads to return, from 1 to 100',
},
starting_after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Forward pagination cursor from next_starting_after',
},
distinct_contacts: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to return distinct contacts',
},
is_website_visitor: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the lead is a website visitor',
},
enrichment_status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Enrichment status filter',
},
esg_code: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Email security gateway code filter',
},
},
request: {
url: () => instantlyUrl('/api/v2/leads/list'),
method: 'POST',
headers: instantlyHeaders,
body: (params) =>
compactBody({
search: params.search,
filter: params.filter,
campaign: params.campaign,
list_id: params.list_id,
in_campaign: params.in_campaign,
in_list: params.in_list,
ids: params.ids,
excluded_ids: params.excluded_ids,
contacts: params.contacts,
limit: params.limit,
starting_after: params.starting_after,
organization_user_ids: params.organization_user_ids,
smart_view_id: params.smart_view_id,
is_website_visitor: params.is_website_visitor,
distinct_contacts: params.distinct_contacts,
enrichment_status: params.enrichment_status,
esg_code: params.esg_code,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const leads = getItems(data).map(mapLead)
return {
success: true,
output: {
leads,
count: leads.length,
next_starting_after: getNextStartingAfter(data),
},
}
},
outputs: leadsListOutputs,
}
+142
View File
@@ -0,0 +1,142 @@
import type {
InstantlyCampaignResponse,
InstantlyPatchCampaignParams,
} from '@/tools/instantly/types'
import {
campaignOutputs,
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const patchCampaignTool: ToolConfig<
InstantlyPatchCampaignParams,
InstantlyCampaignResponse
> = {
id: 'instantly_patch_campaign',
name: 'Instantly Patch Campaign',
description: 'Updates documented Instantly V2 campaign fields.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaignId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Campaign ID',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign name',
},
campaign_schedule: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Campaign schedule object with schedules array',
},
sequences: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Campaign sequence definitions',
items: { type: 'object', description: 'Sequence object' },
},
email_list: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Sending email accounts',
items: { type: 'string', description: 'Email address' },
},
daily_limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Daily sending limit',
},
daily_max_leads: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Daily maximum new leads to contact',
},
open_tracking: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to track opens',
},
stop_on_reply: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to stop the campaign on reply',
},
link_tracking: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to track links',
},
text_only: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the campaign is text only',
},
email_gap: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Gap between emails in minutes',
},
pl_value: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Value of every positive lead',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/campaigns/${params.campaignId.trim()}`),
method: 'PATCH',
headers: instantlyHeaders,
body: (params) =>
compactBody({
name: params.name,
campaign_schedule: params.campaign_schedule,
sequences: params.sequences,
pl_value: params.pl_value,
email_gap: params.email_gap,
text_only: params.text_only,
email_list: params.email_list,
daily_limit: params.daily_limit,
stop_on_reply: params.stop_on_reply,
link_tracking: params.link_tracking,
open_tracking: params.open_tracking,
daily_max_leads: params.daily_max_leads,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaign = mapCampaign(data)
return {
success: true,
output: {
campaign,
id: campaign.id,
name: campaign.name,
status: campaign.status,
},
}
},
outputs: campaignOutputs,
}
+137
View File
@@ -0,0 +1,137 @@
import type { InstantlyLeadResponse, InstantlyPatchLeadParams } from '@/tools/instantly/types'
import {
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
leadOutputs,
mapLead,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const patchLeadTool: ToolConfig<InstantlyPatchLeadParams, InstantlyLeadResponse> = {
id: 'instantly_patch_lead',
name: 'Instantly Patch Lead',
description: 'Updates fields on an existing Instantly V2 lead.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
leadId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Lead ID',
},
first_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead first name',
},
last_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead last name',
},
company_name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead company name',
},
job_title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead job title',
},
phone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead phone number',
},
website: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead website',
},
personalization: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead personalization text',
},
lt_interest_status: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Lead interest status value',
},
pl_value_lead: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Potential value of the lead',
},
assigned_to: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the user assigned to the lead',
},
custom_variables: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Custom variable object with string, number, boolean, or null values',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/leads/${params.leadId.trim()}`),
method: 'PATCH',
headers: instantlyHeaders,
body: (params) => {
const body = compactBody({
first_name: params.first_name,
last_name: params.last_name,
company_name: params.company_name,
job_title: params.job_title,
phone: params.phone,
website: params.website,
personalization: params.personalization,
lt_interest_status: params.lt_interest_status,
pl_value_lead: params.pl_value_lead,
assigned_to: params.assigned_to,
custom_variables: params.custom_variables,
})
if (Object.keys(body).length === 0) {
throw new Error('Provide at least one field to update')
}
return body
},
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const lead = mapLead(data)
return {
success: true,
output: {
lead,
id: lead.id,
email_address: lead.email,
first_name: lead.first_name,
last_name: lead.last_name,
campaign: lead.campaign,
status: lead.status,
},
}
},
outputs: leadOutputs,
}
@@ -0,0 +1,54 @@
import type {
InstantlyCampaignActionResponse,
InstantlyPauseCampaignParams,
} from '@/tools/instantly/types'
import {
campaignActionOutputs,
getMessage,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapCampaign,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const pauseCampaignTool: ToolConfig<
InstantlyPauseCampaignParams,
InstantlyCampaignActionResponse
> = {
id: 'instantly_pause_campaign',
name: 'Instantly Pause Campaign',
description: 'Pauses a running Instantly V2 campaign, stopping further email sends.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
campaignId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Campaign ID',
},
},
request: {
url: (params) => instantlyUrl(`/api/v2/campaigns/${params.campaignId.trim()}/pause`),
method: 'POST',
headers: instantlyHeaders,
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const campaign = mapCampaign(data)
return {
success: true,
output: {
campaign,
id: campaign.id,
name: campaign.name,
status: campaign.status,
message: getMessage(data),
},
}
},
outputs: campaignActionOutputs,
}
@@ -0,0 +1,86 @@
import type { InstantlyEmailResponse, InstantlyReplyToEmailParams } from '@/tools/instantly/types'
import {
compactBody,
emailOutputs,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
mapEmail,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const replyToEmailTool: ToolConfig<InstantlyReplyToEmailParams, InstantlyEmailResponse> = {
id: 'instantly_reply_to_email',
name: 'Instantly Reply To Email',
description: 'Sends an Instantly V2 reply to an existing Unibox email.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
eaccount: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Connected email account used to send the reply',
},
reply_to_uuid: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Email ID to reply to',
},
subject: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Reply subject',
},
body: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description: 'Reply body object with text and/or html',
},
cc_address_email_list: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated CC email addresses',
},
bcc_address_email_list: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated BCC email addresses',
},
},
request: {
url: () => instantlyUrl('/api/v2/emails/reply'),
method: 'POST',
headers: instantlyHeaders,
body: (params) =>
compactBody({
eaccount: params.eaccount,
reply_to_uuid: params.reply_to_uuid,
subject: params.subject,
body: params.body,
cc_address_email_list: params.cc_address_email_list,
bcc_address_email_list: params.bcc_address_email_list,
}),
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const email = mapEmail(data)
return {
success: true,
output: {
email,
id: email.id,
subject: email.subject,
thread_id: email.thread_id,
},
}
},
outputs: emailOutputs,
}
+348
View File
@@ -0,0 +1,348 @@
import type { ToolResponse } from '@/tools/types'
export type InstantlyScalar = string | number | boolean | null
export interface InstantlyBaseParams {
apiKey: string
}
export interface InstantlyLead {
id: string | null
timestamp_created: string | null
timestamp_updated: string | null
organization: string | null
campaign: string | null
status: number | null
email: string | null
personalization: string | null
website: string | null
last_name: string | null
first_name: string | null
company_name: string | null
job_title: string | null
phone: string | null
email_open_count: number | null
email_reply_count: number | null
email_click_count: number | null
company_domain: string | null
payload: Record<string, unknown> | null
lt_interest_status: number | null
}
export interface InstantlyCampaign {
id: string | null
name: string | null
pl_value: number | null
status: number | null
is_evergreen: boolean | null
timestamp_created: string | null
timestamp_updated: string | null
email_gap: number | null
daily_limit: number | null
daily_max_leads: number | null
open_tracking: boolean | null
stop_on_reply: boolean | null
sequences: unknown[]
campaign_schedule: Record<string, unknown> | null
}
export interface InstantlyEmail {
id: string | null
timestamp_created: string | null
timestamp_email: string | null
message_id: string | null
subject: string | null
from_address_email: string | null
to_address_email_list: string | null
cc_address_email_list: string | null
bcc_address_email_list: string | null
reply_to: string | null
body: {
text: string | null
html: string | null
}
organization_id: string | null
campaign_id: string | null
subsequence_id: string | null
list_id: string | null
lead: string | null
lead_id: string | null
eaccount: string | null
ue_type: number | null
is_unread: number | null
is_auto_reply: number | null
i_status: number | null
thread_id: string | null
content_preview: string | null
}
export interface InstantlyLeadList {
id: string | null
organization_id: string | null
has_enrichment_task: boolean | null
owned_by: string | null
name: string | null
timestamp_created: string | null
}
export interface InstantlyListLeadsParams extends InstantlyBaseParams {
search?: string
filter?: string
campaign?: string
list_id?: string
in_campaign?: boolean
in_list?: boolean
ids?: string[]
excluded_ids?: string[]
contacts?: string[]
limit?: number
starting_after?: string
organization_user_ids?: string[]
smart_view_id?: string
is_website_visitor?: boolean
distinct_contacts?: boolean
enrichment_status?: number
esg_code?: string
}
export interface InstantlyGetLeadParams extends InstantlyBaseParams {
leadId: string
}
export interface InstantlyPatchLeadParams extends InstantlyBaseParams {
leadId: string
personalization?: string | null
website?: string | null
last_name?: string | null
first_name?: string | null
company_name?: string | null
job_title?: string | null
phone?: string | null
lt_interest_status?: number
pl_value_lead?: string | null
assigned_to?: string | null
custom_variables?: Record<string, InstantlyScalar>
}
export interface InstantlyCreateLeadParams extends InstantlyBaseParams {
campaign?: string | null
email?: string | null
personalization?: string | null
website?: string | null
last_name?: string | null
first_name?: string | null
company_name?: string | null
job_title?: string | null
phone?: string | null
lt_interest_status?: number
pl_value_lead?: string | null
list_id?: string | null
assigned_to?: string | null
skip_if_in_workspace?: boolean
skip_if_in_campaign?: boolean
skip_if_in_list?: boolean
blocklist_id?: string
verify_leads_for_lead_finder?: boolean
verify_leads_on_import?: boolean
custom_variables?: Record<string, InstantlyScalar>
}
export interface InstantlyDeleteLeadsParams extends InstantlyBaseParams {
campaign_id?: string
list_id?: string
status?: number
ids?: string[]
limit?: number
}
export interface InstantlyUpdateLeadInterestStatusParams extends InstantlyBaseParams {
lead_email: string
interest_value?: number | null
campaign_id?: string
ai_interest_value?: number
disable_auto_interest?: boolean
list_id?: string
}
export interface InstantlyListCampaignsParams extends InstantlyBaseParams {
limit?: number
starting_after?: string
search?: string
tag_ids?: string
ai_sales_agent_id?: string
status?: number
}
export interface InstantlyCreateCampaignParams extends InstantlyBaseParams {
name: string
campaign_schedule: Record<string, unknown>
sequences?: unknown[]
pl_value?: number | null
email_gap?: number | null
text_only?: boolean | null
email_list?: string[]
daily_limit?: number | null
stop_on_reply?: boolean | null
link_tracking?: boolean | null
open_tracking?: boolean
daily_max_leads?: number | null
}
export interface InstantlyPatchCampaignParams extends Partial<InstantlyCreateCampaignParams> {
apiKey: string
campaignId: string
}
export interface InstantlyActivateCampaignParams extends InstantlyBaseParams {
campaignId: string
}
export interface InstantlyPauseCampaignParams extends InstantlyBaseParams {
campaignId: string
}
export interface InstantlyDeleteCampaignParams extends InstantlyBaseParams {
campaignId: string
}
export interface InstantlyListEmailsParams extends InstantlyBaseParams {
limit?: number
starting_after?: string
search?: string
campaign_id?: string
list_id?: string
i_status?: number
eaccount?: string
lead?: string
is_unread?: boolean
}
export interface InstantlyReplyToEmailParams extends InstantlyBaseParams {
eaccount: string
reply_to_uuid: string
subject: string
body: {
text?: string
html?: string
}
cc_address_email_list?: string
bcc_address_email_list?: string
}
export interface InstantlyListLeadListsParams extends InstantlyBaseParams {
limit?: number
starting_after?: string
has_enrichment_task?: boolean
search?: string
}
export interface InstantlyCreateLeadListParams extends InstantlyBaseParams {
name: string
has_enrichment_task?: boolean | null
owned_by?: string | null
}
export interface InstantlyListLeadsResponse extends ToolResponse {
output: {
leads: InstantlyLead[]
count: number
next_starting_after: string | null
}
}
export interface InstantlyLeadResponse extends ToolResponse {
output: {
lead: InstantlyLead
id: string | null
email_address: string | null
first_name: string | null
last_name: string | null
campaign: string | null
status: number | null
}
}
export interface InstantlyDeleteLeadsResponse extends ToolResponse {
output: {
count: number | null
}
}
export interface InstantlyUpdateLeadInterestStatusResponse extends ToolResponse {
output: {
message: string | null
}
}
export interface InstantlyListCampaignsResponse extends ToolResponse {
output: {
campaigns: InstantlyCampaign[]
count: number
next_starting_after: string | null
}
}
export interface InstantlyCampaignResponse extends ToolResponse {
output: {
campaign: InstantlyCampaign
id: string | null
name: string | null
status: number | null
}
}
export interface InstantlyCampaignActionResponse extends ToolResponse {
output: {
campaign: InstantlyCampaign
id: string | null
name: string | null
status: number | null
message: string | null
}
}
export interface InstantlyListEmailsResponse extends ToolResponse {
output: {
emails: InstantlyEmail[]
count: number
next_starting_after: string | null
}
}
export interface InstantlyEmailResponse extends ToolResponse {
output: {
email: InstantlyEmail
id: string | null
subject: string | null
thread_id: string | null
}
}
export interface InstantlyListLeadListsResponse extends ToolResponse {
output: {
lead_lists: InstantlyLeadList[]
count: number
next_starting_after: string | null
}
}
export interface InstantlyLeadListResponse extends ToolResponse {
output: {
lead_list: InstantlyLeadList
id: string | null
name: string | null
}
}
export type InstantlyResponse =
| InstantlyListLeadsResponse
| InstantlyLeadResponse
| InstantlyDeleteLeadsResponse
| InstantlyUpdateLeadInterestStatusResponse
| InstantlyListCampaignsResponse
| InstantlyCampaignResponse
| InstantlyCampaignActionResponse
| InstantlyListEmailsResponse
| InstantlyEmailResponse
| InstantlyListLeadListsResponse
| InstantlyLeadListResponse
@@ -0,0 +1,99 @@
import type {
InstantlyUpdateLeadInterestStatusParams,
InstantlyUpdateLeadInterestStatusResponse,
} from '@/tools/instantly/types'
import {
asRecord,
compactBody,
instantlyBaseParamFields,
instantlyHeaders,
instantlyUrl,
parseInstantlyResponse,
} from '@/tools/instantly/utils'
import type { ToolConfig } from '@/tools/types'
export const updateLeadInterestStatusTool: ToolConfig<
InstantlyUpdateLeadInterestStatusParams,
InstantlyUpdateLeadInterestStatusResponse
> = {
id: 'instantly_update_lead_interest_status',
name: 'Instantly Update Lead Interest Status',
description: 'Submits an Instantly V2 background job to update a lead interest status.',
version: '1.0.0',
params: {
...instantlyBaseParamFields,
lead_email: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Lead email address',
},
interest_value: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Interest status value. Leave empty in the block or pass null to reset to Lead.',
},
campaign_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Campaign ID for the lead',
},
list_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lead list ID for the lead',
},
ai_interest_value: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'AI interest value to set for the lead',
},
disable_auto_interest: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to disable auto interest',
},
},
request: {
url: () => instantlyUrl('/api/v2/leads/update-interest-status'),
method: 'POST',
headers: instantlyHeaders,
body: (params) => {
if (params.interest_value === undefined) {
throw new Error('Interest Value is required for Instantly Update Lead Interest Status')
}
return compactBody({
lead_email: params.lead_email,
interest_value: params.interest_value,
campaign_id: params.campaign_id,
list_id: params.list_id,
ai_interest_value: params.ai_interest_value,
disable_auto_interest: params.disable_auto_interest,
})
},
},
transformResponse: async (response) => {
const data = await parseInstantlyResponse(response)
const result = asRecord(data)
return {
success: true,
output: {
message: typeof result.message === 'string' ? result.message : null,
},
}
},
outputs: {
message: {
type: 'string',
description: 'Background job submission message',
optional: true,
},
},
}
+372
View File
@@ -0,0 +1,372 @@
import { filterUndefined, isRecordLike } from '@sim/utils/object'
import type {
InstantlyCampaign,
InstantlyEmail,
InstantlyLead,
InstantlyLeadList,
} from '@/tools/instantly/types'
import type { ToolConfig } from '@/tools/types'
const INSTANTLY_API_BASE_URL = 'https://api.instantly.ai'
type InstantlyBaseParams = { apiKey: string }
type JsonRecord = Record<string, unknown>
export const instantlyBaseParamFields = {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Instantly API key with the required V2 scopes',
},
} satisfies ToolConfig<InstantlyBaseParams>['params']
export const instantlyHeaders = (params: InstantlyBaseParams) => ({
Authorization: `Bearer ${params.apiKey.trim()}`,
'Content-Type': 'application/json',
})
export function instantlyUrl(path: string, query?: Record<string, unknown>): string {
const url = new URL(path, INSTANTLY_API_BASE_URL)
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value === undefined || value === null || value === '') continue
url.searchParams.append(key, String(value))
}
}
return url.toString()
}
export function compactBody(values: Record<string, unknown>): Record<string, unknown> {
return filterUndefined(values)
}
export async function parseInstantlyResponse(response: Response): Promise<unknown> {
const data = await parseJsonResponse(response)
if (!response.ok) {
throw new Error(
extractInstantlyError(data, `Instantly API request failed (${response.status})`)
)
}
return data
}
export function asRecord(value: unknown): JsonRecord {
return isRecordLike(value) ? value : {}
}
export function getItems(value: unknown): JsonRecord[] {
const data = asRecord(value)
return Array.isArray(data.items) ? data.items.map(asRecord) : []
}
export function getNextStartingAfter(value: unknown): string | null {
const data = asRecord(value)
return asString(data.next_starting_after)
}
export function mapLead(value: unknown): InstantlyLead {
const lead = asRecord(value)
return {
id: asString(lead.id),
timestamp_created: asString(lead.timestamp_created),
timestamp_updated: asString(lead.timestamp_updated),
organization: asString(lead.organization),
campaign: asString(lead.campaign),
status: asNumber(lead.status),
email: asString(lead.email),
personalization: asString(lead.personalization),
website: asString(lead.website),
last_name: asString(lead.last_name),
first_name: asString(lead.first_name),
company_name: asString(lead.company_name),
job_title: asString(lead.job_title),
phone: asString(lead.phone),
email_open_count: asNumber(lead.email_open_count),
email_reply_count: asNumber(lead.email_reply_count),
email_click_count: asNumber(lead.email_click_count),
company_domain: asString(lead.company_domain),
payload: isRecordLike(lead.payload) ? lead.payload : null,
lt_interest_status: asNumber(lead.lt_interest_status),
}
}
export function mapCampaign(value: unknown): InstantlyCampaign {
const campaign = asRecord(value)
return {
id: asString(campaign.id),
name: asString(campaign.name),
pl_value: asNumber(campaign.pl_value),
status: asNumber(campaign.status),
is_evergreen: asBoolean(campaign.is_evergreen),
timestamp_created: asString(campaign.timestamp_created),
timestamp_updated: asString(campaign.timestamp_updated),
email_gap: asNumber(campaign.email_gap),
daily_limit: asNumber(campaign.daily_limit),
daily_max_leads: asNumber(campaign.daily_max_leads),
open_tracking: asBoolean(campaign.open_tracking),
stop_on_reply: asBoolean(campaign.stop_on_reply),
sequences: Array.isArray(campaign.sequences) ? campaign.sequences : [],
campaign_schedule: isRecordLike(campaign.campaign_schedule) ? campaign.campaign_schedule : null,
}
}
export function mapEmail(value: unknown): InstantlyEmail {
const email = asRecord(value)
const body = asRecord(email.body)
return {
id: asString(email.id),
timestamp_created: asString(email.timestamp_created),
timestamp_email: asString(email.timestamp_email),
message_id: asString(email.message_id),
subject: asString(email.subject),
from_address_email: asString(email.from_address_email),
to_address_email_list: asString(email.to_address_email_list),
cc_address_email_list: asString(email.cc_address_email_list),
bcc_address_email_list: asString(email.bcc_address_email_list),
reply_to: asString(email.reply_to),
body: {
text: asString(body.text),
html: asString(body.html),
},
organization_id: asString(email.organization_id),
campaign_id: asString(email.campaign_id),
subsequence_id: asString(email.subsequence_id),
list_id: asString(email.list_id),
lead: asString(email.lead),
lead_id: asString(email.lead_id),
eaccount: asString(email.eaccount),
ue_type: asNumber(email.ue_type),
is_unread: asNumber(email.is_unread),
is_auto_reply: asNumber(email.is_auto_reply),
i_status: asNumber(email.i_status),
thread_id: asString(email.thread_id),
content_preview: asString(email.content_preview),
}
}
export function mapLeadList(value: unknown): InstantlyLeadList {
const leadList = asRecord(value)
return {
id: asString(leadList.id),
organization_id: asString(leadList.organization_id),
has_enrichment_task: asBoolean(leadList.has_enrichment_task),
owned_by: asString(leadList.owned_by),
name: asString(leadList.name),
timestamp_created: asString(leadList.timestamp_created),
}
}
export const leadOutputs = {
lead: {
type: 'object',
description: 'Lead details',
properties: {
id: { type: 'string', description: 'Lead ID', nullable: true },
email: { type: 'string', description: 'Lead email address', nullable: true },
first_name: { type: 'string', description: 'Lead first name', nullable: true },
last_name: { type: 'string', description: 'Lead last name', nullable: true },
company_name: { type: 'string', description: 'Lead company name', nullable: true },
job_title: { type: 'string', description: 'Lead job title', nullable: true },
campaign: { type: 'string', description: 'Campaign ID', nullable: true },
status: { type: 'number', description: 'Lead status', nullable: true },
payload: { type: 'json', description: 'Lead custom variables', nullable: true },
},
},
id: { type: 'string', description: 'Lead ID', optional: true },
email_address: { type: 'string', description: 'Lead email address', optional: true },
first_name: { type: 'string', description: 'Lead first name', optional: true },
last_name: { type: 'string', description: 'Lead last name', optional: true },
campaign: { type: 'string', description: 'Campaign ID', optional: true },
status: { type: 'number', description: 'Lead status', optional: true },
} satisfies ToolConfig['outputs']
export const leadsListOutputs = {
leads: {
type: 'array',
description: 'List of leads',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Lead ID', nullable: true },
email: { type: 'string', description: 'Lead email address', nullable: true },
first_name: { type: 'string', description: 'Lead first name', nullable: true },
last_name: { type: 'string', description: 'Lead last name', nullable: true },
company_name: { type: 'string', description: 'Lead company name', nullable: true },
campaign: { type: 'string', description: 'Campaign ID', nullable: true },
status: { type: 'number', description: 'Lead status', nullable: true },
},
},
},
count: { type: 'number', description: 'Number of leads returned' },
next_starting_after: { type: 'string', description: 'Cursor for the next page', optional: true },
} satisfies ToolConfig['outputs']
export const campaignOutputs = {
campaign: {
type: 'object',
description: 'Campaign details',
properties: {
id: { type: 'string', description: 'Campaign ID', nullable: true },
name: { type: 'string', description: 'Campaign name', nullable: true },
status: { type: 'number', description: 'Campaign status', nullable: true },
daily_limit: { type: 'number', description: 'Daily sending limit', nullable: true },
daily_max_leads: { type: 'number', description: 'Daily max new leads', nullable: true },
open_tracking: {
type: 'boolean',
description: 'Whether open tracking is enabled',
nullable: true,
},
},
},
id: { type: 'string', description: 'Campaign ID', optional: true },
name: { type: 'string', description: 'Campaign name', optional: true },
status: { type: 'number', description: 'Campaign status', optional: true },
} satisfies ToolConfig['outputs']
export const campaignActionOutputs = {
...campaignOutputs,
message: { type: 'string', description: 'Confirmation message from Instantly', optional: true },
} satisfies ToolConfig['outputs']
export const campaignsListOutputs = {
campaigns: {
type: 'array',
description: 'List of campaigns',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Campaign ID', nullable: true },
name: { type: 'string', description: 'Campaign name', nullable: true },
status: { type: 'number', description: 'Campaign status', nullable: true },
daily_limit: { type: 'number', description: 'Daily sending limit', nullable: true },
},
},
},
count: { type: 'number', description: 'Number of campaigns returned' },
next_starting_after: { type: 'string', description: 'Cursor for the next page', optional: true },
} satisfies ToolConfig['outputs']
export const emailOutputs = {
email: {
type: 'object',
description: 'Email details',
properties: {
id: { type: 'string', description: 'Email ID', nullable: true },
subject: { type: 'string', description: 'Email subject', nullable: true },
from_address_email: { type: 'string', description: 'Sender email', nullable: true },
to_address_email_list: {
type: 'string',
description: 'Recipient email list',
nullable: true,
},
thread_id: { type: 'string', description: 'Thread ID', nullable: true },
content_preview: { type: 'string', description: 'Email content preview', nullable: true },
},
},
id: { type: 'string', description: 'Email ID', optional: true },
subject: { type: 'string', description: 'Email subject', optional: true },
thread_id: { type: 'string', description: 'Thread ID', optional: true },
} satisfies ToolConfig['outputs']
export const emailsListOutputs = {
emails: {
type: 'array',
description: 'List of emails',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Email ID', nullable: true },
subject: { type: 'string', description: 'Email subject', nullable: true },
from_address_email: { type: 'string', description: 'Sender email', nullable: true },
lead: { type: 'string', description: 'Lead email', nullable: true },
thread_id: { type: 'string', description: 'Thread ID', nullable: true },
},
},
},
count: { type: 'number', description: 'Number of emails returned' },
next_starting_after: { type: 'string', description: 'Cursor for the next page', optional: true },
} satisfies ToolConfig['outputs']
export const leadListOutputs = {
lead_list: {
type: 'object',
description: 'Lead list details',
properties: {
id: { type: 'string', description: 'Lead list ID', nullable: true },
organization_id: { type: 'string', description: 'Organization ID', nullable: true },
has_enrichment_task: {
type: 'boolean',
description: 'Whether enrichment is enabled',
nullable: true,
},
owned_by: { type: 'string', description: 'Owner user ID', nullable: true },
name: { type: 'string', description: 'Lead list name', nullable: true },
timestamp_created: { type: 'string', description: 'Creation timestamp', nullable: true },
},
},
id: { type: 'string', description: 'Lead list ID', optional: true },
name: { type: 'string', description: 'Lead list name', optional: true },
} satisfies ToolConfig['outputs']
export const leadListsListOutputs = {
lead_lists: {
type: 'array',
description: 'List of lead lists',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Lead list ID', nullable: true },
name: { type: 'string', description: 'Lead list name', nullable: true },
has_enrichment_task: {
type: 'boolean',
description: 'Whether enrichment is enabled',
nullable: true,
},
timestamp_created: { type: 'string', description: 'Creation timestamp', nullable: true },
},
},
},
count: { type: 'number', description: 'Number of lead lists returned' },
next_starting_after: { type: 'string', description: 'Cursor for the next page', optional: true },
} satisfies ToolConfig['outputs']
async function parseJsonResponse(response: Response): Promise<unknown> {
try {
return await response.json()
} catch {
return null
}
}
export function getMessage(value: unknown): string | null {
const data = asRecord(value)
return typeof data.message === 'string' ? data.message : null
}
function extractInstantlyError(value: unknown, fallback: string): string {
const data = asRecord(value)
if (typeof data.message === 'string') return data.message
if (typeof data.error === 'string') return data.error
return fallback
}
function asString(value: unknown): string | null {
return typeof value === 'string' ? value : null
}
function asNumber(value: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? value : null
}
function asBoolean(value: unknown): boolean | null {
return typeof value === 'boolean' ? value : null
}