mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(instantly): block, trigger (#4763)
* feat(instantly): block, trigger * change bg color for icon * address comments * cleanup code * search param missplaced
This commit is contained in:
@@ -415,6 +415,18 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function InstantlyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 766.8 766.8' xmlns='http://www.w3.org/2000/svg'>
|
||||
<circle cx='383.4' cy='383.4' r='383.4' fill='#0081ff' />
|
||||
<path
|
||||
d='M276.12 438.81h-101.8c-3.58 0-5.83-3.87-4.05-6.98l163.07-284.97h238.63c3.87 0 6.06 4.44 3.69 7.51L459.07 305.59c-2.36 3.07-.18 7.51 3.69 7.51h124.51c4.2 0 6.26 5.11 3.23 8.02L235.8 662.51c-3.37 3.24-8.88.06-7.76-4.48l52.61-213.45c.72-2.93-1.5-5.77-4.53-5.77z'
|
||||
fill='#fff'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function EmailBisonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='-66 -2 88 64' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
|
||||
@@ -99,6 +99,7 @@ import {
|
||||
ImageIcon,
|
||||
IncidentioIcon,
|
||||
InfisicalIcon,
|
||||
InstantlyIcon,
|
||||
IntercomIcon,
|
||||
JinaAIIcon,
|
||||
JiraIcon,
|
||||
@@ -319,6 +320,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
imap: MailServerIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
infisical: InfisicalIcon,
|
||||
instantly: InstantlyIcon,
|
||||
intercom: IntercomIcon,
|
||||
intercom_v2: IntercomIcon,
|
||||
jina: JinaAIIcon,
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
---
|
||||
title: Instantly
|
||||
description: Manage Instantly leads, campaigns, emails, and lead lists
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="instantly"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Instantly API V2 into workflows. Create and list leads, manage lead interest status, delete leads in bulk, list and create campaigns, reply to emails, and manage lead lists.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `instantly_list_leads`
|
||||
|
||||
Retrieves Instantly V2 leads with search, campaign, list, and pagination filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `search` | string | No | Search by first name, last name, or email |
|
||||
| `filter` | string | No | Instantly lead filter value, such as FILTER_VAL_CONTACTED or FILTER_VAL_ACTIVE |
|
||||
| `campaign` | string | No | Campaign ID to filter leads |
|
||||
| `list_id` | string | No | Lead list ID to filter leads |
|
||||
| `in_campaign` | boolean | No | Whether the lead is in a campaign |
|
||||
| `in_list` | boolean | No | Whether the lead is in a list |
|
||||
| `ids` | array | No | Lead IDs to include |
|
||||
| `items` | string | No | No description |
|
||||
| `excluded_ids` | array | No | Lead IDs to exclude |
|
||||
| `items` | string | No | No description |
|
||||
| `contacts` | array | No | Lead email addresses to include |
|
||||
| `items` | string | No | No description |
|
||||
| `limit` | number | No | Number of leads to return, from 1 to 100 |
|
||||
| `starting_after` | string | No | Forward pagination cursor from next_starting_after |
|
||||
| `distinct_contacts` | boolean | No | Whether to return distinct contacts |
|
||||
| `enrichment_status` | number | No | Enrichment status filter |
|
||||
| `esg_code` | string | No | Email security gateway code filter |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_get_lead`
|
||||
|
||||
Retrieves an Instantly V2 lead by ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `leadId` | string | Yes | Lead ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_create_lead`
|
||||
|
||||
Creates an Instantly V2 lead in a campaign or lead list.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaign` | string | No | Campaign ID associated with the lead |
|
||||
| `list_id` | string | No | Lead list ID associated with the lead |
|
||||
| `email` | string | No | Lead email address. Required when adding to a campaign. |
|
||||
| `first_name` | string | No | Lead first name |
|
||||
| `last_name` | string | No | Lead last name |
|
||||
| `company_name` | string | No | Lead company name |
|
||||
| `job_title` | string | No | Lead job title |
|
||||
| `phone` | string | No | Lead phone number |
|
||||
| `website` | string | No | Lead website |
|
||||
| `personalization` | string | No | Lead personalization text |
|
||||
| `lt_interest_status` | number | No | Lead interest status value |
|
||||
| `pl_value_lead` | string | No | Potential value of the lead |
|
||||
| `assigned_to` | string | No | Organization user ID assigned to the lead |
|
||||
| `skip_if_in_workspace` | boolean | No | Skip if the lead already exists in the workspace |
|
||||
| `skip_if_in_campaign` | boolean | No | Skip if the lead already exists in the campaign |
|
||||
| `skip_if_in_list` | boolean | No | Skip if the lead already exists in the list |
|
||||
| `blocklist_id` | string | No | Blocklist ID to check for the lead |
|
||||
| `verify_leads_on_import` | boolean | No | Whether to verify leads on import |
|
||||
| `custom_variables` | json | No | Custom variable object with string, number, boolean, or null values |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_delete_leads`
|
||||
|
||||
Deletes Instantly V2 leads in bulk from a campaign or lead list.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaign_id` | string | No | Campaign ID to delete leads from. Required if list_id is not provided. |
|
||||
| `list_id` | string | No | Lead list ID to delete leads from. Required if campaign_id is not provided. |
|
||||
| `status` | number | No | Optional lead status filter |
|
||||
| `ids` | array | No | Specific lead IDs to delete |
|
||||
| `items` | string | No | No description |
|
||||
| `limit` | number | No | Maximum number of matching leads to delete, up to 10000 |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `count` | number | Number of leads deleted |
|
||||
|
||||
### `instantly_update_lead_interest_status`
|
||||
|
||||
Submits an Instantly V2 background job to update a lead interest status.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `lead_email` | string | Yes | Lead email address |
|
||||
| `interest_value` | number | Yes | Interest status value. Use null in JSON/tool input to reset to Lead. |
|
||||
| `campaign_id` | string | No | Campaign ID for the lead |
|
||||
| `list_id` | string | No | Lead list ID for the lead |
|
||||
| `ai_interest_value` | number | No | AI interest value to set for the lead |
|
||||
| `disable_auto_interest` | boolean | No | Whether to disable auto interest |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Background job submission message |
|
||||
|
||||
### `instantly_list_campaigns`
|
||||
|
||||
Retrieves Instantly V2 campaigns with search, status, tag, and pagination filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | number | No | Number of campaigns to return, from 1 to 100 |
|
||||
| `starting_after` | string | No | Pagination cursor from next_starting_after |
|
||||
| `search` | string | No | Search by campaign name |
|
||||
| `tag_ids` | string | No | Comma-separated campaign tag IDs |
|
||||
| `ai_sales_agent_id` | string | No | AI Sales Agent ID filter |
|
||||
| `status` | number | No | Campaign status enum value |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_create_campaign`
|
||||
|
||||
Creates an Instantly V2 campaign using the documented campaign schedule schema.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Campaign name |
|
||||
| `campaign_schedule` | json | Yes | Campaign schedule object with schedules array |
|
||||
| `sequences` | array | No | Campaign sequence definitions |
|
||||
| `items` | object | No | No description |
|
||||
| `email_list` | array | No | Sending email accounts |
|
||||
| `items` | string | No | No description |
|
||||
| `daily_limit` | number | No | Daily sending limit |
|
||||
| `daily_max_leads` | number | No | Daily maximum new leads to contact |
|
||||
| `open_tracking` | boolean | No | Whether to track opens |
|
||||
| `stop_on_reply` | boolean | No | Whether to stop the campaign on reply |
|
||||
| `link_tracking` | boolean | No | Whether to track links |
|
||||
| `text_only` | boolean | No | Whether the campaign is text only |
|
||||
| `email_gap` | number | No | Gap between emails in minutes |
|
||||
| `pl_value` | number | No | Value of every positive lead |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_patch_campaign`
|
||||
|
||||
Updates documented Instantly V2 campaign fields.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaignId` | string | Yes | Campaign ID |
|
||||
| `name` | string | No | Campaign name |
|
||||
| `campaign_schedule` | json | No | Campaign schedule object with schedules array |
|
||||
| `sequences` | array | No | Campaign sequence definitions |
|
||||
| `items` | object | No | No description |
|
||||
| `email_list` | array | No | Sending email accounts |
|
||||
| `items` | string | No | No description |
|
||||
| `daily_limit` | number | No | Daily sending limit |
|
||||
| `daily_max_leads` | number | No | Daily maximum new leads to contact |
|
||||
| `open_tracking` | boolean | No | Whether to track opens |
|
||||
| `stop_on_reply` | boolean | No | Whether to stop the campaign on reply |
|
||||
| `link_tracking` | boolean | No | Whether to track links |
|
||||
| `text_only` | boolean | No | Whether the campaign is text only |
|
||||
| `email_gap` | number | No | Gap between emails in minutes |
|
||||
| `pl_value` | number | No | Value of every positive lead |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_activate_campaign`
|
||||
|
||||
Activates, starts, or resumes an Instantly V2 campaign.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaignId` | string | Yes | Campaign ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_list_emails`
|
||||
|
||||
Retrieves Instantly V2 Unibox emails with search and pagination filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | number | No | Number of emails to return, from 1 to 100 |
|
||||
| `starting_after` | string | No | Pagination cursor from next_starting_after |
|
||||
| `search` | string | No | Search query, email address, or thread:<thread-id> |
|
||||
| `campaign_id` | string | No | Campaign ID filter |
|
||||
| `list_id` | string | No | Lead list ID filter |
|
||||
| `i_status` | number | No | Email interest status filter |
|
||||
| `eaccount` | string | No | Sending email account filter |
|
||||
| `lead` | string | No | Lead email address filter |
|
||||
| `lead_id` | string | No | Lead ID filter |
|
||||
| `is_unread` | number | No | Unread status filter |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_reply_to_email`
|
||||
|
||||
Sends an Instantly V2 reply to an existing Unibox email.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `eaccount` | string | Yes | Connected email account used to send the reply |
|
||||
| `reply_to_uuid` | string | Yes | Email ID to reply to |
|
||||
| `subject` | string | Yes | Reply subject |
|
||||
| `body` | json | Yes | Reply body object with text and/or html |
|
||||
| `cc_address_email_list` | string | No | Comma-separated CC email addresses |
|
||||
| `bcc_address_email_list` | string | No | Comma-separated BCC email addresses |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_list_lead_lists`
|
||||
|
||||
Retrieves Instantly V2 lead lists with search and pagination filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | number | No | Number of lead lists to return, from 1 to 100 |
|
||||
| `starting_after` | string | No | Starting-after timestamp cursor |
|
||||
| `has_enrichment_task` | boolean | No | Filter by enrichment task setting |
|
||||
| `search` | string | No | Search query to filter lead lists by name |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
### `instantly_create_lead_list`
|
||||
|
||||
Creates an Instantly V2 lead list.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Lead list name |
|
||||
| `has_enrichment_task` | boolean | No | Whether this list runs enrichment for every added lead |
|
||||
| `owned_by` | string | No | User ID of the lead list owner |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
| `name` | string | Record name |
|
||||
| `email_address` | string | Lead email address |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `status` | number | Lead or campaign status |
|
||||
| `subject` | string | Email subject |
|
||||
| `thread_id` | string | Email thread ID |
|
||||
| `message` | string | Operation message |
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
"imap",
|
||||
"incidentio",
|
||||
"infisical",
|
||||
"instantly",
|
||||
"intercom",
|
||||
"jina",
|
||||
"jira",
|
||||
|
||||
@@ -98,6 +98,7 @@ import {
|
||||
ImageIcon,
|
||||
IncidentioIcon,
|
||||
InfisicalIcon,
|
||||
InstantlyIcon,
|
||||
IntercomIcon,
|
||||
JinaAIIcon,
|
||||
JiraIcon,
|
||||
@@ -305,6 +306,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
imap: MailServerIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
infisical: InfisicalIcon,
|
||||
instantly: InstantlyIcon,
|
||||
intercom_v2: IntercomIcon,
|
||||
jina: JinaAIIcon,
|
||||
jira: JiraIcon,
|
||||
|
||||
@@ -7028,6 +7028,178 @@
|
||||
"integrationTypes": ["security"],
|
||||
"tags": ["secrets-management"]
|
||||
},
|
||||
{
|
||||
"type": "instantly",
|
||||
"slug": "instantly",
|
||||
"name": "Instantly",
|
||||
"description": "Manage Instantly leads, campaigns, emails, and lead lists",
|
||||
"longDescription": "Integrate Instantly API V2 into workflows. Create and list leads, manage lead interest status, delete leads in bulk, list and create campaigns, reply to emails, and manage lead lists.",
|
||||
"bgColor": "#FFFFFF",
|
||||
"iconName": "InstantlyIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/instantly",
|
||||
"operations": [
|
||||
{
|
||||
"name": "List Leads",
|
||||
"description": "Retrieves Instantly V2 leads with search, campaign, list, and pagination filters."
|
||||
},
|
||||
{
|
||||
"name": "Get Lead",
|
||||
"description": "Retrieves an Instantly V2 lead by ID."
|
||||
},
|
||||
{
|
||||
"name": "Create Lead",
|
||||
"description": "Creates an Instantly V2 lead in a campaign or lead list."
|
||||
},
|
||||
{
|
||||
"name": "Delete Leads",
|
||||
"description": "Deletes Instantly V2 leads in bulk from a campaign or lead list."
|
||||
},
|
||||
{
|
||||
"name": "Update Lead Interest Status",
|
||||
"description": "Submits an Instantly V2 background job to update a lead interest status."
|
||||
},
|
||||
{
|
||||
"name": "List Campaigns",
|
||||
"description": "Retrieves Instantly V2 campaigns with search, status, tag, and pagination filters."
|
||||
},
|
||||
{
|
||||
"name": "Create Campaign",
|
||||
"description": "Creates an Instantly V2 campaign using the documented campaign schedule schema."
|
||||
},
|
||||
{
|
||||
"name": "Patch Campaign",
|
||||
"description": "Updates documented Instantly V2 campaign fields."
|
||||
},
|
||||
{
|
||||
"name": "Activate Campaign",
|
||||
"description": "Activates, starts, or resumes an Instantly V2 campaign."
|
||||
},
|
||||
{
|
||||
"name": "List Emails",
|
||||
"description": "Retrieves Instantly V2 Unibox emails with search and pagination filters."
|
||||
},
|
||||
{
|
||||
"name": "Reply To Email",
|
||||
"description": "Sends an Instantly V2 reply to an existing Unibox email."
|
||||
},
|
||||
{
|
||||
"name": "List Lead Lists",
|
||||
"description": "Retrieves Instantly V2 lead lists with search and pagination filters."
|
||||
},
|
||||
{
|
||||
"name": "Create Lead List",
|
||||
"description": "Creates an Instantly V2 lead list."
|
||||
}
|
||||
],
|
||||
"operationCount": 13,
|
||||
"triggers": [
|
||||
{
|
||||
"id": "instantly_webhook",
|
||||
"name": "Instantly Webhook",
|
||||
"description": "Trigger workflow on any Instantly webhook event"
|
||||
},
|
||||
{
|
||||
"id": "instantly_email_sent",
|
||||
"name": "Instantly Email Sent",
|
||||
"description": "Trigger when Instantly sends an email"
|
||||
},
|
||||
{
|
||||
"id": "instantly_email_opened",
|
||||
"name": "Instantly Email Opened",
|
||||
"description": "Trigger when a lead opens an Instantly email"
|
||||
},
|
||||
{
|
||||
"id": "instantly_reply_received",
|
||||
"name": "Instantly Reply Received",
|
||||
"description": "Trigger when a lead replies to an Instantly email"
|
||||
},
|
||||
{
|
||||
"id": "instantly_auto_reply_received",
|
||||
"name": "Instantly Auto Reply Received",
|
||||
"description": "Trigger when Instantly receives an auto-reply from a lead"
|
||||
},
|
||||
{
|
||||
"id": "instantly_link_clicked",
|
||||
"name": "Instantly Link Clicked",
|
||||
"description": "Trigger when a lead clicks a tracked Instantly link"
|
||||
},
|
||||
{
|
||||
"id": "instantly_email_bounced",
|
||||
"name": "Instantly Email Bounced",
|
||||
"description": "Trigger when an Instantly email bounces"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_unsubscribed",
|
||||
"name": "Instantly Lead Unsubscribed",
|
||||
"description": "Trigger when an Instantly lead unsubscribes"
|
||||
},
|
||||
{
|
||||
"id": "instantly_account_error",
|
||||
"name": "Instantly Account Error",
|
||||
"description": "Trigger when Instantly reports an account-level error"
|
||||
},
|
||||
{
|
||||
"id": "instantly_campaign_completed",
|
||||
"name": "Instantly Campaign Completed",
|
||||
"description": "Trigger when an Instantly campaign completes"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_neutral",
|
||||
"name": "Instantly Lead Neutral",
|
||||
"description": "Trigger when an Instantly lead is marked neutral"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_interested",
|
||||
"name": "Instantly Lead Interested",
|
||||
"description": "Trigger when an Instantly lead is marked interested"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_not_interested",
|
||||
"name": "Instantly Lead Not Interested",
|
||||
"description": "Trigger when an Instantly lead is marked not interested"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_meeting_booked",
|
||||
"name": "Instantly Lead Meeting Booked",
|
||||
"description": "Trigger when an Instantly lead books a meeting"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_meeting_completed",
|
||||
"name": "Instantly Lead Meeting Completed",
|
||||
"description": "Trigger when an Instantly lead completes a meeting"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_closed",
|
||||
"name": "Instantly Lead Closed",
|
||||
"description": "Trigger when an Instantly lead is marked closed"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_out_of_office",
|
||||
"name": "Instantly Lead Out Of Office",
|
||||
"description": "Trigger when an Instantly lead is out of office"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_wrong_person",
|
||||
"name": "Instantly Lead Wrong Person",
|
||||
"description": "Trigger when an Instantly lead is marked wrong person"
|
||||
},
|
||||
{
|
||||
"id": "instantly_lead_no_show",
|
||||
"name": "Instantly Lead No Show",
|
||||
"description": "Trigger when an Instantly lead is marked no show"
|
||||
},
|
||||
{
|
||||
"id": "instantly_supersearch_enrichment_completed",
|
||||
"name": "Instantly Supersearch Enrichment Completed",
|
||||
"description": "Trigger when Instantly completes a Supersearch enrichment"
|
||||
}
|
||||
],
|
||||
"triggerCount": 20,
|
||||
"authType": "api-key",
|
||||
"category": "tools",
|
||||
"integrationTypes": ["email", "developer-tools", "sales"],
|
||||
"tags": ["sales-engagement", "email-marketing", "automation"]
|
||||
},
|
||||
{
|
||||
"type": "intercom_v2",
|
||||
"slug": "intercom",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -103,6 +103,7 @@ import { ImapBlock } from '@/blocks/blocks/imap'
|
||||
import { IncidentioBlock } from '@/blocks/blocks/incidentio'
|
||||
import { InfisicalBlock } from '@/blocks/blocks/infisical'
|
||||
import { InputTriggerBlock } from '@/blocks/blocks/input_trigger'
|
||||
import { InstantlyBlock } from '@/blocks/blocks/instantly'
|
||||
import { IntercomBlock, IntercomV2Block } from '@/blocks/blocks/intercom'
|
||||
import { JinaBlock } from '@/blocks/blocks/jina'
|
||||
import { JiraBlock } from '@/blocks/blocks/jira'
|
||||
@@ -364,6 +365,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
incidentio: IncidentioBlock,
|
||||
infisical: InfisicalBlock,
|
||||
input_trigger: InputTriggerBlock,
|
||||
instantly: InstantlyBlock,
|
||||
intercom: IntercomBlock,
|
||||
intercom_v2: IntercomV2Block,
|
||||
jina: JinaBlock,
|
||||
|
||||
@@ -430,6 +430,18 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function InstantlyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 766.8 766.8' xmlns='http://www.w3.org/2000/svg'>
|
||||
<circle cx='383.4' cy='383.4' r='383.4' fill='#0081ff' />
|
||||
<path
|
||||
d='M276.12 438.81h-101.8c-3.58 0-5.83-3.87-4.05-6.98l163.07-284.97h238.63c3.87 0 6.06 4.44 3.69 7.51L459.07 305.59c-2.36 3.07-.18 7.51 3.69 7.51h124.51c4.2 0 6.26 5.11 3.23 8.02L235.8 662.51c-3.37 3.24-8.88.06-7.76-4.48l52.61-213.45c.72-2.93-1.5-5.77-4.53-5.77z'
|
||||
fill='#fff'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function EmailBisonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='-66 -2 88 64' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { generateShortId } from '@sim/utils/id'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/provider-subscription-utils'
|
||||
import type {
|
||||
AuthContext,
|
||||
DeleteSubscriptionContext,
|
||||
EventMatchContext,
|
||||
FormatInputContext,
|
||||
FormatInputResult,
|
||||
SubscriptionContext,
|
||||
SubscriptionResult,
|
||||
WebhookProviderHandler,
|
||||
} from '@/lib/webhooks/providers/types'
|
||||
import { verifyTokenAuth } from '@/lib/webhooks/providers/utils'
|
||||
import { instantlyUrl } from '@/tools/instantly/utils'
|
||||
|
||||
const logger = createLogger('WebhookProvider:Instantly')
|
||||
const SIM_WEBHOOK_TOKEN_HEADER = 'x-sim-webhook-token'
|
||||
|
||||
export const instantlyHandler: WebhookProviderHandler = {
|
||||
verifyAuth({ request, requestId, providerConfig }: AuthContext): NextResponse | null {
|
||||
const secretToken = providerConfig.secretToken as string | undefined
|
||||
if (!secretToken) {
|
||||
logger.warn(`[${requestId}] Instantly webhook secret token is missing`)
|
||||
return new NextResponse('Unauthorized', { status: 401 })
|
||||
}
|
||||
|
||||
if (!verifyTokenAuth(request, secretToken, SIM_WEBHOOK_TOKEN_HEADER)) {
|
||||
logger.warn(`[${requestId}] Unauthorized Instantly webhook request`)
|
||||
return new NextResponse('Unauthorized', { status: 401 })
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
async matchEvent({ body, providerConfig, requestId }: EventMatchContext): Promise<boolean> {
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
if (!triggerId) return true
|
||||
|
||||
if (!isRecord(body)) {
|
||||
logger.warn(`[${requestId}] Instantly webhook payload was not an object`)
|
||||
return false
|
||||
}
|
||||
|
||||
const { isInstantlyEventMatch } = await import('@/triggers/instantly/utils')
|
||||
if (!isInstantlyEventMatch(triggerId, body)) {
|
||||
logger.info(`[${requestId}] Instantly event did not match trigger`, {
|
||||
triggerId,
|
||||
eventType: body.event_type,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async formatInput({ body }: FormatInputContext): Promise<FormatInputResult> {
|
||||
const payload = isRecord(body) ? body : {}
|
||||
|
||||
return {
|
||||
input: {
|
||||
timestamp: toStringOrNull(payload.timestamp),
|
||||
eventType: toStringOrNull(payload.event_type),
|
||||
workspace: toStringOrNull(payload.workspace),
|
||||
campaignId: toStringOrNull(payload.campaign_id),
|
||||
campaignName: toStringOrNull(payload.campaign_name),
|
||||
leadEmail: toStringOrNull(payload.lead_email),
|
||||
emailAccount: toStringOrNull(payload.email_account),
|
||||
uniboxUrl: toStringOrNull(payload.unibox_url),
|
||||
step: toNumberOrNull(payload.step),
|
||||
variant: toNumberOrNull(payload.variant),
|
||||
isFirst: toBooleanOrNull(payload.is_first),
|
||||
emailId: toStringOrNull(payload.email_id),
|
||||
emailSubject: toStringOrNull(payload.email_subject),
|
||||
emailText: toStringOrNull(payload.email_text),
|
||||
emailHtml: toStringOrNull(payload.email_html),
|
||||
replyTextSnippet: toStringOrNull(payload.reply_text_snippet),
|
||||
replySubject: toStringOrNull(payload.reply_subject),
|
||||
replyText: toStringOrNull(payload.reply_text),
|
||||
replyHtml: toStringOrNull(payload.reply_html),
|
||||
payload,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
async createSubscription(ctx: SubscriptionContext): Promise<SubscriptionResult | undefined> {
|
||||
const { webhook, requestId } = ctx
|
||||
const providerConfig = getProviderConfig(webhook)
|
||||
const apiKey = providerConfig.triggerApiKey as string | undefined
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
const campaignId = optionalId(providerConfig.triggerCampaignId)
|
||||
|
||||
if (!apiKey?.trim()) {
|
||||
throw new Error('Instantly API Key is required.')
|
||||
}
|
||||
|
||||
if (!triggerId) {
|
||||
throw new Error('Instantly trigger ID is required.')
|
||||
}
|
||||
|
||||
const { getInstantlySubscriptionEventTypeForTrigger } = await import(
|
||||
'@/triggers/instantly/utils'
|
||||
)
|
||||
const eventType = getInstantlySubscriptionEventTypeForTrigger(triggerId)
|
||||
if (!eventType) {
|
||||
throw new Error(`Unknown Instantly trigger type: ${triggerId}`)
|
||||
}
|
||||
|
||||
const secretToken =
|
||||
typeof providerConfig.secretToken === 'string' && providerConfig.secretToken.length > 0
|
||||
? providerConfig.secretToken
|
||||
: generateShortId(32)
|
||||
|
||||
const requestBody: Record<string, unknown> = {
|
||||
name: `Sim - ${triggerId.replace(/^instantly_/, '').replace(/_/g, ' ')}`,
|
||||
target_hook_url: getNotificationUrl(webhook),
|
||||
event_type: eventType,
|
||||
headers: {
|
||||
'X-Sim-Webhook-Token': secretToken,
|
||||
},
|
||||
}
|
||||
|
||||
if (campaignId) {
|
||||
requestBody.campaign = campaignId
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Creating Instantly webhook`, {
|
||||
triggerId,
|
||||
eventType,
|
||||
hasCampaignId: Boolean(campaignId),
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
|
||||
const response = await fetch(instantlyUrl('/api/v2/webhooks'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey.trim()}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
|
||||
const responseBody = await parseJsonResponse(response)
|
||||
if (!response.ok) {
|
||||
const message = extractInstantlyError(responseBody)
|
||||
logger.error(`[${requestId}] Failed to create Instantly webhook`, {
|
||||
status: response.status,
|
||||
message,
|
||||
response: responseBody,
|
||||
})
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
throw new Error('Invalid Instantly API Key or missing webhook permissions.')
|
||||
}
|
||||
|
||||
if (response.status === 402) {
|
||||
throw new Error('Instantly webhook creation requires an active paid plan.')
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
message ? `Instantly error: ${message}` : 'Failed to create Instantly webhook'
|
||||
)
|
||||
}
|
||||
|
||||
const externalId = responseBody?.id
|
||||
if (typeof externalId !== 'string' || externalId.length === 0) {
|
||||
throw new Error('Instantly webhook was created but the API response did not include an ID.')
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Successfully created Instantly webhook`, {
|
||||
externalId,
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
|
||||
return { providerConfigUpdates: { externalId, secretToken } }
|
||||
},
|
||||
|
||||
async deleteSubscription(ctx: DeleteSubscriptionContext): Promise<void> {
|
||||
const { webhook, requestId } = ctx
|
||||
|
||||
try {
|
||||
const providerConfig = getProviderConfig(webhook)
|
||||
const apiKey = providerConfig.triggerApiKey as string | undefined
|
||||
const externalId = providerConfig.externalId as string | undefined
|
||||
|
||||
if (!apiKey?.trim() || !externalId?.trim()) {
|
||||
logger.warn(`[${requestId}] Missing Instantly webhook cleanup configuration`, {
|
||||
webhookId: webhook.id,
|
||||
hasApiKey: Boolean(apiKey),
|
||||
hasExternalId: Boolean(externalId),
|
||||
})
|
||||
if (ctx.strict) throw new Error('Missing Instantly webhook cleanup configuration')
|
||||
return
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
instantlyUrl(`/api/v2/webhooks/${encodeURIComponent(externalId.trim())}`),
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey.trim()}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok && response.status !== 404) {
|
||||
const responseBody = await parseJsonResponse(response)
|
||||
logger.warn(`[${requestId}] Failed to delete Instantly webhook`, {
|
||||
status: response.status,
|
||||
response: responseBody,
|
||||
})
|
||||
if (ctx.strict) throw new Error(`Failed to delete Instantly webhook: ${response.status}`)
|
||||
return
|
||||
}
|
||||
|
||||
await response.body?.cancel()
|
||||
logger.info(`[${requestId}] Successfully deleted Instantly webhook`, {
|
||||
externalId,
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.warn(`[${requestId}] Error deleting Instantly webhook`, {
|
||||
message: toError(error).message,
|
||||
})
|
||||
if (ctx.strict) throw error
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
async function parseJsonResponse(response: Response): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const body: unknown = await response.json()
|
||||
return isRecord(body) ? body : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function extractInstantlyError(body: Record<string, unknown> | null): string | null {
|
||||
if (!body) return null
|
||||
if (typeof body.message === 'string') return body.message
|
||||
if (typeof body.error === 'string') return body.error
|
||||
return null
|
||||
}
|
||||
|
||||
function toStringOrNull(value: unknown): string | null {
|
||||
return typeof value === 'string' ? value : null
|
||||
}
|
||||
|
||||
function toNumberOrNull(value: unknown): number | null {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? value : null
|
||||
}
|
||||
|
||||
function toBooleanOrNull(value: unknown): boolean | null {
|
||||
return typeof value === 'boolean' ? value : null
|
||||
}
|
||||
|
||||
function optionalId(value: unknown): string | undefined {
|
||||
if (typeof value !== 'string') return undefined
|
||||
const trimmed = value.trim()
|
||||
if (trimmed === '' || trimmed === '-') return undefined
|
||||
return trimmed
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { googleFormsHandler } from '@/lib/webhooks/providers/google-forms'
|
||||
import { grainHandler } from '@/lib/webhooks/providers/grain'
|
||||
import { greenhouseHandler } from '@/lib/webhooks/providers/greenhouse'
|
||||
import { imapHandler } from '@/lib/webhooks/providers/imap'
|
||||
import { instantlyHandler } from '@/lib/webhooks/providers/instantly'
|
||||
import { intercomHandler } from '@/lib/webhooks/providers/intercom'
|
||||
import { jiraHandler } from '@/lib/webhooks/providers/jira'
|
||||
import { jsmHandler } from '@/lib/webhooks/providers/jsm'
|
||||
@@ -69,6 +70,7 @@ const PROVIDER_HANDLERS: Record<string, WebhookProviderHandler> = {
|
||||
greenhouse: greenhouseHandler,
|
||||
imap: imapHandler,
|
||||
intercom: intercomHandler,
|
||||
instantly: instantlyHandler,
|
||||
jira: jiraHandler,
|
||||
jsm: jsmHandler,
|
||||
lemlist: lemlistHandler,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type {
|
||||
InstantlyActivateCampaignParams,
|
||||
InstantlyCampaignResponse,
|
||||
} from '@/tools/instantly/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
instantlyBaseParamFields,
|
||||
instantlyHeaders,
|
||||
instantlyUrl,
|
||||
mapCampaign,
|
||||
parseInstantlyResponse,
|
||||
} from '@/tools/instantly/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const activateCampaignTool: ToolConfig<
|
||||
InstantlyActivateCampaignParams,
|
||||
InstantlyCampaignResponse
|
||||
> = {
|
||||
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,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,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 },
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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 { 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 { 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: 'AI Sales Agent ID filter',
|
||||
},
|
||||
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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
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 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 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 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
|
||||
| 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
import { filterUndefined } 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 isRecord(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: isRecord(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: isRecord(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 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']
|
||||
|
||||
function isRecord(value: unknown): value is JsonRecord {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
async function parseJsonResponse(response: Response): Promise<unknown> {
|
||||
try {
|
||||
return await response.json()
|
||||
} catch {
|
||||
return 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
|
||||
}
|
||||
@@ -1390,6 +1390,21 @@ import {
|
||||
infisicalListSecretsTool,
|
||||
infisicalUpdateSecretTool,
|
||||
} from '@/tools/infisical'
|
||||
import {
|
||||
instantlyActivateCampaignTool,
|
||||
instantlyCreateCampaignTool,
|
||||
instantlyCreateLeadListTool,
|
||||
instantlyCreateLeadTool,
|
||||
instantlyDeleteLeadsTool,
|
||||
instantlyGetLeadTool,
|
||||
instantlyListCampaignsTool,
|
||||
instantlyListEmailsTool,
|
||||
instantlyListLeadListsTool,
|
||||
instantlyListLeadsTool,
|
||||
instantlyPatchCampaignTool,
|
||||
instantlyReplyToEmailTool,
|
||||
instantlyUpdateLeadInterestStatusTool,
|
||||
} from '@/tools/instantly'
|
||||
import {
|
||||
intercomAssignConversationV2Tool,
|
||||
intercomAttachContactToCompanyV2Tool,
|
||||
@@ -3434,6 +3449,19 @@ export const tools: Record<string, ToolConfig> = {
|
||||
hex_list_users: hexListUsersTool,
|
||||
hex_run_project: hexRunProjectTool,
|
||||
hex_update_project: hexUpdateProjectTool,
|
||||
instantly_activate_campaign: instantlyActivateCampaignTool,
|
||||
instantly_create_campaign: instantlyCreateCampaignTool,
|
||||
instantly_create_lead: instantlyCreateLeadTool,
|
||||
instantly_create_lead_list: instantlyCreateLeadListTool,
|
||||
instantly_delete_leads: instantlyDeleteLeadsTool,
|
||||
instantly_get_lead: instantlyGetLeadTool,
|
||||
instantly_list_campaigns: instantlyListCampaignsTool,
|
||||
instantly_list_emails: instantlyListEmailsTool,
|
||||
instantly_list_lead_lists: instantlyListLeadListsTool,
|
||||
instantly_list_leads: instantlyListLeadsTool,
|
||||
instantly_patch_campaign: instantlyPatchCampaignTool,
|
||||
instantly_reply_to_email: instantlyReplyToEmailTool,
|
||||
instantly_update_lead_interest_status: instantlyUpdateLeadInterestStatusTool,
|
||||
jina_read_url: jinaReadUrlTool,
|
||||
jina_search: jinaSearchTool,
|
||||
ketch_get_consent: ketchGetConsentTool,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyAccountErrorTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_account_error',
|
||||
name: 'Instantly Account Error',
|
||||
description: 'Trigger when Instantly reports an account-level error',
|
||||
eventLabel: 'Account Error',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyAutoReplyReceivedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_auto_reply_received',
|
||||
name: 'Instantly Auto Reply Received',
|
||||
description: 'Trigger when Instantly receives an auto-reply from a lead',
|
||||
eventLabel: 'Auto Reply Received',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyCampaignCompletedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_campaign_completed',
|
||||
name: 'Instantly Campaign Completed',
|
||||
description: 'Trigger when an Instantly campaign completes',
|
||||
eventLabel: 'Campaign Completed',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyEmailBouncedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_email_bounced',
|
||||
name: 'Instantly Email Bounced',
|
||||
description: 'Trigger when an Instantly email bounces',
|
||||
eventLabel: 'Email Bounced',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyEmailOpenedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_email_opened',
|
||||
name: 'Instantly Email Opened',
|
||||
description: 'Trigger when a lead opens an Instantly email',
|
||||
eventLabel: 'Email Opened',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyEmailSentTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_email_sent',
|
||||
name: 'Instantly Email Sent',
|
||||
description: 'Trigger when Instantly sends an email',
|
||||
eventLabel: 'Email Sent',
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
export { instantlyAccountErrorTrigger } from '@/triggers/instantly/account_error'
|
||||
export { instantlyAutoReplyReceivedTrigger } from '@/triggers/instantly/auto_reply_received'
|
||||
export { instantlyCampaignCompletedTrigger } from '@/triggers/instantly/campaign_completed'
|
||||
export { instantlyEmailBouncedTrigger } from '@/triggers/instantly/email_bounced'
|
||||
export { instantlyEmailOpenedTrigger } from '@/triggers/instantly/email_opened'
|
||||
export { instantlyEmailSentTrigger } from '@/triggers/instantly/email_sent'
|
||||
export { instantlyLeadClosedTrigger } from '@/triggers/instantly/lead_closed'
|
||||
export { instantlyLeadInterestedTrigger } from '@/triggers/instantly/lead_interested'
|
||||
export { instantlyLeadMeetingBookedTrigger } from '@/triggers/instantly/lead_meeting_booked'
|
||||
export { instantlyLeadMeetingCompletedTrigger } from '@/triggers/instantly/lead_meeting_completed'
|
||||
export { instantlyLeadNeutralTrigger } from '@/triggers/instantly/lead_neutral'
|
||||
export { instantlyLeadNoShowTrigger } from '@/triggers/instantly/lead_no_show'
|
||||
export { instantlyLeadNotInterestedTrigger } from '@/triggers/instantly/lead_not_interested'
|
||||
export { instantlyLeadOutOfOfficeTrigger } from '@/triggers/instantly/lead_out_of_office'
|
||||
export { instantlyLeadUnsubscribedTrigger } from '@/triggers/instantly/lead_unsubscribed'
|
||||
export { instantlyLeadWrongPersonTrigger } from '@/triggers/instantly/lead_wrong_person'
|
||||
export { instantlyLinkClickedTrigger } from '@/triggers/instantly/link_clicked'
|
||||
export { instantlyReplyReceivedTrigger } from '@/triggers/instantly/reply_received'
|
||||
export { instantlySupersearchEnrichmentCompletedTrigger } from '@/triggers/instantly/supersearch_enrichment_completed'
|
||||
export { instantlyWebhookTrigger } from '@/triggers/instantly/webhook'
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadClosedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_closed',
|
||||
name: 'Instantly Lead Closed',
|
||||
description: 'Trigger when an Instantly lead is marked closed',
|
||||
eventLabel: 'Lead Closed',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadInterestedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_interested',
|
||||
name: 'Instantly Lead Interested',
|
||||
description: 'Trigger when an Instantly lead is marked interested',
|
||||
eventLabel: 'Lead Interested',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadMeetingBookedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_meeting_booked',
|
||||
name: 'Instantly Lead Meeting Booked',
|
||||
description: 'Trigger when an Instantly lead books a meeting',
|
||||
eventLabel: 'Lead Meeting Booked',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadMeetingCompletedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_meeting_completed',
|
||||
name: 'Instantly Lead Meeting Completed',
|
||||
description: 'Trigger when an Instantly lead completes a meeting',
|
||||
eventLabel: 'Lead Meeting Completed',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadNeutralTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_neutral',
|
||||
name: 'Instantly Lead Neutral',
|
||||
description: 'Trigger when an Instantly lead is marked neutral',
|
||||
eventLabel: 'Lead Neutral',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadNoShowTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_no_show',
|
||||
name: 'Instantly Lead No Show',
|
||||
description: 'Trigger when an Instantly lead is marked no show',
|
||||
eventLabel: 'Lead No Show',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadNotInterestedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_not_interested',
|
||||
name: 'Instantly Lead Not Interested',
|
||||
description: 'Trigger when an Instantly lead is marked not interested',
|
||||
eventLabel: 'Lead Not Interested',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadOutOfOfficeTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_out_of_office',
|
||||
name: 'Instantly Lead Out Of Office',
|
||||
description: 'Trigger when an Instantly lead is out of office',
|
||||
eventLabel: 'Lead Out Of Office',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadUnsubscribedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_unsubscribed',
|
||||
name: 'Instantly Lead Unsubscribed',
|
||||
description: 'Trigger when an Instantly lead unsubscribes',
|
||||
eventLabel: 'Lead Unsubscribed',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLeadWrongPersonTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_lead_wrong_person',
|
||||
name: 'Instantly Lead Wrong Person',
|
||||
description: 'Trigger when an Instantly lead is marked wrong person',
|
||||
eventLabel: 'Lead Wrong Person',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyLinkClickedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_link_clicked',
|
||||
name: 'Instantly Link Clicked',
|
||||
description: 'Trigger when a lead clicks a tracked Instantly link',
|
||||
eventLabel: 'Link Clicked',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyReplyReceivedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_reply_received',
|
||||
name: 'Instantly Reply Received',
|
||||
description: 'Trigger when a lead replies to an Instantly email',
|
||||
eventLabel: 'Reply Received',
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlySupersearchEnrichmentCompletedTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_supersearch_enrichment_completed',
|
||||
name: 'Instantly Supersearch Enrichment Completed',
|
||||
description: 'Trigger when Instantly completes a Supersearch enrichment',
|
||||
eventLabel: 'Supersearch Enrichment Completed',
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { InstantlyIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildInstantlyExtraFields,
|
||||
buildInstantlyOutputs,
|
||||
instantlySetupInstructions,
|
||||
instantlyTriggerOptions,
|
||||
} from '@/triggers/instantly/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
interface CreateInstantlyTriggerOptions {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
eventLabel: string
|
||||
includeDropdown?: boolean
|
||||
}
|
||||
|
||||
export function createInstantlyTrigger({
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
eventLabel,
|
||||
includeDropdown = false,
|
||||
}: CreateInstantlyTriggerOptions): TriggerConfig {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
provider: 'instantly',
|
||||
description,
|
||||
version: '1.0.0',
|
||||
icon: InstantlyIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: id,
|
||||
triggerOptions: instantlyTriggerOptions,
|
||||
includeDropdown,
|
||||
setupInstructions: instantlySetupInstructions(eventLabel),
|
||||
extraFields: buildInstantlyExtraFields(id),
|
||||
}),
|
||||
outputs: buildInstantlyOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { TriggerOutput } from '@/triggers/types'
|
||||
|
||||
export const INSTANTLY_TRIGGER_TO_EVENT_TYPE = {
|
||||
instantly_webhook: 'all_events',
|
||||
instantly_email_sent: 'email_sent',
|
||||
instantly_email_opened: 'email_opened',
|
||||
instantly_reply_received: 'reply_received',
|
||||
instantly_auto_reply_received: 'auto_reply_received',
|
||||
instantly_link_clicked: 'link_clicked',
|
||||
instantly_email_bounced: 'email_bounced',
|
||||
instantly_lead_unsubscribed: 'lead_unsubscribed',
|
||||
instantly_account_error: 'account_error',
|
||||
instantly_campaign_completed: 'campaign_completed',
|
||||
instantly_lead_neutral: 'lead_neutral',
|
||||
instantly_lead_interested: 'lead_interested',
|
||||
instantly_lead_not_interested: 'lead_not_interested',
|
||||
instantly_lead_meeting_booked: 'lead_meeting_booked',
|
||||
instantly_lead_meeting_completed: 'lead_meeting_completed',
|
||||
instantly_lead_closed: 'lead_closed',
|
||||
instantly_lead_out_of_office: 'lead_out_of_office',
|
||||
instantly_lead_wrong_person: 'lead_wrong_person',
|
||||
instantly_lead_no_show: 'lead_no_show',
|
||||
instantly_supersearch_enrichment_completed: 'supersearch_enrichment_completed',
|
||||
} as const
|
||||
|
||||
export const INSTANTLY_TRIGGER_TO_SUBSCRIPTION_EVENT_TYPE = {
|
||||
...INSTANTLY_TRIGGER_TO_EVENT_TYPE,
|
||||
instantly_auto_reply_received: 'all_events',
|
||||
instantly_link_clicked: 'email_link_clicked',
|
||||
} as const
|
||||
|
||||
export const instantlyTriggerOptions = [
|
||||
{ label: 'All Events', id: 'instantly_webhook' },
|
||||
{ label: 'Email Sent', id: 'instantly_email_sent' },
|
||||
{ label: 'Email Opened', id: 'instantly_email_opened' },
|
||||
{ label: 'Reply Received', id: 'instantly_reply_received' },
|
||||
{ label: 'Auto Reply Received', id: 'instantly_auto_reply_received' },
|
||||
{ label: 'Link Clicked', id: 'instantly_link_clicked' },
|
||||
{ label: 'Email Bounced', id: 'instantly_email_bounced' },
|
||||
{ label: 'Lead Unsubscribed', id: 'instantly_lead_unsubscribed' },
|
||||
{ label: 'Account Error', id: 'instantly_account_error' },
|
||||
{ label: 'Campaign Completed', id: 'instantly_campaign_completed' },
|
||||
{ label: 'Lead Neutral', id: 'instantly_lead_neutral' },
|
||||
{ label: 'Lead Interested', id: 'instantly_lead_interested' },
|
||||
{ label: 'Lead Not Interested', id: 'instantly_lead_not_interested' },
|
||||
{ label: 'Lead Meeting Booked', id: 'instantly_lead_meeting_booked' },
|
||||
{ label: 'Lead Meeting Completed', id: 'instantly_lead_meeting_completed' },
|
||||
{ label: 'Lead Closed', id: 'instantly_lead_closed' },
|
||||
{ label: 'Lead Out Of Office', id: 'instantly_lead_out_of_office' },
|
||||
{ label: 'Lead Wrong Person', id: 'instantly_lead_wrong_person' },
|
||||
{ label: 'Lead No Show', id: 'instantly_lead_no_show' },
|
||||
{
|
||||
label: 'Supersearch Enrichment Completed',
|
||||
id: 'instantly_supersearch_enrichment_completed',
|
||||
},
|
||||
]
|
||||
|
||||
export function instantlySetupInstructions(eventType: string): string {
|
||||
const instructions = [
|
||||
'Enter an <strong>Instantly API Key</strong> with webhook create/delete permissions.',
|
||||
'Optionally enter a <strong>Campaign ID</strong> to receive only events for that campaign.',
|
||||
`Click <strong>Save Configuration</strong> to automatically create an Instantly webhook for <strong>${eventType}</strong>.`,
|
||||
'The webhook will be automatically deleted from Instantly when this trigger is removed.',
|
||||
]
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
export function buildInstantlyExtraFields(triggerId: string): SubBlockConfig[] {
|
||||
return [
|
||||
{
|
||||
id: 'triggerApiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Instantly API key',
|
||||
password: true,
|
||||
required: true,
|
||||
paramVisibility: 'user-only',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
},
|
||||
{
|
||||
id: 'triggerCampaignId',
|
||||
title: 'Campaign ID (Optional)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Leave empty for all campaigns',
|
||||
paramVisibility: 'user-only',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function buildInstantlyOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
timestamp: { type: 'string', description: 'ISO timestamp when the event occurred' },
|
||||
eventType: { type: 'string', description: 'Instantly webhook event type' },
|
||||
workspace: { type: 'string', description: 'Instantly workspace UUID' },
|
||||
campaignId: { type: 'string', description: 'Instantly campaign UUID' },
|
||||
campaignName: { type: 'string', description: 'Instantly campaign name' },
|
||||
leadEmail: { type: 'string', description: 'Lead email address' },
|
||||
emailAccount: { type: 'string', description: 'Email account used to send the message' },
|
||||
uniboxUrl: { type: 'string', description: 'URL to view the conversation in Unibox' },
|
||||
step: { type: 'number', description: 'Campaign step number, starting at 1' },
|
||||
variant: { type: 'number', description: 'Campaign step variant number, starting at 1' },
|
||||
isFirst: { type: 'boolean', description: 'Whether this is the first event of this type' },
|
||||
emailId: { type: 'string', description: 'Email ID, usable as reply_to_uuid' },
|
||||
emailSubject: { type: 'string', description: 'Sent email subject' },
|
||||
emailText: { type: 'string', description: 'Sent email plain-text content' },
|
||||
emailHtml: { type: 'string', description: 'Sent email HTML content' },
|
||||
replyTextSnippet: { type: 'string', description: 'Short preview of the reply content' },
|
||||
replySubject: { type: 'string', description: 'Reply email subject' },
|
||||
replyText: { type: 'string', description: 'Full plain-text reply content' },
|
||||
replyHtml: { type: 'string', description: 'Full HTML reply content' },
|
||||
payload: {
|
||||
type: 'json',
|
||||
description: 'Full Instantly webhook payload, including any extra lead data fields',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getInstantlyEventTypeForTrigger(triggerId: string): string | undefined {
|
||||
return INSTANTLY_TRIGGER_TO_EVENT_TYPE[triggerId as keyof typeof INSTANTLY_TRIGGER_TO_EVENT_TYPE]
|
||||
}
|
||||
|
||||
export function getInstantlySubscriptionEventTypeForTrigger(triggerId: string): string | undefined {
|
||||
return INSTANTLY_TRIGGER_TO_SUBSCRIPTION_EVENT_TYPE[
|
||||
triggerId as keyof typeof INSTANTLY_TRIGGER_TO_SUBSCRIPTION_EVENT_TYPE
|
||||
]
|
||||
}
|
||||
|
||||
export function isInstantlyEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
|
||||
if (triggerId === 'instantly_webhook') return true
|
||||
|
||||
const expectedEventType = getInstantlyEventTypeForTrigger(triggerId)
|
||||
if (!expectedEventType) return false
|
||||
|
||||
const actualEventType = body.event_type
|
||||
if (typeof actualEventType !== 'string') return false
|
||||
|
||||
if (triggerId === 'instantly_link_clicked') {
|
||||
return actualEventType === 'link_clicked' || actualEventType === 'email_link_clicked'
|
||||
}
|
||||
|
||||
return actualEventType === expectedEventType
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createInstantlyTrigger } from '@/triggers/instantly/trigger'
|
||||
|
||||
export const instantlyWebhookTrigger = createInstantlyTrigger({
|
||||
id: 'instantly_webhook',
|
||||
name: 'Instantly Webhook',
|
||||
description: 'Trigger workflow on any Instantly webhook event',
|
||||
eventLabel: 'All Events',
|
||||
includeDropdown: true,
|
||||
})
|
||||
@@ -147,6 +147,28 @@ import {
|
||||
} from '@/triggers/greenhouse'
|
||||
import { hubspotPollingTrigger } from '@/triggers/hubspot'
|
||||
import { imapPollingTrigger } from '@/triggers/imap'
|
||||
import {
|
||||
instantlyAccountErrorTrigger,
|
||||
instantlyAutoReplyReceivedTrigger,
|
||||
instantlyCampaignCompletedTrigger,
|
||||
instantlyEmailBouncedTrigger,
|
||||
instantlyEmailOpenedTrigger,
|
||||
instantlyEmailSentTrigger,
|
||||
instantlyLeadClosedTrigger,
|
||||
instantlyLeadInterestedTrigger,
|
||||
instantlyLeadMeetingBookedTrigger,
|
||||
instantlyLeadMeetingCompletedTrigger,
|
||||
instantlyLeadNeutralTrigger,
|
||||
instantlyLeadNoShowTrigger,
|
||||
instantlyLeadNotInterestedTrigger,
|
||||
instantlyLeadOutOfOfficeTrigger,
|
||||
instantlyLeadUnsubscribedTrigger,
|
||||
instantlyLeadWrongPersonTrigger,
|
||||
instantlyLinkClickedTrigger,
|
||||
instantlyReplyReceivedTrigger,
|
||||
instantlySupersearchEnrichmentCompletedTrigger,
|
||||
instantlyWebhookTrigger,
|
||||
} from '@/triggers/instantly'
|
||||
import {
|
||||
intercomContactCreatedTrigger,
|
||||
intercomConversationClosedTrigger,
|
||||
@@ -563,6 +585,26 @@ export const TRIGGER_REGISTRY: TriggerRegistry = {
|
||||
intercom_contact_created: intercomContactCreatedTrigger,
|
||||
intercom_user_created: intercomUserCreatedTrigger,
|
||||
intercom_webhook: intercomWebhookTrigger,
|
||||
instantly_webhook: instantlyWebhookTrigger,
|
||||
instantly_email_sent: instantlyEmailSentTrigger,
|
||||
instantly_email_opened: instantlyEmailOpenedTrigger,
|
||||
instantly_reply_received: instantlyReplyReceivedTrigger,
|
||||
instantly_auto_reply_received: instantlyAutoReplyReceivedTrigger,
|
||||
instantly_link_clicked: instantlyLinkClickedTrigger,
|
||||
instantly_email_bounced: instantlyEmailBouncedTrigger,
|
||||
instantly_lead_unsubscribed: instantlyLeadUnsubscribedTrigger,
|
||||
instantly_account_error: instantlyAccountErrorTrigger,
|
||||
instantly_campaign_completed: instantlyCampaignCompletedTrigger,
|
||||
instantly_lead_neutral: instantlyLeadNeutralTrigger,
|
||||
instantly_lead_interested: instantlyLeadInterestedTrigger,
|
||||
instantly_lead_not_interested: instantlyLeadNotInterestedTrigger,
|
||||
instantly_lead_meeting_booked: instantlyLeadMeetingBookedTrigger,
|
||||
instantly_lead_meeting_completed: instantlyLeadMeetingCompletedTrigger,
|
||||
instantly_lead_closed: instantlyLeadClosedTrigger,
|
||||
instantly_lead_out_of_office: instantlyLeadOutOfOfficeTrigger,
|
||||
instantly_lead_wrong_person: instantlyLeadWrongPersonTrigger,
|
||||
instantly_lead_no_show: instantlyLeadNoShowTrigger,
|
||||
instantly_supersearch_enrichment_completed: instantlySupersearchEnrichmentCompletedTrigger,
|
||||
zoom_meeting_started: zoomMeetingStartedTrigger,
|
||||
zoom_meeting_ended: zoomMeetingEndedTrigger,
|
||||
zoom_participant_joined: zoomParticipantJoinedTrigger,
|
||||
|
||||
Reference in New Issue
Block a user