mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(emailbison): block, tools, sharepoint v2 block with cleaner code (#4470)
* feat(emailbison): block, tools * type improvments * typecheck issue * add email bison trigger, cleanup sharepoint block * address comments * fix tests * error on partial upload failures
This commit is contained in:
@@ -415,6 +415,17 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function EmailBisonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='-66 -2 88 64' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M0 0 C1 2.3 2 4.6 3 7 C7 8.8 7 8.8 11.7 9.9 C13.2 10.3 14.8 10.7 16.4 11.2 C17.6 11.4 18.8 11.7 20 12 C21.5 38 21.5 38 15.8 45.1 C11 49 11 49 7 50 C5.3 49.3 3.7 48.7 2 48 C2 46 2 44 2 42 C0.7 42.3 -0.6 42.7 -2 43 C-1 49.1 0.8 54.3 3 60 C-1 61 -1 61 -7 58 C-7 56.3 -7 54.7 -7 53 C-7.7 54 -8.3 55 -9 56 C-9.7 56.8 -9.7 56.8 -13 61 C-18 60 -18 60 -21 57 C-21.2 56.1 -21.2 56.1 -22 51.3 C-26.1 42.7 -31.1 41.6 -39.7 38.3 C-44 37 -44 37 -47 37 C-53.6 45.2 -56 50.5 -55 61 C-57 60.7 -59 60.3 -61 60 C-65.9 51.1 -63 42.4 -61 33 C-62 34 -63 35 -64 36 C-64.7 36 -65.3 36 -66 36 C-64.4 29.7 -62.6 24 -60 18 C-56.3 16 -52.5 14 -48.6 12.2 C-44 10 -44 10 -39.6 7.3 C-26.4 -0.8 -15.2 -1.1 0 0 Z M-61 27 C-61.3 28 -61.7 29 -62 30 C-61.3 30 -60.7 30 -60 30 C-60.3 29 -60.7 28 -61 27 Z M-7 50 C-6 52 -6 52 -6 52 Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function MailServerIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
DynamoDBIcon,
|
||||
ElasticsearchIcon,
|
||||
ElevenLabsIcon,
|
||||
EmailBisonIcon,
|
||||
EnrichSoIcon,
|
||||
EvernoteIcon,
|
||||
ExaAIIcon,
|
||||
@@ -250,6 +251,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
dynamodb: DynamoDBIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
emailbison: EmailBisonIcon,
|
||||
enrich: EnrichSoIcon,
|
||||
evernote: EvernoteIcon,
|
||||
exa: ExaAIIcon,
|
||||
@@ -380,6 +382,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
ses: SESIcon,
|
||||
sftp: SftpIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
sharepoint_v2: MicrosoftSharepointIcon,
|
||||
shopify: ShopifyIcon,
|
||||
similarweb: SimilarwebIcon,
|
||||
sixtyfour: SixtyfourIcon,
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
---
|
||||
title: Email Bison
|
||||
description: Manage Email Bison leads, campaigns, replies, and tags
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="emailbison"
|
||||
color="#FB7A22"
|
||||
/>
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Email Bison into workflows. Create and update leads, manage campaigns, attach leads to campaigns, list replies, and organize leads with tags.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `emailbison_list_leads`
|
||||
|
||||
Retrieves leads from Email Bison with optional search and tag filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `search` | string | No | Search term for filtering leads |
|
||||
| `campaignStatus` | string | No | Lead campaign status filter: in_sequence, sequence_finished, sequence_stopped, never_contacted, or replied |
|
||||
| `tagIds` | array | No | Tag IDs to include |
|
||||
| `items` | number | No | No description |
|
||||
| `excludedTagIds` | array | No | Tag IDs to exclude |
|
||||
| `items` | number | No | No description |
|
||||
| `withoutTags` | boolean | No | Only return leads without tags |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_get_lead`
|
||||
|
||||
Retrieves a lead by Email Bison lead ID or email address.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `leadId` | string | Yes | Lead ID or email address |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_create_lead`
|
||||
|
||||
Creates a single lead in Email Bison.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `firstName` | string | Yes | Lead first name |
|
||||
| `lastName` | string | Yes | Lead last name |
|
||||
| `email` | string | Yes | Lead email address |
|
||||
| `title` | string | No | Lead job title |
|
||||
| `company` | string | No | Lead company |
|
||||
| `notes` | string | No | Additional notes about the lead |
|
||||
| `customVariables` | array | No | Custom variables to store on the lead |
|
||||
| `items` | object | No | Custom variable name |
|
||||
| `properties` | string | No | Custom variable name |
|
||||
| `name` | string | No | No description |
|
||||
| `value` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_update_lead`
|
||||
|
||||
Updates an existing Email Bison lead. Fields omitted from a PUT update may be cleared by Email Bison.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `leadId` | string | Yes | Lead ID or email address |
|
||||
| `firstName` | string | Yes | Lead first name |
|
||||
| `lastName` | string | Yes | Lead last name |
|
||||
| `email` | string | Yes | Lead email address |
|
||||
| `title` | string | No | Lead job title |
|
||||
| `company` | string | No | Lead company |
|
||||
| `notes` | string | No | Additional notes about the lead |
|
||||
| `customVariables` | array | No | Custom variables to store on the lead |
|
||||
| `items` | object | No | Custom variable name |
|
||||
| `properties` | string | No | Custom variable name |
|
||||
| `name` | string | No | No description |
|
||||
| `value` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_list_campaigns`
|
||||
|
||||
Retrieves Email Bison campaigns.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_create_campaign`
|
||||
|
||||
Creates a new Email Bison campaign.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Campaign name |
|
||||
| `campaignType` | string | No | Campaign type: outbound or reply_followup |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_update_campaign`
|
||||
|
||||
Updates Email Bison campaign settings.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaignId` | number | Yes | Campaign ID |
|
||||
| `name` | string | No | Campaign name |
|
||||
| `maxEmailsPerDay` | number | No | Maximum emails per day |
|
||||
| `maxNewLeadsPerDay` | number | No | Maximum new leads per day |
|
||||
| `plainText` | boolean | No | Send plain text emails |
|
||||
| `openTracking` | boolean | No | Enable open tracking |
|
||||
| `reputationBuilding` | boolean | No | Enable reputation building |
|
||||
| `canUnsubscribe` | boolean | No | Enable unsubscribe link |
|
||||
| `includeAutoRepliesInStats` | boolean | No | Include auto replies in campaign stats |
|
||||
| `sequencePrioritization` | string | No | Sequence prioritization: followups or new_leads |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_update_campaign_status`
|
||||
|
||||
Pauses, resumes, or archives an Email Bison campaign.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaignId` | number | Yes | Campaign ID |
|
||||
| `action` | string | Yes | Status action: pause, resume, or archive |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_attach_leads_to_campaign`
|
||||
|
||||
Adds existing Email Bison leads to a campaign.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `campaignId` | number | Yes | Campaign ID |
|
||||
| `leadIds` | array | Yes | Lead IDs to add to the campaign |
|
||||
| `items` | number | No | No description |
|
||||
| `allowParallelSending` | boolean | No | Force add leads already in sequence in other campaigns |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_list_replies`
|
||||
|
||||
Retrieves Email Bison replies with optional status, folder, campaign, sender, lead, and tag filters.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `search` | string | No | Search term for replies |
|
||||
| `status` | string | No | Reply status: interested, automated_reply, or not_automated_reply |
|
||||
| `folder` | string | No | Reply folder: inbox, sent, spam, bounced, or all |
|
||||
| `read` | boolean | No | Filter by read state |
|
||||
| `campaignId` | number | No | Campaign ID |
|
||||
| `senderEmailId` | number | No | Sender email ID |
|
||||
| `leadId` | number | No | Lead ID |
|
||||
| `tagIds` | array | No | Tag IDs to filter replies by |
|
||||
| `items` | number | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_list_tags`
|
||||
|
||||
Retrieves all Email Bison tags for the authenticated workspace.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_create_tag`
|
||||
|
||||
Creates a new Email Bison tag.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Tag name |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
### `emailbison_attach_tags_to_leads`
|
||||
|
||||
Attaches Email Bison tags to one or more leads.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tagIds` | array | Yes | Tag IDs to attach |
|
||||
| `items` | number | No | No description |
|
||||
| `leadIds` | array | Yes | Lead IDs to tag |
|
||||
| `items` | number | No | No description |
|
||||
| `skipWebhooks` | boolean | No | Skip Email Bison webhooks for this action |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `replies` | array | List of replies |
|
||||
| `tags` | array | List of tags |
|
||||
| `count` | number | Number of returned records |
|
||||
| `id` | number | Record ID |
|
||||
| `uuid` | string | Record UUID |
|
||||
| `name` | string | Campaign or tag name |
|
||||
| `first_name` | string | Lead first name |
|
||||
| `last_name` | string | Lead last name |
|
||||
| `email` | string | Lead email address |
|
||||
| `status` | string | Record status |
|
||||
| `success` | boolean | Whether the action succeeded |
|
||||
| `message` | string | Action message |
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"dynamodb",
|
||||
"elasticsearch",
|
||||
"elevenlabs",
|
||||
"emailbison",
|
||||
"enrich",
|
||||
"evernote",
|
||||
"exa",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Sharepoint
|
||||
title: SharePoint
|
||||
description: Work with pages and lists
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="sharepoint"
|
||||
type="sharepoint_v2"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
@@ -73,6 +73,7 @@ Read a specific page from a SharePoint site
|
||||
| `pageId` | string | No | The ID of the page to read. Example: a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `pageName` | string | No | The name of the page to read \(alternative to pageId\). Example: Home.aspx or About-Us.aspx |
|
||||
| `maxPages` | number | No | Maximum number of pages to return when listing all pages \(default: 10, max: 50\) |
|
||||
| `nextPageUrl` | string | No | Full @odata.nextLink URL from a previous Microsoft Graph page response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -84,6 +85,7 @@ Read a specific page from a SharePoint site
|
||||
| ↳ `title` | string | The title of the page |
|
||||
| ↳ `webUrl` | string | The URL to access the page |
|
||||
| ↳ `pageLayout` | string | The layout type of the page |
|
||||
| ↳ `description` | string | The description of the page |
|
||||
| ↳ `createdDateTime` | string | When the page was created |
|
||||
| ↳ `lastModifiedDateTime` | string | When the page was last modified |
|
||||
| `pages` | array | List of SharePoint pages |
|
||||
@@ -93,6 +95,7 @@ Read a specific page from a SharePoint site
|
||||
| ↳ `title` | string | The title of the page |
|
||||
| ↳ `webUrl` | string | The URL to access the page |
|
||||
| ↳ `pageLayout` | string | The layout type of the page |
|
||||
| ↳ `description` | string | The description of the page |
|
||||
| ↳ `createdDateTime` | string | When the page was created |
|
||||
| ↳ `lastModifiedDateTime` | string | When the page was last modified |
|
||||
| ↳ `content` | object | content output from the tool |
|
||||
@@ -102,6 +105,7 @@ Read a specific page from a SharePoint site
|
||||
| ↳ `content` | string | Extracted text content from the page |
|
||||
| ↳ `canvasLayout` | object | Raw SharePoint canvas layout structure |
|
||||
| `totalPages` | number | Total number of pages found |
|
||||
| `nextPageUrl` | string | Full Microsoft Graph @odata.nextLink URL for the next page of results |
|
||||
|
||||
### `sharepoint_list_sites`
|
||||
|
||||
@@ -112,7 +116,9 @@ List details of all SharePoint sites
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `groupId` | string | No | The group ID for accessing a group team site. Example: a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `nextPageUrl` | string | No | Full @odata.nextLink URL from a previous Microsoft Graph page response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -139,6 +145,7 @@ List details of all SharePoint sites
|
||||
| ↳ `description` | string | The description of the site |
|
||||
| ↳ `createdDateTime` | string | When the site was created |
|
||||
| ↳ `lastModifiedDateTime` | string | When the site was last modified |
|
||||
| `nextPageUrl` | string | Full Microsoft Graph @odata.nextLink URL for the next page of results |
|
||||
|
||||
### `sharepoint_create_list`
|
||||
|
||||
@@ -153,7 +160,7 @@ Create a new list in a SharePoint site
|
||||
| `listDisplayName` | string | Yes | Display name of the list to create. Example: Project Tasks or Customer Contacts |
|
||||
| `listDescription` | string | No | Description of the list |
|
||||
| `listTemplate` | string | No | List template name \(e.g., 'genericList'\) |
|
||||
| `pageContent` | string | No | Optional JSON of columns. Either a top-level array of column definitions or an object with \{ columns: \[...\] \}. |
|
||||
| `pageContent` | json | No | Optional JSON of columns. Either a top-level array of column definitions or an object with \{ columns: \[...\] \}. |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -179,6 +186,9 @@ Get metadata (and optionally columns/items) for a SharePoint list
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `listId` | string | No | The ID of the list to retrieve. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `includeColumns` | boolean | No | Whether to include column definitions when retrieving a specific list |
|
||||
| `includeItems` | boolean | No | Whether to include list items when retrieving a specific list |
|
||||
| `nextPageUrl` | string | No | Full @odata.nextLink URL from a previous Microsoft Graph page response |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -194,6 +204,10 @@ Get metadata (and optionally columns/items) for a SharePoint list
|
||||
| ↳ `list` | object | List properties \(e.g., template\) |
|
||||
| ↳ `columns` | array | List column definitions |
|
||||
| `lists` | array | All lists in the site when no listId/title provided |
|
||||
| `items` | array | List items with expanded fields when reading list items |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `fields` | object | Field values for the item |
|
||||
| `nextPageUrl` | string | Full Microsoft Graph @odata.nextLink URL for the next page of results |
|
||||
|
||||
### `sharepoint_update_list`
|
||||
|
||||
@@ -205,9 +219,9 @@ Update the properties (fields) on a SharePoint list item
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `listId` | string | No | The ID of the list containing the item. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `listId` | string | Yes | The ID of the list containing the item. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `itemId` | string | Yes | The ID of the list item to update. Example: 1, 42, or 123 |
|
||||
| `listItemFields` | object | Yes | Field values to update on the list item |
|
||||
| `listItemFields` | json | Yes | Field values to update on the list item |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -228,7 +242,7 @@ Add a new item to a SharePoint list
|
||||
| `siteSelector` | string | No | Select the SharePoint site |
|
||||
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
|
||||
| `listId` | string | Yes | The ID of the list to add the item to. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012 |
|
||||
| `listItemFields` | object | Yes | Field values for the new list item |
|
||||
| `listItemFields` | json | Yes | Field values for the new list item |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -250,7 +264,7 @@ Upload files to a SharePoint document library
|
||||
| `driveId` | string | No | The ID of the document library \(drive\). If not provided, uses default drive. Example: b!abc123def456 |
|
||||
| `folderPath` | string | No | Optional folder path within the document library. Example: /Documents/Subfolder or /Shared Documents/Reports |
|
||||
| `fileName` | string | No | Optional: override the uploaded file name. Example: report-2024.pdf |
|
||||
| `files` | file[] | No | Files to upload to SharePoint |
|
||||
| `files` | file[] | Yes | Files to upload to SharePoint |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
||||
"calendly",
|
||||
"circleback",
|
||||
"confluence",
|
||||
"emailbison",
|
||||
"fathom",
|
||||
"fireflies",
|
||||
"github",
|
||||
|
||||
@@ -75,12 +75,21 @@ const AUTH_STEP: Record<AuthType, string> = {
|
||||
none: 'Authenticate your account to connect.',
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures autogenerated prose can be safely composed with a following sentence.
|
||||
*/
|
||||
function sentenceWithTerminalPunctuation(value: string): string {
|
||||
const trimmedValue = value.trim()
|
||||
return /[.!?]$/.test(trimmedValue) ? trimmedValue : `${trimmedValue}.`
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates targeted FAQs from integration metadata.
|
||||
* Questions mirror real search queries to drive FAQPage rich snippets.
|
||||
*/
|
||||
function buildFAQs(integration: Integration): FAQItem[] {
|
||||
const { name, description, operations, triggers, authType } = integration
|
||||
const faqDescription = sentenceWithTerminalPunctuation(description)
|
||||
const topOps = operations.slice(0, 5)
|
||||
const topOpNames = topOps.map((o) => o.name)
|
||||
const authStep = AUTH_STEP[authType]
|
||||
@@ -88,7 +97,7 @@ function buildFAQs(integration: Integration): FAQItem[] {
|
||||
const faqs: FAQItem[] = [
|
||||
{
|
||||
question: `What is Sim's ${name} integration?`,
|
||||
answer: `Sim's ${name} integration lets you build AI agents that automate tasks in ${name} without writing code. ${description} You can connect ${name} to hundreds of other services in the same agent — from CRMs and spreadsheets to messaging tools and databases.`,
|
||||
answer: `Sim's ${name} integration lets you build AI agents that automate tasks in ${name} without writing code. ${faqDescription} You can connect ${name} to hundreds of other services in the same agent — from CRMs and spreadsheets to messaging tools and databases.`,
|
||||
},
|
||||
{
|
||||
question: `What can I automate with ${name} in Sim?`,
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
DynamoDBIcon,
|
||||
ElasticsearchIcon,
|
||||
ElevenLabsIcon,
|
||||
EmailBisonIcon,
|
||||
EnrichSoIcon,
|
||||
EvernoteIcon,
|
||||
ExaAIIcon,
|
||||
@@ -248,6 +249,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
dynamodb: DynamoDBIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
emailbison: EmailBisonIcon,
|
||||
enrich: EnrichSoIcon,
|
||||
evernote: EvernoteIcon,
|
||||
exa: ExaAIIcon,
|
||||
@@ -361,7 +363,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
servicenow: ServiceNowIcon,
|
||||
ses: SESIcon,
|
||||
sftp: SftpIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
sharepoint_v2: MicrosoftSharepointIcon,
|
||||
shopify: ShopifyIcon,
|
||||
similarweb: SimilarwebIcon,
|
||||
sixtyfour: SixtyfourIcon,
|
||||
|
||||
@@ -3637,6 +3637,77 @@
|
||||
"integrationTypes": ["ai"],
|
||||
"tags": ["text-to-speech"]
|
||||
},
|
||||
{
|
||||
"type": "emailbison",
|
||||
"slug": "email-bison",
|
||||
"name": "Email Bison",
|
||||
"description": "Manage Email Bison leads, campaigns, replies, and tags",
|
||||
"longDescription": "Integrate Email Bison into workflows. Create and update leads, manage campaigns, attach leads to campaigns, list replies, and organize leads with tags.",
|
||||
"bgColor": "#FB7A22",
|
||||
"iconName": "EmailBisonIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/emailbison",
|
||||
"operations": [
|
||||
{
|
||||
"name": "List Leads",
|
||||
"description": "Retrieves leads from Email Bison with optional search and tag filters."
|
||||
},
|
||||
{
|
||||
"name": "Get Lead",
|
||||
"description": "Retrieves a lead by Email Bison lead ID or email address."
|
||||
},
|
||||
{
|
||||
"name": "Create Lead",
|
||||
"description": "Creates a single lead in Email Bison."
|
||||
},
|
||||
{
|
||||
"name": "Update Lead",
|
||||
"description": "Updates an existing Email Bison lead. Fields omitted from a PUT update may be cleared by Email Bison."
|
||||
},
|
||||
{
|
||||
"name": "List Campaigns",
|
||||
"description": "Retrieves Email Bison campaigns."
|
||||
},
|
||||
{
|
||||
"name": "Create Campaign",
|
||||
"description": "Creates a new Email Bison campaign."
|
||||
},
|
||||
{
|
||||
"name": "Update Campaign",
|
||||
"description": "Updates Email Bison campaign settings."
|
||||
},
|
||||
{
|
||||
"name": "Update Campaign Status",
|
||||
"description": "Pauses, resumes, or archives an Email Bison campaign."
|
||||
},
|
||||
{
|
||||
"name": "Attach Leads to Campaign",
|
||||
"description": "Adds existing Email Bison leads to a campaign."
|
||||
},
|
||||
{
|
||||
"name": "List Replies",
|
||||
"description": "Retrieves Email Bison replies with optional status, folder, campaign, sender, lead, and tag filters."
|
||||
},
|
||||
{
|
||||
"name": "List Tags",
|
||||
"description": "Retrieves all Email Bison tags for the authenticated workspace."
|
||||
},
|
||||
{
|
||||
"name": "Create Tag",
|
||||
"description": "Creates a new Email Bison tag."
|
||||
},
|
||||
{
|
||||
"name": "Attach Tags to Leads",
|
||||
"description": "Attaches Email Bison tags to one or more leads."
|
||||
}
|
||||
],
|
||||
"operationCount": 13,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "api-key",
|
||||
"category": "tools",
|
||||
"integrationTypes": ["email", "developer-tools", "sales"],
|
||||
"tags": ["sales-engagement", "email-marketing", "automation"]
|
||||
},
|
||||
{
|
||||
"type": "openai",
|
||||
"slug": "embeddings",
|
||||
@@ -11873,9 +11944,9 @@
|
||||
"tags": ["cloud", "automation"]
|
||||
},
|
||||
{
|
||||
"type": "sharepoint",
|
||||
"type": "sharepoint_v2",
|
||||
"slug": "sharepoint",
|
||||
"name": "Sharepoint",
|
||||
"name": "SharePoint",
|
||||
"description": "Work with pages and lists",
|
||||
"longDescription": "Integrate SharePoint into the workflow. Read/create pages, list sites, and work with lists (read, create, update items). Requires OAuth.",
|
||||
"bgColor": "#E0E0E0",
|
||||
@@ -11903,11 +11974,11 @@
|
||||
"description": "Get metadata (and optionally columns/items) for a SharePoint list"
|
||||
},
|
||||
{
|
||||
"name": "Update List",
|
||||
"name": "Update List Item",
|
||||
"description": "Update the properties (fields) on a SharePoint list item"
|
||||
},
|
||||
{
|
||||
"name": "Add List Items",
|
||||
"name": "Add List Item",
|
||||
"description": "Add a new item to a SharePoint list"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { sharepointUploadContract } from '@/lib/api/contracts/tools/microsoft'
|
||||
import { parseRequest } from '@/lib/api/server'
|
||||
@@ -9,10 +10,12 @@ import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
import type { MicrosoftGraphDriveItem } from '@/tools/onedrive/types'
|
||||
import type { SharepointSkippedFile, SharepointUploadError } from '@/tools/sharepoint/types'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('SharepointUploadAPI')
|
||||
const MAX_SHAREPOINT_UPLOAD_BYTES = 250 * 1024 * 1024
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateRequestId()
|
||||
@@ -72,42 +75,11 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
)
|
||||
}
|
||||
|
||||
let effectiveDriveId = validatedData.driveId
|
||||
if (!effectiveDriveId) {
|
||||
logger.info(`[${requestId}] No driveId provided, fetching default drive for site`)
|
||||
const driveUrl = `https://graph.microsoft.com/v1.0/sites/${validatedData.siteId}/drive`
|
||||
const driveResponse = await secureFetchWithValidation(
|
||||
driveUrl,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
},
|
||||
'driveUrl'
|
||||
)
|
||||
|
||||
if (!driveResponse.ok) {
|
||||
const errorData = (await driveResponse.json().catch(() => ({}))) as {
|
||||
error?: { message?: string }
|
||||
}
|
||||
logger.error(`[${requestId}] Failed to get default drive:`, errorData)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorData.error?.message || 'Failed to get default document library',
|
||||
},
|
||||
{ status: driveResponse.status }
|
||||
)
|
||||
}
|
||||
|
||||
const driveData = (await driveResponse.json()) as { id: string }
|
||||
effectiveDriveId = driveData.id
|
||||
logger.info(`[${requestId}] Using default drive: ${effectiveDriveId}`)
|
||||
}
|
||||
|
||||
const uploadedFiles: any[] = []
|
||||
const siteId = validatedData.siteId.trim() || 'root'
|
||||
const driveId = validatedData.driveId?.trim() || null
|
||||
const uploadedFiles: MicrosoftGraphDriveItem[] = []
|
||||
const skippedFiles: SharepointSkippedFile[] = []
|
||||
const errors: SharepointUploadError[] = []
|
||||
|
||||
for (const userFile of userFiles) {
|
||||
logger.info(`[${requestId}] Uploading file: ${userFile.name}`)
|
||||
@@ -119,10 +91,16 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
|
||||
const fileSizeMB = buffer.length / (1024 * 1024)
|
||||
|
||||
if (fileSizeMB > 250) {
|
||||
if (buffer.length > MAX_SHAREPOINT_UPLOAD_BYTES) {
|
||||
logger.warn(
|
||||
`[${requestId}] File ${fileName} is ${fileSizeMB.toFixed(2)}MB, exceeds 250MB limit`
|
||||
)
|
||||
skippedFiles.push({
|
||||
name: fileName,
|
||||
size: buffer.length,
|
||||
limit: MAX_SHAREPOINT_UPLOAD_BYTES,
|
||||
reason: 'File exceeds the 250 MB Microsoft Graph small upload limit',
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -142,7 +120,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
.map((segment) => (segment ? encodeURIComponent(segment) : ''))
|
||||
.join('/')
|
||||
|
||||
const uploadUrl = `https://graph.microsoft.com/v1.0/sites/${validatedData.siteId}/drives/${effectiveDriveId}/root:${encodedPath}:/content`
|
||||
const uploadUrl = driveId
|
||||
? `https://graph.microsoft.com/v1.0/drives/${driveId}/root:${encodedPath}:/content`
|
||||
: `https://graph.microsoft.com/v1.0/sites/${siteId}/drive/root:${encodedPath}:/content`
|
||||
|
||||
logger.info(`[${requestId}] Uploading to: ${uploadUrl}`)
|
||||
|
||||
@@ -185,13 +165,12 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
error?: { message?: string }
|
||||
}
|
||||
logger.error(`[${requestId}] Failed to replace file ${fileName}:`, replaceErrorData)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: replaceErrorData.error?.message || `Failed to replace file: ${fileName}`,
|
||||
},
|
||||
{ status: replaceResponse.status }
|
||||
)
|
||||
errors.push({
|
||||
name: fileName,
|
||||
status: replaceResponse.status,
|
||||
error: replaceErrorData.error?.message || `Failed to replace file: ${fileName}`,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const replaceData = (await replaceResponse.json()) as {
|
||||
@@ -215,15 +194,14 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
continue
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error:
|
||||
(errorData as { error?: { message?: string } }).error?.message ||
|
||||
`Failed to upload file: ${fileName}`,
|
||||
},
|
||||
{ status: uploadResponse.status }
|
||||
)
|
||||
errors.push({
|
||||
name: fileName,
|
||||
status: uploadResponse.status,
|
||||
error:
|
||||
(errorData as { error?: { message?: string } }).error?.message ||
|
||||
`Failed to upload file: ${fileName}`,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const uploadData = (await uploadResponse.json()) as MicrosoftGraphDriveItem
|
||||
@@ -240,22 +218,33 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
}
|
||||
|
||||
if (uploadedFiles.length === 0) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'No files were uploaded successfully',
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'No files were uploaded successfully',
|
||||
output: {
|
||||
uploadedFiles,
|
||||
fileCount: 0,
|
||||
skippedFiles,
|
||||
skippedCount: skippedFiles.length,
|
||||
errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Successfully uploaded ${uploadedFiles.length} file(s)`)
|
||||
logger.info(`[${requestId}] Completed SharePoint upload`, {
|
||||
uploadedCount: uploadedFiles.length,
|
||||
skippedCount: skippedFiles.length,
|
||||
errorCount: errors.length,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
uploadedFiles,
|
||||
fileCount: uploadedFiles.length,
|
||||
skippedFiles,
|
||||
skippedCount: skippedFiles.length,
|
||||
errors,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -263,7 +252,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
||||
error: toError(error).message,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode, IntegrationType } from '@/blocks/types'
|
||||
import type { EmailBisonResponse } from '@/tools/emailbison/types'
|
||||
import { getTrigger } from '@/triggers'
|
||||
|
||||
const LEAD_MUTATION_OPERATIONS = ['create_lead', 'update_lead'] as const
|
||||
const LEAD_LIST_OPERATIONS = ['list_leads'] as const
|
||||
const CAMPAIGN_ID_OPERATIONS = [
|
||||
'update_campaign',
|
||||
'update_campaign_status',
|
||||
'attach_leads_to_campaign',
|
||||
] as const
|
||||
const REPLY_FILTER_OPERATIONS = ['list_replies'] as const
|
||||
const TAG_FILTER_OPERATIONS = ['list_leads', 'list_replies'] as const
|
||||
const EMAILBISON_TRIGGER_IDS = [
|
||||
'emailbison_email_sent',
|
||||
'emailbison_lead_first_contacted',
|
||||
'emailbison_lead_replied',
|
||||
'emailbison_lead_interested',
|
||||
'emailbison_lead_unsubscribed',
|
||||
'emailbison_untracked_reply_received',
|
||||
'emailbison_email_opened',
|
||||
'emailbison_email_bounced',
|
||||
'emailbison_email_account_added',
|
||||
'emailbison_email_account_removed',
|
||||
'emailbison_email_account_disconnected',
|
||||
'emailbison_email_account_reconnected',
|
||||
'emailbison_manual_email_sent',
|
||||
'emailbison_tag_attached',
|
||||
'emailbison_tag_removed',
|
||||
'emailbison_warmup_disabled_receiving_bounces',
|
||||
'emailbison_warmup_disabled_causing_bounces',
|
||||
] as const
|
||||
|
||||
export const EmailBisonBlock: BlockConfig<EmailBisonResponse> = {
|
||||
type: 'emailbison',
|
||||
name: 'Email Bison',
|
||||
description: 'Manage Email Bison leads, campaigns, replies, and tags',
|
||||
longDescription:
|
||||
'Integrate Email Bison into workflows. Create and update leads, manage campaigns, attach leads to campaigns, list replies, and organize leads with tags.',
|
||||
docsLink: 'https://docs.sim.ai/tools/emailbison',
|
||||
category: 'tools',
|
||||
integrationType: IntegrationType.Email,
|
||||
tags: ['sales-engagement', 'email-marketing', 'automation'],
|
||||
bgColor: '#FB7A22',
|
||||
icon: EmailBisonIcon,
|
||||
authMode: AuthMode.ApiKey,
|
||||
triggers: {
|
||||
enabled: true,
|
||||
available: [...EMAILBISON_TRIGGER_IDS],
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Leads', id: 'list_leads' },
|
||||
{ label: 'Get Lead', id: 'get_lead' },
|
||||
{ label: 'Create Lead', id: 'create_lead' },
|
||||
{ label: 'Update Lead', id: 'update_lead' },
|
||||
{ label: 'List Campaigns', id: 'list_campaigns' },
|
||||
{ label: 'Create Campaign', id: 'create_campaign' },
|
||||
{ label: 'Update Campaign', id: 'update_campaign' },
|
||||
{ label: 'Update Campaign Status', id: 'update_campaign_status' },
|
||||
{ label: 'Attach Leads to Campaign', id: 'attach_leads_to_campaign' },
|
||||
{ label: 'List Replies', id: 'list_replies' },
|
||||
{ label: 'List Tags', id: 'list_tags' },
|
||||
{ label: 'Create Tag', id: 'create_tag' },
|
||||
{ label: 'Attach Tags to Leads', id: 'attach_tags_to_leads' },
|
||||
],
|
||||
value: () => 'list_leads',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your Email Bison API token',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'apiBaseUrl',
|
||||
title: 'Instance URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://your-emailbison-workspace.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'leadId',
|
||||
title: 'Lead ID or Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'Lead ID or email address',
|
||||
required: { field: 'operation', value: ['get_lead', 'update_lead'] },
|
||||
condition: { field: 'operation', value: ['get_lead', 'update_lead'] },
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
title: 'Search',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search term',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [...LEAD_LIST_OPERATIONS, ...REPLY_FILTER_OPERATIONS],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'campaignStatus',
|
||||
title: 'Lead Campaign Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'In Sequence', id: 'in_sequence' },
|
||||
{ label: 'Sequence Finished', id: 'sequence_finished' },
|
||||
{ label: 'Sequence Stopped', id: 'sequence_stopped' },
|
||||
{ label: 'Never Contacted', id: 'never_contacted' },
|
||||
{ label: 'Replied', id: 'replied' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_leads' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'firstName',
|
||||
title: 'First Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'John',
|
||||
required: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
},
|
||||
{
|
||||
id: 'lastName',
|
||||
title: 'Last Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Doe',
|
||||
required: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
title: 'Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'john@example.com',
|
||||
required: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Engineer',
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'company',
|
||||
title: 'Company',
|
||||
type: 'short-input',
|
||||
placeholder: 'Acme Inc.',
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
title: 'Notes',
|
||||
type: 'long-input',
|
||||
placeholder: 'Additional notes',
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'customVariables',
|
||||
title: 'Custom Variables',
|
||||
type: 'long-input',
|
||||
placeholder: '[{"name":"linkedin_url","value":"https://linkedin.com/in/john"}]',
|
||||
condition: { field: 'operation', value: [...LEAD_MUTATION_OPERATIONS] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt:
|
||||
'Generate a JSON array of Email Bison custom variables. Each item must have name and value. Return ONLY the JSON array.',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'campaignId',
|
||||
title: 'Campaign ID',
|
||||
type: 'short-input',
|
||||
placeholder: '123',
|
||||
required: { field: 'operation', value: [...CAMPAIGN_ID_OPERATIONS] },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [...CAMPAIGN_ID_OPERATIONS, ...REPLY_FILTER_OPERATIONS],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'campaignName',
|
||||
title: 'Campaign Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Outbound Campaign',
|
||||
required: { field: 'operation', value: 'create_campaign' },
|
||||
condition: { field: 'operation', value: ['create_campaign', 'update_campaign'] },
|
||||
},
|
||||
{
|
||||
id: 'campaignType',
|
||||
title: 'Campaign Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Outbound', id: 'outbound' },
|
||||
{ label: 'Reply Follow-up', id: 'reply_followup' },
|
||||
],
|
||||
value: () => 'outbound',
|
||||
condition: { field: 'operation', value: 'create_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'action',
|
||||
title: 'Status Action',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Pause', id: 'pause' },
|
||||
{ label: 'Resume', id: 'resume' },
|
||||
{ label: 'Archive', id: 'archive' },
|
||||
],
|
||||
value: () => 'pause',
|
||||
required: { field: 'operation', value: 'update_campaign_status' },
|
||||
condition: { field: 'operation', value: 'update_campaign_status' },
|
||||
},
|
||||
{
|
||||
id: 'maxEmailsPerDay',
|
||||
title: 'Max Emails Per Day',
|
||||
type: 'short-input',
|
||||
placeholder: '500',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'maxNewLeadsPerDay',
|
||||
title: 'Max New Leads Per Day',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'sequencePrioritization',
|
||||
title: 'Sequence Prioritization',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Follow-ups', id: 'followups' },
|
||||
{ label: 'New Leads', id: 'new_leads' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'plainText',
|
||||
title: 'Plain Text',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'openTracking',
|
||||
title: 'Open Tracking',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'reputationBuilding',
|
||||
title: 'Reputation Building',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'canUnsubscribe',
|
||||
title: 'Can Unsubscribe',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'includeAutoRepliesInStats',
|
||||
title: 'Include Auto Replies in Stats',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Unchanged', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'leadIds',
|
||||
title: 'Lead IDs',
|
||||
type: 'short-input',
|
||||
placeholder: '1,2,3',
|
||||
required: { field: 'operation', value: ['attach_leads_to_campaign', 'attach_tags_to_leads'] },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['attach_leads_to_campaign', 'attach_tags_to_leads'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'allowParallelSending',
|
||||
title: 'Allow Parallel Sending',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'attach_leads_to_campaign' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'replyStatus',
|
||||
title: 'Reply Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'Interested', id: 'interested' },
|
||||
{ label: 'Automated Reply', id: 'automated_reply' },
|
||||
{ label: 'Not Automated Reply', id: 'not_automated_reply' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_replies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'folder',
|
||||
title: 'Folder',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'Inbox', id: 'inbox' },
|
||||
{ label: 'Sent', id: 'sent' },
|
||||
{ label: 'Spam', id: 'spam' },
|
||||
{ label: 'Bounced', id: 'bounced' },
|
||||
{ label: 'All', id: 'all' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_replies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'read',
|
||||
title: 'Read',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'Read', id: 'true' },
|
||||
{ label: 'Unread', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_replies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'senderEmailId',
|
||||
title: 'Sender Email ID',
|
||||
type: 'short-input',
|
||||
placeholder: '243',
|
||||
condition: { field: 'operation', value: 'list_replies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'replyLeadId',
|
||||
title: 'Lead ID',
|
||||
type: 'short-input',
|
||||
placeholder: '14',
|
||||
condition: { field: 'operation', value: 'list_replies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tagName',
|
||||
title: 'Tag Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Interested',
|
||||
required: { field: 'operation', value: 'create_tag' },
|
||||
condition: { field: 'operation', value: 'create_tag' },
|
||||
},
|
||||
{
|
||||
id: 'tagIds',
|
||||
title: 'Tag IDs',
|
||||
type: 'short-input',
|
||||
placeholder: '1,2,3',
|
||||
required: { field: 'operation', value: 'attach_tags_to_leads' },
|
||||
condition: { field: 'operation', value: 'attach_tags_to_leads' },
|
||||
},
|
||||
{
|
||||
id: 'filterTagIds',
|
||||
title: 'Filter Tag IDs',
|
||||
type: 'short-input',
|
||||
placeholder: '1,2,3',
|
||||
condition: { field: 'operation', value: [...TAG_FILTER_OPERATIONS] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'excludedTagIds',
|
||||
title: 'Excluded Tag IDs',
|
||||
type: 'short-input',
|
||||
placeholder: '4,5,6',
|
||||
condition: { field: 'operation', value: 'list_leads' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'withoutTags',
|
||||
title: 'Without Tags',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'list_leads' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'skipWebhooks',
|
||||
title: 'Skip Webhooks',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'attach_tags_to_leads' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
...EMAILBISON_TRIGGER_IDS.flatMap((triggerId) => getTrigger(triggerId).subBlocks),
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'emailbison_list_leads',
|
||||
'emailbison_get_lead',
|
||||
'emailbison_create_lead',
|
||||
'emailbison_update_lead',
|
||||
'emailbison_list_campaigns',
|
||||
'emailbison_create_campaign',
|
||||
'emailbison_update_campaign',
|
||||
'emailbison_update_campaign_status',
|
||||
'emailbison_attach_leads_to_campaign',
|
||||
'emailbison_list_replies',
|
||||
'emailbison_list_tags',
|
||||
'emailbison_create_tag',
|
||||
'emailbison_attach_tags_to_leads',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `emailbison_${params.operation}`,
|
||||
params: (params) => ({
|
||||
apiBaseUrl: params.apiBaseUrl,
|
||||
leadId:
|
||||
params.operation === 'list_replies' ? toNumberParam(params.replyLeadId) : params.leadId,
|
||||
leadIds: parseNumberList(params.leadIds),
|
||||
tagIds: parseNumberList(
|
||||
params.operation === 'attach_tags_to_leads' ? params.tagIds : params.filterTagIds
|
||||
),
|
||||
excludedTagIds: parseNumberList(params.excludedTagIds),
|
||||
customVariables: parseJsonArray(params.customVariables),
|
||||
campaignId: toNumberParam(params.campaignId),
|
||||
campaignType: emptyToUndefined(params.campaignType),
|
||||
name:
|
||||
params.operation === 'create_tag' || params.operation === 'create_campaign'
|
||||
? params.operation === 'create_tag'
|
||||
? params.tagName
|
||||
: params.campaignName
|
||||
: emptyToUndefined(params.campaignName),
|
||||
action: params.action,
|
||||
maxEmailsPerDay: toNumberParam(params.maxEmailsPerDay),
|
||||
maxNewLeadsPerDay: toNumberParam(params.maxNewLeadsPerDay),
|
||||
sequencePrioritization: emptyToUndefined(params.sequencePrioritization),
|
||||
plainText: toBooleanParam(params.plainText),
|
||||
openTracking: toBooleanParam(params.openTracking),
|
||||
reputationBuilding: toBooleanParam(params.reputationBuilding),
|
||||
canUnsubscribe: toBooleanParam(params.canUnsubscribe),
|
||||
includeAutoRepliesInStats: toBooleanParam(params.includeAutoRepliesInStats),
|
||||
allowParallelSending: toBooleanParam(params.allowParallelSending),
|
||||
skipWebhooks: toBooleanParam(params.skipWebhooks),
|
||||
withoutTags: toBooleanParam(params.withoutTags),
|
||||
read: toBooleanParam(params.read),
|
||||
senderEmailId: toNumberParam(params.senderEmailId),
|
||||
campaignStatus: emptyToUndefined(params.campaignStatus),
|
||||
status:
|
||||
params.operation === 'list_replies' ? emptyToUndefined(params.replyStatus) : undefined,
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Email Bison API token' },
|
||||
apiBaseUrl: { type: 'string', description: 'Email Bison instance URL' },
|
||||
leadId: { type: 'string', description: 'Lead ID or email address' },
|
||||
search: { type: 'string', description: 'Search term' },
|
||||
campaignStatus: { type: 'string', description: 'Lead campaign status filter' },
|
||||
firstName: { type: 'string', description: 'Lead first name' },
|
||||
lastName: { type: 'string', description: 'Lead last name' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
title: { type: 'string', description: 'Lead title' },
|
||||
company: { type: 'string', description: 'Lead company' },
|
||||
notes: { type: 'string', description: 'Lead notes' },
|
||||
customVariables: { type: 'array', description: 'Lead custom variables' },
|
||||
campaignId: { type: 'number', description: 'Campaign ID' },
|
||||
campaignName: { type: 'string', description: 'Campaign name' },
|
||||
campaignType: { type: 'string', description: 'Campaign type' },
|
||||
action: { type: 'string', description: 'Campaign status action' },
|
||||
maxEmailsPerDay: { type: 'number', description: 'Maximum emails per day' },
|
||||
maxNewLeadsPerDay: { type: 'number', description: 'Maximum new leads per day' },
|
||||
sequencePrioritization: { type: 'string', description: 'Campaign sequence prioritization' },
|
||||
plainText: { type: 'boolean', description: 'Whether campaign emails should be plain text' },
|
||||
openTracking: { type: 'boolean', description: 'Whether open tracking should be enabled' },
|
||||
reputationBuilding: { type: 'boolean', description: 'Whether reputation building is enabled' },
|
||||
canUnsubscribe: { type: 'boolean', description: 'Whether recipients can unsubscribe' },
|
||||
includeAutoRepliesInStats: {
|
||||
type: 'boolean',
|
||||
description: 'Whether auto replies are included in campaign stats',
|
||||
},
|
||||
leadIds: { type: 'array', description: 'Lead IDs' },
|
||||
allowParallelSending: { type: 'boolean', description: 'Allow parallel sending' },
|
||||
replyStatus: { type: 'string', description: 'Reply status filter' },
|
||||
folder: { type: 'string', description: 'Reply folder filter' },
|
||||
read: { type: 'boolean', description: 'Reply read filter' },
|
||||
senderEmailId: { type: 'number', description: 'Sender email ID' },
|
||||
replyLeadId: { type: 'number', description: 'Reply lead ID filter' },
|
||||
tagName: { type: 'string', description: 'Tag name' },
|
||||
tagIds: { type: 'array', description: 'Tag IDs' },
|
||||
filterTagIds: { type: 'array', description: 'Tag IDs to filter by' },
|
||||
excludedTagIds: { type: 'array', description: 'Excluded tag IDs' },
|
||||
withoutTags: { type: 'boolean', description: 'Only include leads without tags' },
|
||||
skipWebhooks: { type: 'boolean', description: 'Skip Email Bison webhooks' },
|
||||
},
|
||||
outputs: {
|
||||
leads: { type: 'array', description: 'List of leads' },
|
||||
campaigns: { type: 'array', description: 'List of campaigns' },
|
||||
replies: { type: 'array', description: 'List of replies' },
|
||||
tags: { type: 'array', description: 'List of tags' },
|
||||
count: { type: 'number', description: 'Number of returned records' },
|
||||
id: { type: 'number', description: 'Record ID' },
|
||||
uuid: { type: 'string', description: 'Record UUID' },
|
||||
name: { type: 'string', description: 'Campaign or tag name' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
status: { type: 'string', description: 'Record status' },
|
||||
success: { type: 'boolean', description: 'Whether the action succeeded' },
|
||||
message: { type: 'string', description: 'Action message' },
|
||||
},
|
||||
}
|
||||
|
||||
function parseNumberList(value: unknown): number[] | undefined {
|
||||
if (Array.isArray(value)) {
|
||||
const numbers = value.map(toNumberParam).filter((number) => number !== undefined)
|
||||
return numbers.length > 0 ? numbers : undefined
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') return undefined
|
||||
|
||||
const numbers = value
|
||||
.split(/[\s,]+/)
|
||||
.map(toNumberParam)
|
||||
.filter((number) => number !== undefined)
|
||||
|
||||
return numbers.length > 0 ? numbers : undefined
|
||||
}
|
||||
|
||||
function parseJsonArray(value: unknown): unknown[] | undefined {
|
||||
if (Array.isArray(value)) return value
|
||||
if (typeof value !== 'string' || value.trim() === '') return undefined
|
||||
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(value)
|
||||
return Array.isArray(parsed) ? parsed : undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function toNumberParam(value: unknown): number | undefined {
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
|
||||
if (typeof value !== 'string' || value.trim() === '') return undefined
|
||||
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : undefined
|
||||
}
|
||||
|
||||
function toBooleanParam(value: unknown): boolean | undefined {
|
||||
if (typeof value === 'boolean') return value
|
||||
if (typeof value !== 'string' || value.trim() === '') return undefined
|
||||
if (value === 'true') return true
|
||||
if (value === 'false') return false
|
||||
return undefined
|
||||
}
|
||||
|
||||
function emptyToUndefined(value: unknown): unknown {
|
||||
return value === '' ? undefined : value
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
|
||||
name: 'Sharepoint',
|
||||
description: 'Work with pages and lists',
|
||||
authMode: AuthMode.OAuth,
|
||||
hideFromToolbar: true,
|
||||
longDescription:
|
||||
'Integrate SharePoint into the workflow. Read/create pages, list sites, and work with lists (read, create, update items). Requires OAuth.',
|
||||
docsLink: 'https://docs.sim.ai/tools/sharepoint',
|
||||
@@ -567,3 +568,485 @@ Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const SHAREPOINT_V2_TOOL_IDS = [
|
||||
'sharepoint_create_page',
|
||||
'sharepoint_read_page',
|
||||
'sharepoint_list_sites',
|
||||
'sharepoint_create_list',
|
||||
'sharepoint_get_list',
|
||||
'sharepoint_update_list',
|
||||
'sharepoint_add_list_items',
|
||||
'sharepoint_upload_file',
|
||||
] as const
|
||||
|
||||
const SHAREPOINT_V2_SITE_OPERATIONS = Array.from(SHAREPOINT_V2_TOOL_IDS)
|
||||
|
||||
const SHAREPOINT_V2_LIST_ITEM_OPERATIONS = [
|
||||
'sharepoint_update_list',
|
||||
'sharepoint_add_list_items',
|
||||
] as const
|
||||
|
||||
export const SharepointV2Block: BlockConfig<SharepointResponse> = {
|
||||
...SharepointBlock,
|
||||
type: 'sharepoint_v2',
|
||||
name: 'SharePoint',
|
||||
hideFromToolbar: false,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create Page', id: 'sharepoint_create_page' },
|
||||
{ label: 'Read Page', id: 'sharepoint_read_page' },
|
||||
{ label: 'List Sites', id: 'sharepoint_list_sites' },
|
||||
{ label: 'Create List', id: 'sharepoint_create_list' },
|
||||
{ label: 'Read List', id: 'sharepoint_get_list' },
|
||||
{ label: 'Update List Item', id: 'sharepoint_update_list' },
|
||||
{ label: 'Add List Item', id: 'sharepoint_add_list_items' },
|
||||
{ label: 'Upload File', id: 'sharepoint_upload_file' },
|
||||
],
|
||||
value: () => 'sharepoint_create_page',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'basic',
|
||||
serviceId: 'sharepoint',
|
||||
requiredScopes: getScopesForService('sharepoint'),
|
||||
placeholder: 'Select Microsoft account',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'manualCredential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'advanced',
|
||||
placeholder: 'Enter credential ID',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'siteSelector',
|
||||
title: 'Select Site',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'siteId',
|
||||
serviceId: 'sharepoint',
|
||||
selectorKey: 'sharepoint.sites',
|
||||
requiredScopes: getScopesForService('sharepoint'),
|
||||
mimeType: 'application/vnd.microsoft.graph.site',
|
||||
placeholder: 'Select a site',
|
||||
dependsOn: ['credential'],
|
||||
mode: 'basic',
|
||||
condition: { field: 'operation', value: SHAREPOINT_V2_SITE_OPERATIONS },
|
||||
},
|
||||
{
|
||||
id: 'manualSiteId',
|
||||
title: 'Site ID',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'siteId',
|
||||
placeholder: 'Enter site ID (leave empty for root site)',
|
||||
dependsOn: ['credential'],
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: SHAREPOINT_V2_SITE_OPERATIONS },
|
||||
},
|
||||
{
|
||||
id: 'pageName',
|
||||
title: 'Page Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Name of the page',
|
||||
condition: { field: 'operation', value: ['sharepoint_create_page', 'sharepoint_read_page'] },
|
||||
required: { field: 'operation', value: 'sharepoint_create_page' },
|
||||
},
|
||||
{
|
||||
id: 'pageId',
|
||||
title: 'Page ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page ID (alternative to page name)',
|
||||
condition: { field: 'operation', value: 'sharepoint_read_page' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'pageTitle',
|
||||
title: 'Page Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional title (defaults to page name)',
|
||||
condition: { field: 'operation', value: 'sharepoint_create_page' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'pageContent',
|
||||
title: 'Page Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Optional text content for the page',
|
||||
condition: { field: 'operation', value: 'sharepoint_create_page' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'maxPages',
|
||||
title: 'Max Pages',
|
||||
type: 'short-input',
|
||||
placeholder: 'Default 10, maximum 50',
|
||||
condition: { field: 'operation', value: 'sharepoint_read_page' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'groupId',
|
||||
title: 'Group ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional Microsoft 365 group ID',
|
||||
condition: { field: 'operation', value: 'sharepoint_list_sites' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'listSelector',
|
||||
title: 'List',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'listId',
|
||||
serviceId: 'sharepoint',
|
||||
selectorKey: 'sharepoint.lists',
|
||||
placeholder: 'Select a list',
|
||||
dependsOn: ['credential', 'siteSelector'],
|
||||
mode: 'basic',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['sharepoint_get_list', ...SHAREPOINT_V2_LIST_ITEM_OPERATIONS],
|
||||
},
|
||||
required: { field: 'operation', value: [...SHAREPOINT_V2_LIST_ITEM_OPERATIONS] },
|
||||
},
|
||||
{
|
||||
id: 'manualListId',
|
||||
title: 'List ID',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'listId',
|
||||
placeholder: 'Enter list ID (GUID). Required for list item operations.',
|
||||
mode: 'advanced',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['sharepoint_get_list', ...SHAREPOINT_V2_LIST_ITEM_OPERATIONS],
|
||||
},
|
||||
required: { field: 'operation', value: [...SHAREPOINT_V2_LIST_ITEM_OPERATIONS] },
|
||||
},
|
||||
{
|
||||
id: 'includeColumns',
|
||||
title: 'Include Columns',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'sharepoint_get_list' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'includeItems',
|
||||
title: 'Include Items',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: 'sharepoint_get_list' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'listDisplayName',
|
||||
title: 'List Display Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Name of the list',
|
||||
condition: { field: 'operation', value: 'sharepoint_create_list' },
|
||||
required: { field: 'operation', value: 'sharepoint_create_list' },
|
||||
},
|
||||
{
|
||||
id: 'listTemplate',
|
||||
title: 'List Template',
|
||||
type: 'short-input',
|
||||
placeholder: "Template (e.g., 'genericList')",
|
||||
condition: { field: 'operation', value: 'sharepoint_create_list' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a SharePoint list template name based on the user's description.
|
||||
|
||||
### AVAILABLE TEMPLATES
|
||||
- genericList - Standard list for general data (default)
|
||||
- documentLibrary - For storing and managing documents
|
||||
- survey - For creating surveys and polls
|
||||
- links - For storing hyperlinks
|
||||
- announcements - For news and announcements
|
||||
- contacts - For contact information (name, email, phone)
|
||||
- events - For calendar events and scheduling
|
||||
- tasks - For task tracking and project management
|
||||
- discussionBoard - For team discussions and forums
|
||||
- pictureLibrary - For storing images and photos
|
||||
- issue - For issue/bug tracking
|
||||
|
||||
Return ONLY the template name - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe what kind of list you need...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'columnDefinitions',
|
||||
title: 'Column Definitions',
|
||||
type: 'long-input',
|
||||
placeholder: 'Optional: Define custom columns as JSON array',
|
||||
condition: { field: 'operation', value: 'sharepoint_create_list' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON array of SharePoint list column definitions based on the user's description.
|
||||
|
||||
Each column needs at minimum a "name" and column type properties, for example:
|
||||
[{"name": "Status", "choice": {"choices": ["Active", "Completed"]}}, {"name": "DueDate", "dateTime": {"format": "dateOnly"}}]
|
||||
|
||||
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the columns you want to add...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'listDescription',
|
||||
title: 'List Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Optional description',
|
||||
condition: { field: 'operation', value: 'sharepoint_create_list' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'itemId',
|
||||
title: 'Item ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter item ID',
|
||||
condition: { field: 'operation', value: 'sharepoint_update_list' },
|
||||
required: { field: 'operation', value: 'sharepoint_update_list' },
|
||||
},
|
||||
{
|
||||
id: 'listItemFields',
|
||||
title: 'List Item Fields',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'Enter list item fields as JSON (e.g., {"Title": "My Item", "Status": "Active"})',
|
||||
condition: { field: 'operation', value: [...SHAREPOINT_V2_LIST_ITEM_OPERATIONS] },
|
||||
required: { field: 'operation', value: [...SHAREPOINT_V2_LIST_ITEM_OPERATIONS] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON object for SharePoint list item fields based on the user's description.
|
||||
|
||||
Use column internal names as keys and valid field values as values.
|
||||
|
||||
Return ONLY the JSON object - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the fields and values you want to set...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'driveId',
|
||||
title: 'Document Library ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter document library (drive) ID',
|
||||
condition: { field: 'operation', value: 'sharepoint_upload_file' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'folderPath',
|
||||
title: 'Folder Path',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional folder path (e.g., /Documents/Subfolder)',
|
||||
condition: { field: 'operation', value: 'sharepoint_upload_file' },
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional: override uploaded file name',
|
||||
condition: { field: 'operation', value: 'sharepoint_upload_file' },
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'uploadFiles',
|
||||
title: 'Files',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Upload files to SharePoint',
|
||||
condition: { field: 'operation', value: 'sharepoint_upload_file' },
|
||||
mode: 'basic',
|
||||
multiple: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'fileRefs',
|
||||
title: 'Files',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'files',
|
||||
placeholder: 'Reference files from previous blocks',
|
||||
condition: { field: 'operation', value: 'sharepoint_upload_file' },
|
||||
mode: 'advanced',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'nextPageUrl',
|
||||
title: 'Next Page URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'Paste the @odata.nextLink URL from a previous result',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['sharepoint_read_page', 'sharepoint_list_sites', 'sharepoint_get_list'],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [...SHAREPOINT_V2_TOOL_IDS],
|
||||
config: {
|
||||
tool: (params) => params.operation || 'sharepoint_create_page',
|
||||
params: (params) => {
|
||||
const {
|
||||
oauthCredential,
|
||||
siteId,
|
||||
listId,
|
||||
itemId,
|
||||
includeColumns,
|
||||
includeItems,
|
||||
columnDefinitions,
|
||||
listItemFields,
|
||||
files,
|
||||
maxPages,
|
||||
driveId,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
const cleanString = (value: unknown) =>
|
||||
value === undefined || value === null ? undefined : String(value).trim() || undefined
|
||||
const coerceBoolean = (value: unknown) => {
|
||||
if (typeof value === 'boolean') return value
|
||||
if (typeof value === 'string') return value.toLowerCase() === 'true'
|
||||
return undefined
|
||||
}
|
||||
const parseJsonObject = (value: unknown) => {
|
||||
if (typeof value !== 'string') return value
|
||||
if (!value.trim()) return undefined
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (error) {
|
||||
logger.error('Failed to parse SharePoint JSON input', {
|
||||
error: toError(error).message,
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedFiles = normalizeFileInput(files)
|
||||
const result: Record<string, any> = {
|
||||
...rest,
|
||||
oauthCredential,
|
||||
siteId: cleanString(siteId),
|
||||
listId: cleanString(listId),
|
||||
itemId: cleanString(itemId),
|
||||
driveId: cleanString(driveId),
|
||||
includeColumns: coerceBoolean(includeColumns),
|
||||
includeItems: coerceBoolean(includeItems),
|
||||
listItemFields: parseJsonObject(listItemFields),
|
||||
maxPages: maxPages ? Number.parseInt(String(maxPages), 10) : undefined,
|
||||
}
|
||||
|
||||
if (columnDefinitions) {
|
||||
result.pageContent = columnDefinitions
|
||||
}
|
||||
if (normalizedFiles) {
|
||||
result.files = normalizedFiles
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
oauthCredential: { type: 'string', description: 'Microsoft account credential' },
|
||||
siteId: { type: 'string', description: 'SharePoint site ID' },
|
||||
pageName: { type: 'string', description: 'Page name' },
|
||||
pageTitle: { type: 'string', description: 'Page title' },
|
||||
pageContent: { type: 'string', description: 'Page text content' },
|
||||
pageId: { type: 'string', description: 'Page ID' },
|
||||
maxPages: { type: 'number', description: 'Maximum pages to return' },
|
||||
nextPageUrl: { type: 'string', description: 'Microsoft Graph @odata.nextLink URL' },
|
||||
groupId: { type: 'string', description: 'Microsoft 365 group ID' },
|
||||
listId: { type: 'string', description: 'List ID' },
|
||||
includeColumns: { type: 'boolean', description: 'Include columns in response' },
|
||||
includeItems: { type: 'boolean', description: 'Include items in response' },
|
||||
listDisplayName: { type: 'string', description: 'List display name' },
|
||||
listDescription: { type: 'string', description: 'List description' },
|
||||
listTemplate: { type: 'string', description: 'List template' },
|
||||
columnDefinitions: {
|
||||
type: 'json',
|
||||
description: 'Column definitions for list creation (JSON array)',
|
||||
},
|
||||
itemId: { type: 'string', description: 'List item ID' },
|
||||
listItemFields: { type: 'json', description: 'List item fields' },
|
||||
driveId: { type: 'string', description: 'Document library (drive) ID' },
|
||||
folderPath: { type: 'string', description: 'Folder path for file upload' },
|
||||
fileName: { type: 'string', description: 'File name override' },
|
||||
files: { type: 'json', description: 'Files to upload' },
|
||||
},
|
||||
outputs: {
|
||||
site: {
|
||||
type: 'json',
|
||||
description: 'SharePoint site object (id, name, displayName, webUrl, description)',
|
||||
},
|
||||
sites: {
|
||||
type: 'json',
|
||||
description: 'Array of SharePoint site objects (id, name, displayName, webUrl)',
|
||||
},
|
||||
page: {
|
||||
type: 'json',
|
||||
description: 'SharePoint page object (id, name, title, webUrl, pageLayout, description)',
|
||||
},
|
||||
pages: {
|
||||
type: 'json',
|
||||
description: 'Array of SharePoint pages with page metadata and extracted content',
|
||||
},
|
||||
content: {
|
||||
type: 'json',
|
||||
description: 'SharePoint page content (content, canvasLayout)',
|
||||
},
|
||||
totalPages: { type: 'number', description: 'Number of pages returned' },
|
||||
list: {
|
||||
type: 'json',
|
||||
description: 'SharePoint list object (id, displayName, name, webUrl, columns, items)',
|
||||
},
|
||||
lists: {
|
||||
type: 'json',
|
||||
description: 'Array of SharePoint lists (id, displayName, name, webUrl, list)',
|
||||
},
|
||||
item: { type: 'json', description: 'SharePoint list item with fields' },
|
||||
items: { type: 'json', description: 'Array of SharePoint list items with fields' },
|
||||
uploadedFiles: {
|
||||
type: 'json',
|
||||
description: 'Array of uploaded file objects with id, name, webUrl, size',
|
||||
},
|
||||
fileCount: { type: 'number', description: 'Number of files uploaded' },
|
||||
skippedFiles: {
|
||||
type: 'json',
|
||||
description: 'Array of skipped upload files (name, size, limit, reason)',
|
||||
},
|
||||
skippedCount: { type: 'number', description: 'Number of files skipped' },
|
||||
errors: {
|
||||
type: 'json',
|
||||
description: 'Array of per-file upload errors (name, error, status)',
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
description: 'Microsoft Graph @odata.nextLink URL for the next page of results',
|
||||
},
|
||||
success: { type: 'boolean', description: 'Success status' },
|
||||
error: { type: 'string', description: 'Error message' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import { DuckDuckGoBlock } from '@/blocks/blocks/duckduckgo'
|
||||
import { DynamoDBBlock } from '@/blocks/blocks/dynamodb'
|
||||
import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
|
||||
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
|
||||
import { EmailBisonBlock } from '@/blocks/blocks/emailbison'
|
||||
import { EnrichBlock } from '@/blocks/blocks/enrich'
|
||||
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
|
||||
import { EvernoteBlock } from '@/blocks/blocks/evernote'
|
||||
@@ -180,7 +181,7 @@ import { SerperBlock } from '@/blocks/blocks/serper'
|
||||
import { ServiceNowBlock } from '@/blocks/blocks/servicenow'
|
||||
import { SESBlock } from '@/blocks/blocks/ses'
|
||||
import { SftpBlock } from '@/blocks/blocks/sftp'
|
||||
import { SharepointBlock } from '@/blocks/blocks/sharepoint'
|
||||
import { SharepointBlock, SharepointV2Block } from '@/blocks/blocks/sharepoint'
|
||||
import { ShopifyBlock } from '@/blocks/blocks/shopify'
|
||||
import { SimilarwebBlock } from '@/blocks/blocks/similarweb'
|
||||
import { SixtyfourBlock } from '@/blocks/blocks/sixtyfour'
|
||||
@@ -283,6 +284,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
dub: DubBlock,
|
||||
duckduckgo: DuckDuckGoBlock,
|
||||
dynamodb: DynamoDBBlock,
|
||||
emailbison: EmailBisonBlock,
|
||||
elasticsearch: ElasticsearchBlock,
|
||||
elevenlabs: ElevenLabsBlock,
|
||||
fathom: FathomBlock,
|
||||
@@ -431,6 +433,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
servicenow: ServiceNowBlock,
|
||||
sftp: SftpBlock,
|
||||
sharepoint: SharepointBlock,
|
||||
sharepoint_v2: SharepointV2Block,
|
||||
shopify: ShopifyBlock,
|
||||
similarweb: SimilarwebBlock,
|
||||
sixtyfour: SixtyfourBlock,
|
||||
|
||||
@@ -415,6 +415,17 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function EmailBisonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='-66 -2 88 64' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M0 0 C1 2.3 2 4.6 3 7 C7 8.8 7 8.8 11.7 9.9 C13.2 10.3 14.8 10.7 16.4 11.2 C17.6 11.4 18.8 11.7 20 12 C21.5 38 21.5 38 15.8 45.1 C11 49 11 49 7 50 C5.3 49.3 3.7 48.7 2 48 C2 46 2 44 2 42 C0.7 42.3 -0.6 42.7 -2 43 C-1 49.1 0.8 54.3 3 60 C-1 61 -1 61 -7 58 C-7 56.3 -7 54.7 -7 53 C-7.7 54 -8.3 55 -9 56 C-9.7 56.8 -9.7 56.8 -13 61 C-18 60 -18 60 -21 57 C-21.2 56.1 -21.2 56.1 -22 51.3 C-26.1 42.7 -31.1 41.6 -39.7 38.3 C-44 37 -44 37 -47 37 C-53.6 45.2 -56 50.5 -55 61 C-57 60.7 -59 60.3 -61 60 C-65.9 51.1 -63 42.4 -61 33 C-62 34 -63 35 -64 36 C-64.7 36 -65.3 36 -66 36 C-64.4 29.7 -62.6 24 -60 18 C-56.3 16 -52.5 14 -48.6 12.2 C-44 10 -44 10 -39.6 7.3 C-26.4 -0.8 -15.2 -1.1 0 0 Z M-61 27 C-61.3 28 -61.7 29 -62 30 C-61.3 30 -60.7 30 -60 30 C-60.3 29 -60.7 28 -61 27 Z M-7 50 C-6 52 -6 52 -6 52 Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function MailServerIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/provider-subscription-utils'
|
||||
import type {
|
||||
DeleteSubscriptionContext,
|
||||
EventMatchContext,
|
||||
FormatInputContext,
|
||||
FormatInputResult,
|
||||
SubscriptionContext,
|
||||
SubscriptionResult,
|
||||
WebhookProviderHandler,
|
||||
} from '@/lib/webhooks/providers/types'
|
||||
import { emailBisonHeaders, emailBisonUrl } from '@/tools/emailbison/utils'
|
||||
|
||||
const logger = createLogger('WebhookProvider:EmailBison')
|
||||
|
||||
export const emailBisonHandler: WebhookProviderHandler = {
|
||||
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}] Email Bison webhook payload was not an object`)
|
||||
return false
|
||||
}
|
||||
|
||||
const { isEmailBisonEventMatch } = await import('@/triggers/emailbison/utils')
|
||||
if (!isEmailBisonEventMatch(triggerId, unwrapEmailBisonPayload(body))) {
|
||||
logger.info(`[${requestId}] Email Bison event did not match trigger`, {
|
||||
triggerId,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async formatInput({ body, webhook }: FormatInputContext): Promise<FormatInputResult> {
|
||||
const payload = isRecord(body) ? unwrapEmailBisonPayload(body) : {}
|
||||
const event = isRecord(payload.event) ? payload.event : null
|
||||
const data = isRecord(payload.data) ? payload.data : null
|
||||
const providerConfig = getProviderConfig(webhook)
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
const input: Record<string, unknown> = {
|
||||
eventType: toStringOrNull(event?.type),
|
||||
eventName: toStringOrNull(event?.name),
|
||||
instanceUrl: toStringOrNull(event?.instance_url),
|
||||
workspaceId: toNumberOrNull(event?.workspace_id),
|
||||
workspaceName: toStringOrNull(event?.workspace_name),
|
||||
event,
|
||||
data,
|
||||
}
|
||||
|
||||
if (
|
||||
triggerId === 'emailbison_email_sent' ||
|
||||
triggerId === 'emailbison_lead_first_contacted' ||
|
||||
triggerId === 'emailbison_lead_unsubscribed' ||
|
||||
triggerId === 'emailbison_email_opened'
|
||||
) {
|
||||
input.scheduledEmail = toRecordOrNull(data?.scheduled_email)
|
||||
input.campaignEvent = renameTypeField(data?.campaign_event, 'event_type')
|
||||
input.lead = toRecordOrNull(data?.lead)
|
||||
input.campaign = toRecordOrNull(data?.campaign)
|
||||
input.senderEmail = renameTypeField(data?.sender_email, 'account_type')
|
||||
}
|
||||
|
||||
if (
|
||||
triggerId === 'emailbison_lead_replied' ||
|
||||
triggerId === 'emailbison_lead_interested' ||
|
||||
triggerId === 'emailbison_email_bounced'
|
||||
) {
|
||||
input.reply = renameTypeField(data?.reply, 'reply_type')
|
||||
input.campaignEvent = renameTypeField(data?.campaign_event, 'event_type')
|
||||
input.lead = toRecordOrNull(data?.lead)
|
||||
input.campaign = toRecordOrNull(data?.campaign)
|
||||
input.scheduledEmail = toRecordOrNull(data?.scheduled_email)
|
||||
input.senderEmail = renameTypeField(data?.sender_email, 'account_type')
|
||||
}
|
||||
|
||||
if (triggerId === 'emailbison_untracked_reply_received') {
|
||||
input.reply = renameTypeField(data?.reply, 'reply_type')
|
||||
input.senderEmail = renameTypeField(data?.sender_email, 'account_type')
|
||||
}
|
||||
|
||||
if (
|
||||
triggerId === 'emailbison_email_account_added' ||
|
||||
triggerId === 'emailbison_email_account_removed' ||
|
||||
triggerId === 'emailbison_email_account_disconnected' ||
|
||||
triggerId === 'emailbison_email_account_reconnected' ||
|
||||
triggerId === 'emailbison_warmup_disabled_receiving_bounces' ||
|
||||
triggerId === 'emailbison_warmup_disabled_causing_bounces'
|
||||
) {
|
||||
input.senderEmail = renameTypeField(data?.sender_email, 'account_type')
|
||||
}
|
||||
|
||||
if (triggerId === 'emailbison_manual_email_sent') {
|
||||
input.reply = renameTypeField(data?.reply, 'reply_type')
|
||||
input.lead = toRecordOrNull(data?.lead)
|
||||
input.campaign = toRecordOrNull(data?.campaign)
|
||||
input.scheduledEmail = toRecordOrNull(data?.scheduled_email)
|
||||
input.senderEmail = renameTypeField(data?.sender_email, 'account_type')
|
||||
}
|
||||
|
||||
if (triggerId === 'emailbison_tag_attached' || triggerId === 'emailbison_tag_removed') {
|
||||
input.tagId = toNumberOrNull(data?.tag_id)
|
||||
input.tagName = toStringOrNull(data?.tag_name)
|
||||
input.taggableId = toNumberOrNull(data?.taggable_id)
|
||||
input.taggableType = toStringOrNull(data?.taggable_type)
|
||||
}
|
||||
|
||||
return {
|
||||
input,
|
||||
}
|
||||
},
|
||||
|
||||
async createSubscription(ctx: SubscriptionContext): Promise<SubscriptionResult | undefined> {
|
||||
const { webhook, requestId } = ctx
|
||||
const providerConfig = getProviderConfig(webhook)
|
||||
const apiKey = providerConfig.apiKey as string | undefined
|
||||
const apiBaseUrl = providerConfig.apiBaseUrl as string | undefined
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
|
||||
if (!apiKey?.trim()) {
|
||||
throw new Error('Email Bison API Key is required.')
|
||||
}
|
||||
|
||||
if (!apiBaseUrl?.trim()) {
|
||||
throw new Error('Email Bison Instance URL is required.')
|
||||
}
|
||||
|
||||
if (!triggerId) {
|
||||
throw new Error('Email Bison trigger ID is required.')
|
||||
}
|
||||
|
||||
const { getEmailBisonEventTypeForTrigger } = await import('@/triggers/emailbison/utils')
|
||||
const eventType = getEmailBisonEventTypeForTrigger(triggerId)
|
||||
if (!eventType) {
|
||||
throw new Error(`Unknown Email Bison trigger type: ${triggerId}`)
|
||||
}
|
||||
|
||||
const notificationUrl = getNotificationUrl(webhook)
|
||||
|
||||
logger.info(`[${requestId}] Creating Email Bison webhook`, {
|
||||
triggerId,
|
||||
eventType,
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
|
||||
const response = await fetch(emailBisonUrl('/api/webhook-url', {}, apiBaseUrl), {
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders({ apiKey, apiBaseUrl }),
|
||||
body: JSON.stringify({
|
||||
name: `Sim - ${eventType}`,
|
||||
url: notificationUrl,
|
||||
events: [eventType],
|
||||
}),
|
||||
})
|
||||
|
||||
const responseBody = await parseJsonResponse(response)
|
||||
if (!response.ok) {
|
||||
const message = extractEmailBisonError(responseBody)
|
||||
logger.error(`[${requestId}] Failed to create Email Bison webhook`, {
|
||||
status: response.status,
|
||||
message,
|
||||
response: responseBody,
|
||||
})
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
throw new Error(
|
||||
'Invalid Email Bison API Key or Instance URL. Confirm both came from the same Email Bison instance.'
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
message ? `Email Bison error: ${message}` : 'Failed to create Email Bison webhook'
|
||||
)
|
||||
}
|
||||
|
||||
const data = isRecord(responseBody?.data) ? responseBody.data : null
|
||||
const externalId = data?.id
|
||||
if (externalId === undefined || externalId === null || externalId === '') {
|
||||
throw new Error('Email Bison webhook was created but the API response did not include an ID.')
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Successfully created Email Bison webhook`, {
|
||||
externalId,
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
|
||||
return { providerConfigUpdates: { externalId: String(externalId) } }
|
||||
},
|
||||
|
||||
async deleteSubscription(ctx: DeleteSubscriptionContext): Promise<void> {
|
||||
const { webhook, requestId } = ctx
|
||||
|
||||
try {
|
||||
const providerConfig = getProviderConfig(webhook)
|
||||
const apiKey = providerConfig.apiKey as string | undefined
|
||||
const apiBaseUrl = providerConfig.apiBaseUrl as string | undefined
|
||||
const externalId = providerConfig.externalId as string | undefined
|
||||
|
||||
if (!apiKey?.trim() || !apiBaseUrl?.trim() || !externalId?.trim()) {
|
||||
logger.warn(`[${requestId}] Missing Email Bison webhook cleanup configuration`, {
|
||||
webhookId: webhook.id,
|
||||
hasApiKey: Boolean(apiKey),
|
||||
hasApiBaseUrl: Boolean(apiBaseUrl),
|
||||
hasExternalId: Boolean(externalId),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
emailBisonUrl(`/api/webhook-url/${encodeURIComponent(externalId)}`, {}, apiBaseUrl),
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: emailBisonHeaders({ apiKey, apiBaseUrl }),
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok && response.status !== 404) {
|
||||
const responseBody = await parseJsonResponse(response)
|
||||
logger.warn(`[${requestId}] Failed to delete Email Bison webhook`, {
|
||||
status: response.status,
|
||||
response: responseBody,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
await response.body?.cancel()
|
||||
logger.info(`[${requestId}] Successfully deleted Email Bison webhook`, {
|
||||
externalId,
|
||||
webhookId: webhook.id,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.warn(`[${requestId}] Error deleting Email Bison webhook`, {
|
||||
message: toError(error).message,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
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 extractEmailBisonError(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
|
||||
|
||||
const data = body.data
|
||||
if (isRecord(data) && typeof data.message === 'string') return data.message
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function unwrapEmailBisonPayload(body: Record<string, unknown>): Record<string, unknown> {
|
||||
if (isRecord(body.event)) return body
|
||||
|
||||
const data = body.data
|
||||
if (isRecord(data) && isRecord(data.event)) return data
|
||||
|
||||
const payload = isRecord(data) ? data.payload : body.payload
|
||||
if (isRecord(payload) && isRecord(payload.event)) return payload
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
function toStringOrNull(value: unknown): string | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function toNumberOrNull(value: unknown): number | null {
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? value : null
|
||||
if (typeof value !== 'string' || value.trim() === '') return null
|
||||
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
}
|
||||
|
||||
function toRecordOrNull(value: unknown): Record<string, unknown> | null {
|
||||
return isRecord(value) ? value : null
|
||||
}
|
||||
|
||||
function renameTypeField(value: unknown, targetKey: string): Record<string, unknown> | null {
|
||||
if (!isRecord(value)) return null
|
||||
|
||||
const { type, ...rest } = value
|
||||
return { ...rest, [targetKey]: type ?? null }
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { calcomHandler } from '@/lib/webhooks/providers/calcom'
|
||||
import { calendlyHandler } from '@/lib/webhooks/providers/calendly'
|
||||
import { circlebackHandler } from '@/lib/webhooks/providers/circleback'
|
||||
import { confluenceHandler } from '@/lib/webhooks/providers/confluence'
|
||||
import { emailBisonHandler } from '@/lib/webhooks/providers/emailbison'
|
||||
import { fathomHandler } from '@/lib/webhooks/providers/fathom'
|
||||
import { firefliesHandler } from '@/lib/webhooks/providers/fireflies'
|
||||
import { genericHandler } from '@/lib/webhooks/providers/generic'
|
||||
@@ -55,6 +56,7 @@ const PROVIDER_HANDLERS: Record<string, WebhookProviderHandler> = {
|
||||
calcom: calcomHandler,
|
||||
circleback: circlebackHandler,
|
||||
confluence: confluenceHandler,
|
||||
emailbison: emailBisonHandler,
|
||||
fireflies: firefliesHandler,
|
||||
generic: genericHandler,
|
||||
gmail: gmailHandler,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import type {
|
||||
EmailBisonActionResponse,
|
||||
EmailBisonAttachLeadsParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
actionOutput,
|
||||
actionOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const attachLeadsToCampaignTool: ToolConfig<
|
||||
EmailBisonAttachLeadsParams,
|
||||
EmailBisonActionResponse
|
||||
> = {
|
||||
id: 'emailbison_attach_leads_to_campaign',
|
||||
name: 'Email Bison Attach Leads to Campaign',
|
||||
description: 'Adds existing Email Bison leads to a campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
leadIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead IDs to add to the campaign',
|
||||
items: { type: 'number', description: 'Lead ID' },
|
||||
},
|
||||
allowParallelSending: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Force add leads already in sequence in other campaigns',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/campaigns/${params.campaignId}/leads/attach-leads`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
lead_ids: params.leadIds,
|
||||
allow_parallel_sending: params.allowParallelSending,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign lead attachment result')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: actionOutput(data),
|
||||
}
|
||||
},
|
||||
outputs: actionOutputs,
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type {
|
||||
EmailBisonActionResponse,
|
||||
EmailBisonAttachTagsToLeadsParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
actionOutput,
|
||||
actionOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const attachTagsToLeadsTool: ToolConfig<
|
||||
EmailBisonAttachTagsToLeadsParams,
|
||||
EmailBisonActionResponse
|
||||
> = {
|
||||
id: 'emailbison_attach_tags_to_leads',
|
||||
name: 'Email Bison Attach Tags to Leads',
|
||||
description: 'Attaches Email Bison tags to one or more leads.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to attach',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
leadIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead IDs to tag',
|
||||
items: { type: 'number', description: 'Lead ID' },
|
||||
},
|
||||
skipWebhooks: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Skip Email Bison webhooks for this action',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags/attach-to-leads', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
tag_ids: params.tagIds,
|
||||
lead_ids: params.leadIds,
|
||||
skip_webhooks: params.skipWebhooks,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'tag attachment result')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: actionOutput(data),
|
||||
}
|
||||
},
|
||||
outputs: actionOutputs,
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonCreateCampaignParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createCampaignTool: ToolConfig<
|
||||
EmailBisonCreateCampaignParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_create_campaign',
|
||||
name: 'Email Bison Create Campaign',
|
||||
description: 'Creates a new Email Bison campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign name',
|
||||
},
|
||||
campaignType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign type: outbound or reply_followup',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/campaigns', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
name: params.name,
|
||||
type: params.campaignType,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { EmailBisonLeadMutationParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createLeadTool: ToolConfig<EmailBisonLeadMutationParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_create_lead',
|
||||
name: 'Email Bison Create Lead',
|
||||
description: 'Creates a single lead in Email Bison.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead last name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead email address',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead job title',
|
||||
},
|
||||
company: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead company',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional notes about the lead',
|
||||
},
|
||||
customVariables: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom variables to store on the lead',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/leads', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
first_name: params.firstName,
|
||||
last_name: params.lastName,
|
||||
email: params.email,
|
||||
title: params.title,
|
||||
company: params.company,
|
||||
notes: params.notes,
|
||||
custom_variables: params.customVariables,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { EmailBisonCreateTagParams, EmailBisonTagResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapTag,
|
||||
tagOutputs,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createTagTool: ToolConfig<EmailBisonCreateTagParams, EmailBisonTagResponse> = {
|
||||
id: 'emailbison_create_tag',
|
||||
name: 'Email Bison Create Tag',
|
||||
description: 'Creates a new Email Bison tag.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag name',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags', {}, params.apiBaseUrl),
|
||||
method: 'POST',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) => jsonBody({ name: params.name }),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'tag')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapTag(data),
|
||||
}
|
||||
},
|
||||
outputs: tagOutputs,
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { EmailBisonGetLeadParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getLeadTool: ToolConfig<EmailBisonGetLeadParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_get_lead',
|
||||
name: 'Email Bison Get Lead',
|
||||
description: 'Retrieves a lead by Email Bison lead ID or email address.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
leadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID or email address',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/leads/${encodeURIComponent(params.leadId.trim())}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export { attachLeadsToCampaignTool as emailBisonAttachLeadsToCampaignTool } from '@/tools/emailbison/attach_leads_to_campaign'
|
||||
export { attachTagsToLeadsTool as emailBisonAttachTagsToLeadsTool } from '@/tools/emailbison/attach_tags_to_leads'
|
||||
export { createCampaignTool as emailBisonCreateCampaignTool } from '@/tools/emailbison/create_campaign'
|
||||
export { createLeadTool as emailBisonCreateLeadTool } from '@/tools/emailbison/create_lead'
|
||||
export { createTagTool as emailBisonCreateTagTool } from '@/tools/emailbison/create_tag'
|
||||
export { getLeadTool as emailBisonGetLeadTool } from '@/tools/emailbison/get_lead'
|
||||
export { listCampaignsTool as emailBisonListCampaignsTool } from '@/tools/emailbison/list_campaigns'
|
||||
export { listLeadsTool as emailBisonListLeadsTool } from '@/tools/emailbison/list_leads'
|
||||
export { listRepliesTool as emailBisonListRepliesTool } from '@/tools/emailbison/list_replies'
|
||||
export { listTagsTool as emailBisonListTagsTool } from '@/tools/emailbison/list_tags'
|
||||
export * from '@/tools/emailbison/types'
|
||||
export { updateCampaignTool as emailBisonUpdateCampaignTool } from '@/tools/emailbison/update_campaign'
|
||||
export { updateCampaignStatusTool as emailBisonUpdateCampaignStatusTool } from '@/tools/emailbison/update_campaign_status'
|
||||
export { updateLeadTool as emailBisonUpdateLeadTool } from '@/tools/emailbison/update_lead'
|
||||
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
EmailBisonBaseParams,
|
||||
EmailBisonListCampaignsResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listCampaignsOutputs,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listCampaignsTool: ToolConfig<EmailBisonBaseParams, EmailBisonListCampaignsResponse> =
|
||||
{
|
||||
id: 'emailbison_list_campaigns',
|
||||
name: 'Email Bison List Campaigns',
|
||||
description: 'Retrieves Email Bison campaigns.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/campaigns', {}, params.apiBaseUrl),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'campaigns')
|
||||
const campaigns = data.map(mapCampaign)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
campaigns,
|
||||
count: campaigns.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listCampaignsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import type {
|
||||
EmailBisonListLeadsParams,
|
||||
EmailBisonListLeadsResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listLeadsOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listLeadsTool: ToolConfig<EmailBisonListLeadsParams, EmailBisonListLeadsResponse> = {
|
||||
id: 'emailbison_list_leads',
|
||||
name: 'Email Bison List Leads',
|
||||
description: 'Retrieves leads from Email Bison with optional search and tag filters.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term for filtering leads',
|
||||
},
|
||||
campaignStatus: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Lead campaign status filter: in_sequence, sequence_finished, sequence_stopped, never_contacted, or replied',
|
||||
},
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to include',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
excludedTagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to exclude',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
withoutTags: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return leads without tags',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
'/api/leads',
|
||||
{
|
||||
search: params.search,
|
||||
'filters.lead_campaign_status': params.campaignStatus,
|
||||
'filters.tag_ids': params.tagIds,
|
||||
'filters.excluded_tag_ids': params.excludedTagIds,
|
||||
'filters.without_tags': params.withoutTags,
|
||||
},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'leads')
|
||||
const leads = data.map(mapLead)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
leads,
|
||||
count: leads.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listLeadsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import type {
|
||||
EmailBisonListRepliesParams,
|
||||
EmailBisonListRepliesResponse,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listRepliesOutputs,
|
||||
mapReply,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listRepliesTool: ToolConfig<
|
||||
EmailBisonListRepliesParams,
|
||||
EmailBisonListRepliesResponse
|
||||
> = {
|
||||
id: 'emailbison_list_replies',
|
||||
name: 'Email Bison List Replies',
|
||||
description:
|
||||
'Retrieves Email Bison replies with optional status, folder, campaign, sender, lead, and tag filters.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term for replies',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply status: interested, automated_reply, or not_automated_reply',
|
||||
},
|
||||
folder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reply folder: inbox, sent, spam, bounced, or all',
|
||||
},
|
||||
read: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by read state',
|
||||
},
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
senderEmailId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sender email ID',
|
||||
},
|
||||
leadId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID',
|
||||
},
|
||||
tagIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag IDs to filter replies by',
|
||||
items: { type: 'number', description: 'Tag ID' },
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
'/api/replies',
|
||||
{
|
||||
search: params.search,
|
||||
status: params.status,
|
||||
folder: params.folder,
|
||||
read: params.read,
|
||||
campaign_id: params.campaignId,
|
||||
sender_email_id: params.senderEmailId,
|
||||
lead_id: params.leadId,
|
||||
tag_ids: params.tagIds,
|
||||
},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'replies')
|
||||
const replies = data.map(mapReply)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
replies,
|
||||
count: replies.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listRepliesOutputs,
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { EmailBisonBaseParams, EmailBisonListTagsResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonArrayData,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonUrl,
|
||||
listTagsOutputs,
|
||||
mapTag,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTagsTool: ToolConfig<EmailBisonBaseParams, EmailBisonListTagsResponse> = {
|
||||
id: 'emailbison_list_tags',
|
||||
name: 'Email Bison List Tags',
|
||||
description: 'Retrieves all Email Bison tags for the authenticated workspace.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
},
|
||||
request: {
|
||||
url: (params) => emailBisonUrl('/api/tags', {}, params.apiBaseUrl),
|
||||
method: 'GET',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonArrayData(response, 'tags')
|
||||
const tags = data.map(mapTag)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tags,
|
||||
count: tags.length,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: listTagsOutputs,
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface EmailBisonBaseParams {
|
||||
apiKey: string
|
||||
apiBaseUrl: string
|
||||
}
|
||||
|
||||
export interface EmailBisonCustomVariable {
|
||||
name: string | null
|
||||
value: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadStats {
|
||||
emails_sent: number | null
|
||||
opens: number | null
|
||||
replies: number | null
|
||||
unique_replies: number | null
|
||||
unique_opens: number | null
|
||||
}
|
||||
|
||||
export interface EmailBisonLead {
|
||||
id: number | null
|
||||
first_name: string | null
|
||||
last_name: string | null
|
||||
email: string | null
|
||||
title: string | null
|
||||
company: string | null
|
||||
notes: string | null
|
||||
status: string | null
|
||||
custom_variables: EmailBisonCustomVariable[]
|
||||
lead_campaign_data: unknown[]
|
||||
overall_stats: EmailBisonLeadStats
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonTag {
|
||||
id: number | null
|
||||
name: string | null
|
||||
default: boolean | null
|
||||
created_at?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignTag {
|
||||
id: number | null
|
||||
name: string | null
|
||||
default: boolean | null
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaign {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
name: string | null
|
||||
type: string | null
|
||||
status: string | null
|
||||
emails_sent: number | null
|
||||
opened: number | null
|
||||
unique_opens: number | null
|
||||
replied: number | null
|
||||
unique_replies: number | null
|
||||
bounced: number | null
|
||||
unsubscribed: number | null
|
||||
interested: number | null
|
||||
total_leads_contacted: number | null
|
||||
total_leads: number | null
|
||||
max_emails_per_day: number | null
|
||||
max_new_leads_per_day: number | null
|
||||
plain_text: boolean | null
|
||||
open_tracking: boolean | null
|
||||
can_unsubscribe: boolean | null
|
||||
unsubscribe_text: string | null
|
||||
sequence_prioritization?: string | null
|
||||
tags: EmailBisonCampaignTag[]
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReplyAddress {
|
||||
name: string | null
|
||||
address: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReplyAttachment {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
reply_id: number | null
|
||||
file_name: string | null
|
||||
download_url: string | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonReply {
|
||||
id: number | null
|
||||
uuid: string | null
|
||||
folder: string | null
|
||||
subject: string | null
|
||||
read: boolean | null
|
||||
interested: boolean | null
|
||||
automated_reply: boolean | null
|
||||
html_body: string | null
|
||||
text_body: string | null
|
||||
raw_body: string | null
|
||||
headers: string | null
|
||||
date_received: string | null
|
||||
type: string | null
|
||||
tracked_reply: boolean | null
|
||||
scheduled_email_id: number | string | null
|
||||
campaign_id: number | string | null
|
||||
lead_id: number | null
|
||||
sender_email_id: number | null
|
||||
raw_message_id: string | null
|
||||
from_name: string | null
|
||||
from_email_address: string | null
|
||||
primary_to_email_address: string | null
|
||||
to: EmailBisonReplyAddress[]
|
||||
cc: string | null
|
||||
bcc: string | null
|
||||
parent_id: number | string | null
|
||||
attachments: EmailBisonReplyAttachment[]
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
|
||||
export interface EmailBisonListLeadsParams extends EmailBisonBaseParams {
|
||||
search?: string
|
||||
campaignStatus?: string
|
||||
tagIds?: number[]
|
||||
excludedTagIds?: number[]
|
||||
withoutTags?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonGetLeadParams extends EmailBisonBaseParams {
|
||||
leadId: string
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadMutationParams extends EmailBisonBaseParams {
|
||||
leadId?: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
title?: string
|
||||
company?: string
|
||||
notes?: string
|
||||
customVariables?: EmailBisonCustomVariable[]
|
||||
}
|
||||
|
||||
export interface EmailBisonCreateCampaignParams extends EmailBisonBaseParams {
|
||||
name: string
|
||||
campaignType?: 'outbound' | 'reply_followup'
|
||||
}
|
||||
|
||||
export interface EmailBisonUpdateCampaignParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
name?: string
|
||||
maxEmailsPerDay?: number
|
||||
maxNewLeadsPerDay?: number
|
||||
plainText?: boolean
|
||||
openTracking?: boolean
|
||||
reputationBuilding?: boolean
|
||||
canUnsubscribe?: boolean
|
||||
includeAutoRepliesInStats?: boolean
|
||||
sequencePrioritization?: 'followups' | 'new_leads'
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignStatusParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
action: 'pause' | 'resume' | 'archive'
|
||||
}
|
||||
|
||||
export interface EmailBisonAttachLeadsParams extends EmailBisonBaseParams {
|
||||
campaignId: number
|
||||
leadIds: number[]
|
||||
allowParallelSending?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonListRepliesParams extends EmailBisonBaseParams {
|
||||
search?: string
|
||||
status?: string
|
||||
folder?: string
|
||||
read?: boolean
|
||||
campaignId?: number
|
||||
senderEmailId?: number
|
||||
leadId?: number
|
||||
tagIds?: number[]
|
||||
}
|
||||
|
||||
export interface EmailBisonCreateTagParams extends EmailBisonBaseParams {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface EmailBisonAttachTagsToLeadsParams extends EmailBisonBaseParams {
|
||||
tagIds: number[]
|
||||
leadIds: number[]
|
||||
skipWebhooks?: boolean
|
||||
}
|
||||
|
||||
export interface EmailBisonListLeadsResponse extends ToolResponse {
|
||||
output: {
|
||||
leads: EmailBisonLead[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonLeadResponse extends ToolResponse {
|
||||
output: EmailBisonLead
|
||||
}
|
||||
|
||||
export interface EmailBisonListCampaignsResponse extends ToolResponse {
|
||||
output: {
|
||||
campaigns: EmailBisonCampaign[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonCampaignResponse extends ToolResponse {
|
||||
output: EmailBisonCampaign
|
||||
}
|
||||
|
||||
export interface EmailBisonActionResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonListRepliesResponse extends ToolResponse {
|
||||
output: {
|
||||
replies: EmailBisonReply[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonListTagsResponse extends ToolResponse {
|
||||
output: {
|
||||
tags: EmailBisonTag[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface EmailBisonTagResponse extends ToolResponse {
|
||||
output: EmailBisonTag
|
||||
}
|
||||
|
||||
export type EmailBisonResponse =
|
||||
| EmailBisonListLeadsResponse
|
||||
| EmailBisonLeadResponse
|
||||
| EmailBisonListCampaignsResponse
|
||||
| EmailBisonCampaignResponse
|
||||
| EmailBisonActionResponse
|
||||
| EmailBisonListRepliesResponse
|
||||
| EmailBisonListTagsResponse
|
||||
| EmailBisonTagResponse
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonUpdateCampaignParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateCampaignTool: ToolConfig<
|
||||
EmailBisonUpdateCampaignParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_update_campaign',
|
||||
name: 'Email Bison Update Campaign',
|
||||
description: 'Updates Email Bison campaign settings.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign name',
|
||||
},
|
||||
maxEmailsPerDay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum emails per day',
|
||||
},
|
||||
maxNewLeadsPerDay: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum new leads per day',
|
||||
},
|
||||
plainText: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send plain text emails',
|
||||
},
|
||||
openTracking: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable open tracking',
|
||||
},
|
||||
reputationBuilding: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable reputation building',
|
||||
},
|
||||
canUnsubscribe: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable unsubscribe link',
|
||||
},
|
||||
includeAutoRepliesInStats: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Include auto replies in campaign stats',
|
||||
},
|
||||
sequencePrioritization: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sequence prioritization: followups or new_leads',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(`/api/campaigns/${params.campaignId}/update`, {}, params.apiBaseUrl),
|
||||
method: 'PATCH',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
name: params.name,
|
||||
max_emails_per_day: params.maxEmailsPerDay,
|
||||
max_new_leads_per_day: params.maxNewLeadsPerDay,
|
||||
plain_text: params.plainText,
|
||||
open_tracking: params.openTracking,
|
||||
reputation_building: params.reputationBuilding,
|
||||
can_unsubscribe: params.canUnsubscribe,
|
||||
include_auto_replies_in_stats: params.includeAutoRepliesInStats,
|
||||
sequence_prioritization: params.sequencePrioritization,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
EmailBisonCampaignResponse,
|
||||
EmailBisonCampaignStatusParams,
|
||||
} from '@/tools/emailbison/types'
|
||||
import {
|
||||
campaignOutputs,
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
mapCampaign,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const CAMPAIGN_STATUS_ACTIONS = new Set(['pause', 'resume', 'archive'])
|
||||
|
||||
export const updateCampaignStatusTool: ToolConfig<
|
||||
EmailBisonCampaignStatusParams,
|
||||
EmailBisonCampaignResponse
|
||||
> = {
|
||||
id: 'emailbison_update_campaign_status',
|
||||
name: 'Email Bison Update Campaign Status',
|
||||
description: 'Pauses, resumes, or archives an Email Bison campaign.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
campaignId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Campaign ID',
|
||||
},
|
||||
action: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Status action: pause, resume, or archive',
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!CAMPAIGN_STATUS_ACTIONS.has(params.action)) {
|
||||
throw new Error('Email Bison campaign status action must be pause, resume, or archive')
|
||||
}
|
||||
|
||||
return emailBisonUrl(
|
||||
`/api/campaigns/${params.campaignId}/${params.action}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
)
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: emailBisonHeaders,
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'campaign')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapCampaign(data),
|
||||
}
|
||||
},
|
||||
outputs: campaignOutputs,
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { EmailBisonLeadMutationParams, EmailBisonLeadResponse } from '@/tools/emailbison/types'
|
||||
import {
|
||||
emailBisonBaseParamFields,
|
||||
emailBisonHeaders,
|
||||
emailBisonRecordData,
|
||||
emailBisonUrl,
|
||||
jsonBody,
|
||||
leadOutputs,
|
||||
mapLead,
|
||||
} from '@/tools/emailbison/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateLeadTool: ToolConfig<EmailBisonLeadMutationParams, EmailBisonLeadResponse> = {
|
||||
id: 'emailbison_update_lead',
|
||||
name: 'Email Bison Update Lead',
|
||||
description:
|
||||
'Updates an existing Email Bison lead. Fields omitted from a PUT update may be cleared by Email Bison.',
|
||||
version: '1.0.0',
|
||||
params: {
|
||||
...emailBisonBaseParamFields,
|
||||
leadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead ID or email address',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead last name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead email address',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead job title',
|
||||
},
|
||||
company: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Lead company',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional notes about the lead',
|
||||
},
|
||||
customVariables: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom variables to store on the lead',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
url: (params) =>
|
||||
emailBisonUrl(
|
||||
`/api/leads/${encodeURIComponent(params.leadId?.trim() ?? '')}`,
|
||||
{},
|
||||
params.apiBaseUrl
|
||||
),
|
||||
method: 'PUT',
|
||||
headers: emailBisonHeaders,
|
||||
body: (params) =>
|
||||
jsonBody({
|
||||
first_name: params.firstName,
|
||||
last_name: params.lastName,
|
||||
email: params.email,
|
||||
title: params.title,
|
||||
company: params.company,
|
||||
notes: params.notes,
|
||||
custom_variables: params.customVariables,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response) => {
|
||||
const data = await emailBisonRecordData(response, 'lead')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: mapLead(data),
|
||||
}
|
||||
},
|
||||
outputs: leadOutputs,
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
import type {
|
||||
EmailBisonBaseParams,
|
||||
EmailBisonCampaign,
|
||||
EmailBisonCampaignTag,
|
||||
EmailBisonLead,
|
||||
EmailBisonLeadStats,
|
||||
EmailBisonReply,
|
||||
EmailBisonReplyAddress,
|
||||
EmailBisonReplyAttachment,
|
||||
EmailBisonTag,
|
||||
} from '@/tools/emailbison/types'
|
||||
import type { OutputProperty, ToolConfig } from '@/tools/types'
|
||||
|
||||
type QueryValue = string | number | boolean | Array<string | number> | undefined | null
|
||||
|
||||
interface EmailBisonEnvelope<T> {
|
||||
data?: T
|
||||
}
|
||||
|
||||
export function emailBisonHeaders(params: EmailBisonBaseParams): Record<string, string> {
|
||||
return {
|
||||
Authorization: `Bearer ${params.apiKey.trim()}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}
|
||||
|
||||
export const emailBisonBaseParamFields = {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Email Bison API token',
|
||||
},
|
||||
apiBaseUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Email Bison instance URL that issued the token',
|
||||
},
|
||||
} satisfies ToolConfig['params']
|
||||
|
||||
export function emailBisonUrl(
|
||||
path: string,
|
||||
query: Record<string, QueryValue>,
|
||||
baseUrl: string
|
||||
): string {
|
||||
const url = new URL(path, normalizeEmailBisonBaseUrl(baseUrl))
|
||||
|
||||
Object.entries(query).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null || value === '') return
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => {
|
||||
url.searchParams.append(key, String(item))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
url.searchParams.set(key, String(value))
|
||||
})
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
function normalizeEmailBisonBaseUrl(baseUrl: string): string {
|
||||
const trimmedBaseUrl = baseUrl.trim()
|
||||
if (!trimmedBaseUrl) {
|
||||
throw new Error('Email Bison Instance URL is required')
|
||||
}
|
||||
|
||||
const rawBaseUrl = /^https?:\/\//i.test(trimmedBaseUrl)
|
||||
? trimmedBaseUrl
|
||||
: `https://${trimmedBaseUrl}`
|
||||
const parsed = new URL(rawBaseUrl)
|
||||
|
||||
if (parsed.protocol !== 'https:') {
|
||||
throw new Error('Email Bison Instance URL must use HTTPS')
|
||||
}
|
||||
|
||||
return parsed.origin
|
||||
}
|
||||
|
||||
export function jsonBody(fields: Record<string, unknown>): Record<string, unknown> {
|
||||
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined))
|
||||
}
|
||||
|
||||
export async function emailBisonData<T>(response: Response): Promise<T | null> {
|
||||
const payload = (await response.json()) as EmailBisonEnvelope<T>
|
||||
return payload.data ?? null
|
||||
}
|
||||
|
||||
export async function emailBisonArrayData(response: Response, label: string): Promise<unknown[]> {
|
||||
const data = await emailBisonData<unknown[]>(response)
|
||||
if (!Array.isArray(data)) {
|
||||
throw new Error(`Email Bison response did not include a valid ${label} array`)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export async function emailBisonRecordData(response: Response, label: string): Promise<unknown> {
|
||||
const data = await emailBisonData<unknown>(response)
|
||||
if (!isRecord(data)) {
|
||||
throw new Error(`Email Bison response did not include a valid ${label} object`)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export function mapLead(value: unknown): EmailBisonLead {
|
||||
const record = toRecord(value)
|
||||
const stats = toRecord(record.overall_stats)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
first_name: toStringOrNull(record.first_name),
|
||||
last_name: toStringOrNull(record.last_name),
|
||||
email: toStringOrNull(record.email),
|
||||
title: toStringOrNull(record.title),
|
||||
company: toStringOrNull(record.company),
|
||||
notes: toStringOrNull(record.notes),
|
||||
status: toStringOrNull(record.status),
|
||||
custom_variables: toArray(record.custom_variables).map((item) => {
|
||||
const variable = toRecord(item)
|
||||
return {
|
||||
name: toStringOrNull(variable.name),
|
||||
value: toStringOrNull(variable.value),
|
||||
}
|
||||
}),
|
||||
lead_campaign_data: toArray(record.lead_campaign_data),
|
||||
overall_stats: mapLeadStats(stats),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapCampaign(value: unknown): EmailBisonCampaign {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
name: toStringOrNull(record.name),
|
||||
type: toStringOrNull(record.type),
|
||||
status: toStringOrNull(record.status),
|
||||
emails_sent: toNullableNumber(record.emails_sent),
|
||||
opened: toNullableNumber(record.opened),
|
||||
unique_opens: toNullableNumber(record.unique_opens),
|
||||
replied: toNullableNumber(record.replied),
|
||||
unique_replies: toNullableNumber(record.unique_replies),
|
||||
bounced: toNullableNumber(record.bounced),
|
||||
unsubscribed: toNullableNumber(record.unsubscribed),
|
||||
interested: toNullableNumber(record.interested),
|
||||
total_leads_contacted: toNullableNumber(record.total_leads_contacted),
|
||||
total_leads: toNullableNumber(record.total_leads),
|
||||
max_emails_per_day: toNullableNumber(record.max_emails_per_day),
|
||||
max_new_leads_per_day: toNullableNumber(record.max_new_leads_per_day),
|
||||
plain_text: toNullableBoolean(record.plain_text),
|
||||
open_tracking: toNullableBoolean(record.open_tracking),
|
||||
can_unsubscribe: toNullableBoolean(record.can_unsubscribe),
|
||||
unsubscribe_text: toStringOrNull(record.unsubscribe_text),
|
||||
...(record.sequence_prioritization !== undefined && {
|
||||
sequence_prioritization: toStringOrNull(record.sequence_prioritization),
|
||||
}),
|
||||
tags: toArray(record.tags).map(mapCampaignTag),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
function mapCampaignTag(value: unknown): EmailBisonCampaignTag {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
name: toStringOrNull(record.name),
|
||||
default: toNullableBoolean(record.default),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTag(value: unknown): EmailBisonTag {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
name: toStringOrNull(record.name),
|
||||
default: toNullableBoolean(record.default),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapReply(value: unknown): EmailBisonReply {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
folder: toStringOrNull(record.folder),
|
||||
subject: toStringOrNull(record.subject),
|
||||
read: toNullableBoolean(record.read),
|
||||
interested: toNullableBoolean(record.interested),
|
||||
automated_reply: toNullableBoolean(record.automated_reply),
|
||||
html_body: toStringOrNull(record.html_body),
|
||||
text_body: toStringOrNull(record.text_body),
|
||||
raw_body: toStringOrNull(record.raw_body),
|
||||
headers: toStringOrNull(record.headers),
|
||||
date_received: toStringOrNull(record.date_received),
|
||||
type: toStringOrNull(record.type),
|
||||
tracked_reply: toNullableBoolean(record.tracked_reply),
|
||||
scheduled_email_id: toStringNumberOrNull(record.scheduled_email_id),
|
||||
campaign_id: toStringNumberOrNull(record.campaign_id),
|
||||
lead_id: toNullableNumber(record.lead_id),
|
||||
sender_email_id: toNullableNumber(record.sender_email_id),
|
||||
raw_message_id: toStringOrNull(record.raw_message_id),
|
||||
from_name: toStringOrNull(record.from_name),
|
||||
from_email_address: toStringOrNull(record.from_email_address),
|
||||
primary_to_email_address: toStringOrNull(record.primary_to_email_address),
|
||||
to: toArray(record.to).map(mapReplyAddress),
|
||||
cc: toStringOrNull(record.cc),
|
||||
bcc: toStringOrNull(record.bcc),
|
||||
parent_id: toStringNumberOrNull(record.parent_id),
|
||||
attachments: toArray(record.attachments).map(mapReplyAttachment),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
export function actionOutput(value: unknown): { success: boolean; message: string | null } {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
success: record.success === true,
|
||||
message: toStringOrNull(record.message),
|
||||
}
|
||||
}
|
||||
|
||||
export const leadOutputs = {
|
||||
id: { type: 'number', description: 'Lead ID' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
title: { type: 'string', description: 'Lead title', optional: true },
|
||||
company: { type: 'string', description: 'Lead company', optional: true },
|
||||
notes: { type: 'string', description: 'Lead notes', optional: true },
|
||||
status: { type: 'string', description: 'Lead status', optional: true },
|
||||
custom_variables: {
|
||||
type: 'array',
|
||||
description: 'Lead custom variables',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Custom variable name' },
|
||||
value: { type: 'string', description: 'Custom variable value', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
lead_campaign_data: {
|
||||
type: 'array',
|
||||
description: 'Lead campaign data returned by Email Bison',
|
||||
},
|
||||
overall_stats: {
|
||||
type: 'object',
|
||||
description: 'Lead engagement stats',
|
||||
properties: {
|
||||
emails_sent: { type: 'number', description: 'Emails sent' },
|
||||
opens: { type: 'number', description: 'Email opens' },
|
||||
replies: { type: 'number', description: 'Replies' },
|
||||
unique_replies: { type: 'number', description: 'Unique replies' },
|
||||
unique_opens: { type: 'number', description: 'Unique opens' },
|
||||
},
|
||||
},
|
||||
created_at: { type: 'string', description: 'Lead creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Lead update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
const tagProperties = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
created_at: { type: 'string', description: 'Tag creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Tag update timestamp', optional: true },
|
||||
} satisfies Record<string, OutputProperty>
|
||||
|
||||
const campaignTagProperties = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
} satisfies Record<string, OutputProperty>
|
||||
|
||||
export const listLeadsOutputs = {
|
||||
leads: {
|
||||
type: 'array',
|
||||
description: 'List of leads',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: leadOutputs,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of leads returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const campaignOutputs = {
|
||||
id: { type: 'number', description: 'Campaign ID' },
|
||||
uuid: { type: 'string', description: 'Campaign UUID', optional: true },
|
||||
name: { type: 'string', description: 'Campaign name' },
|
||||
type: { type: 'string', description: 'Campaign type', optional: true },
|
||||
status: { type: 'string', description: 'Campaign status', optional: true },
|
||||
emails_sent: { type: 'number', description: 'Emails sent' },
|
||||
opened: { type: 'number', description: 'Total opens' },
|
||||
unique_opens: { type: 'number', description: 'Unique opens' },
|
||||
replied: { type: 'number', description: 'Total replies' },
|
||||
unique_replies: { type: 'number', description: 'Unique replies' },
|
||||
bounced: { type: 'number', description: 'Bounces' },
|
||||
unsubscribed: { type: 'number', description: 'Unsubscribes' },
|
||||
interested: { type: 'number', description: 'Interested replies' },
|
||||
total_leads_contacted: { type: 'number', description: 'Total leads contacted' },
|
||||
total_leads: { type: 'number', description: 'Total leads' },
|
||||
max_emails_per_day: { type: 'number', description: 'Maximum emails per day', optional: true },
|
||||
max_new_leads_per_day: {
|
||||
type: 'number',
|
||||
description: 'Maximum new leads per day',
|
||||
optional: true,
|
||||
},
|
||||
plain_text: {
|
||||
type: 'boolean',
|
||||
description: 'Whether campaign emails are plain text',
|
||||
optional: true,
|
||||
},
|
||||
open_tracking: {
|
||||
type: 'boolean',
|
||||
description: 'Whether open tracking is enabled',
|
||||
optional: true,
|
||||
},
|
||||
can_unsubscribe: {
|
||||
type: 'boolean',
|
||||
description: 'Whether recipients can unsubscribe',
|
||||
optional: true,
|
||||
},
|
||||
unsubscribe_text: { type: 'string', description: 'Unsubscribe text', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Campaign tags',
|
||||
items: { type: 'object', properties: campaignTagProperties },
|
||||
},
|
||||
created_at: { type: 'string', description: 'Campaign creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Campaign update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listCampaignsOutputs = {
|
||||
campaigns: {
|
||||
type: 'array',
|
||||
description: 'List of campaigns',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: campaignOutputs,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of campaigns returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const actionOutputs = {
|
||||
success: { type: 'boolean', description: 'Whether the action succeeded' },
|
||||
message: { type: 'string', description: 'Action message', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listRepliesOutputs = {
|
||||
replies: {
|
||||
type: 'array',
|
||||
description: 'List of replies',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Reply ID' },
|
||||
subject: { type: 'string', description: 'Reply subject', optional: true },
|
||||
text_body: { type: 'string', description: 'Reply text body', optional: true },
|
||||
from_email_address: { type: 'string', description: 'Sender email', optional: true },
|
||||
primary_to_email_address: {
|
||||
type: 'string',
|
||||
description: 'Primary recipient',
|
||||
optional: true,
|
||||
},
|
||||
date_received: { type: 'string', description: 'Date received', optional: true },
|
||||
interested: { type: 'boolean', description: 'Whether the reply is marked interested' },
|
||||
read: { type: 'boolean', description: 'Whether the reply is read' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of replies returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const tagOutputs = {
|
||||
id: { type: 'number', description: 'Tag ID' },
|
||||
name: { type: 'string', description: 'Tag name' },
|
||||
default: { type: 'boolean', description: 'Whether this is a default tag' },
|
||||
created_at: { type: 'string', description: 'Tag creation timestamp', optional: true },
|
||||
updated_at: { type: 'string', description: 'Tag update timestamp', optional: true },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
export const listTagsOutputs = {
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'List of tags',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: tagProperties,
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of tags returned' },
|
||||
} satisfies NonNullable<ToolConfig['outputs']>
|
||||
|
||||
function mapLeadStats(record: Record<string, unknown>): EmailBisonLeadStats {
|
||||
return {
|
||||
emails_sent: toNullableNumber(record.emails_sent),
|
||||
opens: toNullableNumber(record.opens),
|
||||
replies: toNullableNumber(record.replies),
|
||||
unique_replies: toNullableNumber(record.unique_replies),
|
||||
unique_opens: toNullableNumber(record.unique_opens),
|
||||
}
|
||||
}
|
||||
|
||||
function mapReplyAddress(value: unknown): EmailBisonReplyAddress {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
name: toStringOrNull(record.name),
|
||||
address: toStringOrNull(record.address),
|
||||
}
|
||||
}
|
||||
|
||||
function mapReplyAttachment(value: unknown): EmailBisonReplyAttachment {
|
||||
const record = toRecord(value)
|
||||
|
||||
return {
|
||||
id: toNullableNumber(record.id),
|
||||
uuid: toStringOrNull(record.uuid),
|
||||
reply_id: toNullableNumber(record.reply_id),
|
||||
file_name: toStringOrNull(record.file_name),
|
||||
download_url: toStringOrNull(record.download_url),
|
||||
created_at: toStringOrNull(record.created_at),
|
||||
updated_at: toStringOrNull(record.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function toRecord(value: unknown): Record<string, unknown> {
|
||||
return isRecord(value) ? value : {}
|
||||
}
|
||||
|
||||
function toArray(value: unknown): unknown[] {
|
||||
return Array.isArray(value) ? value : []
|
||||
}
|
||||
|
||||
function toStringOrNull(value: unknown): string | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function toStringNumberOrNull(value: unknown): number | string | null {
|
||||
if (typeof value === 'number' || typeof value === 'string') return value
|
||||
return null
|
||||
}
|
||||
|
||||
function toNumber(value: unknown): number | null {
|
||||
if (typeof value === 'number') return value
|
||||
if (typeof value === 'string') {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function toNullableNumber(value: unknown): number | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return toNumber(value)
|
||||
}
|
||||
|
||||
function toNullableBoolean(value: unknown): boolean | null {
|
||||
if (value === undefined || value === null) return null
|
||||
return typeof value === 'boolean' ? value : null
|
||||
}
|
||||
@@ -567,6 +567,21 @@ import {
|
||||
elasticsearchUpdateDocumentTool,
|
||||
} from '@/tools/elasticsearch'
|
||||
import { elevenLabsTtsTool } from '@/tools/elevenlabs'
|
||||
import {
|
||||
emailBisonAttachLeadsToCampaignTool,
|
||||
emailBisonAttachTagsToLeadsTool,
|
||||
emailBisonCreateCampaignTool,
|
||||
emailBisonCreateLeadTool,
|
||||
emailBisonCreateTagTool,
|
||||
emailBisonGetLeadTool,
|
||||
emailBisonListCampaignsTool,
|
||||
emailBisonListLeadsTool,
|
||||
emailBisonListRepliesTool,
|
||||
emailBisonListTagsTool,
|
||||
emailBisonUpdateCampaignStatusTool,
|
||||
emailBisonUpdateCampaignTool,
|
||||
emailBisonUpdateLeadTool,
|
||||
} from '@/tools/emailbison'
|
||||
import {
|
||||
enrichCheckCreditsTool,
|
||||
enrichCompanyFundingTool,
|
||||
@@ -4016,6 +4031,19 @@ export const tools: Record<string, ToolConfig> = {
|
||||
elasticsearch_list_indices: elasticsearchListIndicesTool,
|
||||
elasticsearch_cluster_health: elasticsearchClusterHealthTool,
|
||||
elasticsearch_cluster_stats: elasticsearchClusterStatsTool,
|
||||
emailbison_attach_leads_to_campaign: emailBisonAttachLeadsToCampaignTool,
|
||||
emailbison_attach_tags_to_leads: emailBisonAttachTagsToLeadsTool,
|
||||
emailbison_create_campaign: emailBisonCreateCampaignTool,
|
||||
emailbison_create_lead: emailBisonCreateLeadTool,
|
||||
emailbison_create_tag: emailBisonCreateTagTool,
|
||||
emailbison_get_lead: emailBisonGetLeadTool,
|
||||
emailbison_list_campaigns: emailBisonListCampaignsTool,
|
||||
emailbison_list_leads: emailBisonListLeadsTool,
|
||||
emailbison_list_replies: emailBisonListRepliesTool,
|
||||
emailbison_list_tags: emailBisonListTagsTool,
|
||||
emailbison_update_campaign: emailBisonUpdateCampaignTool,
|
||||
emailbison_update_campaign_status: emailBisonUpdateCampaignStatusTool,
|
||||
emailbison_update_lead: emailBisonUpdateLeadTool,
|
||||
evernote_copy_note: evernoteCopyNoteTool,
|
||||
evernote_create_note: evernoteCreateNoteTool,
|
||||
evernote_create_notebook: evernoteCreateNotebookTool,
|
||||
@@ -5249,13 +5277,13 @@ export const tools: Record<string, ToolConfig> = {
|
||||
hubspot_update_deal: hubspotUpdateDealTool,
|
||||
hubspot_update_line_item: hubspotUpdateLineItemTool,
|
||||
hubspot_update_ticket: hubspotUpdateTicketTool,
|
||||
sharepoint_create_page: sharepointCreatePageTool,
|
||||
sharepoint_read_page: sharepointReadPageTool,
|
||||
sharepoint_list_sites: sharepointListSitesTool,
|
||||
sharepoint_get_list: sharepointGetListTool,
|
||||
sharepoint_create_list: sharepointCreateListTool,
|
||||
sharepoint_update_list: sharepointUpdateListItemTool,
|
||||
sharepoint_add_list_items: sharepointAddListItemTool,
|
||||
sharepoint_create_list: sharepointCreateListTool,
|
||||
sharepoint_create_page: sharepointCreatePageTool,
|
||||
sharepoint_get_list: sharepointGetListTool,
|
||||
sharepoint_list_sites: sharepointListSitesTool,
|
||||
sharepoint_read_page: sharepointReadPageTool,
|
||||
sharepoint_update_list: sharepointUpdateListItemTool,
|
||||
sharepoint_upload_file: sharepointUploadFileTool,
|
||||
stripe_create_payment_intent: stripeCreatePaymentIntentTool,
|
||||
stripe_retrieve_payment_intent: stripeRetrievePaymentIntentTool,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { SharepointAddListItemResponse, SharepointToolParams } from '@/tools/sharepoint/types'
|
||||
import { optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointAddListItem')
|
||||
@@ -8,7 +9,7 @@ export const addListItemTool: ToolConfig<SharepointToolParams, SharepointAddList
|
||||
id: 'sharepoint_add_list_items',
|
||||
name: 'Add SharePoint List Item',
|
||||
description: 'Add a new item to a SharePoint list',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -42,7 +43,7 @@ export const addListItemTool: ToolConfig<SharepointToolParams, SharepointAddList
|
||||
'The ID of the list to add the item to. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012',
|
||||
},
|
||||
listItemFields: {
|
||||
type: 'object',
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Field values for the new list item',
|
||||
@@ -51,11 +52,12 @@ export const addListItemTool: ToolConfig<SharepointToolParams, SharepointAddList
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const siteId = params.siteId || params.siteSelector || 'root'
|
||||
if (!params.listId) {
|
||||
const siteId = optionalTrim(params.siteId) || optionalTrim(params.siteSelector) || 'root'
|
||||
const listId = optionalTrim(params.listId)
|
||||
if (!listId) {
|
||||
throw new Error('listId must be provided')
|
||||
}
|
||||
const listSegment = params.listId
|
||||
const listSegment = encodeURIComponent(listId)
|
||||
return `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}/items`
|
||||
},
|
||||
method: 'POST',
|
||||
@@ -74,7 +76,10 @@ export const addListItemTool: ToolConfig<SharepointToolParams, SharepointAddList
|
||||
params.listItemFields !== null &&
|
||||
'fields' in (params.listItemFields as Record<string, unknown>) &&
|
||||
Object.keys(params.listItemFields as Record<string, unknown>).length === 1
|
||||
? ((params.listItemFields as any).fields as Record<string, unknown>)
|
||||
? ((params.listItemFields as { fields: Record<string, unknown> }).fields as Record<
|
||||
string,
|
||||
unknown
|
||||
>)
|
||||
: (params.listItemFields as Record<string, unknown>)
|
||||
|
||||
if (!providedFields || Object.keys(providedFields).length === 0) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
SharepointList,
|
||||
SharepointToolParams,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointCreateList')
|
||||
@@ -13,7 +14,7 @@ export const createListTool: ToolConfig<SharepointToolParams, SharepointCreateLi
|
||||
id: 'sharepoint_create_list',
|
||||
name: 'Create SharePoint List',
|
||||
description: 'Create a new list in a SharePoint site',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -59,7 +60,7 @@ export const createListTool: ToolConfig<SharepointToolParams, SharepointCreateLi
|
||||
description: "List template name (e.g., 'genericList')",
|
||||
},
|
||||
pageContent: {
|
||||
type: 'string',
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
@@ -69,7 +70,7 @@ export const createListTool: ToolConfig<SharepointToolParams, SharepointCreateLi
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const siteId = params.siteSelector || params.siteId || 'root'
|
||||
const siteId = optionalTrim(params.siteSelector) || optionalTrim(params.siteId) || 'root'
|
||||
return `https://graph.microsoft.com/v1.0/sites/${siteId}/lists`
|
||||
},
|
||||
method: 'POST',
|
||||
@@ -79,36 +80,51 @@ export const createListTool: ToolConfig<SharepointToolParams, SharepointCreateLi
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (!params.listDisplayName) {
|
||||
const listDisplayName = optionalTrim(params.listDisplayName)
|
||||
if (!listDisplayName) {
|
||||
throw new Error('listDisplayName is required')
|
||||
}
|
||||
|
||||
// Derive columns from pageContent JSON (object or string) or top-level array
|
||||
let columns: unknown[] | undefined
|
||||
if (params.pageContent) {
|
||||
if (typeof params.pageContent === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(params.pageContent)
|
||||
if (Array.isArray(parsed)) columns = parsed
|
||||
else if (parsed && Array.isArray((parsed as any).columns))
|
||||
columns = (parsed as any).columns
|
||||
if (Array.isArray(parsed)) {
|
||||
columns = parsed
|
||||
} else if (
|
||||
parsed &&
|
||||
typeof parsed === 'object' &&
|
||||
Array.isArray((parsed as { columns?: unknown[] }).columns)
|
||||
) {
|
||||
columns = (parsed as { columns: unknown[] }).columns
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Invalid JSON in pageContent for create list; ignoring', {
|
||||
error: toError(error).message,
|
||||
})
|
||||
}
|
||||
} else if (typeof params.pageContent === 'object') {
|
||||
const pc: any = params.pageContent
|
||||
if (Array.isArray(pc)) columns = pc
|
||||
else if (pc && Array.isArray(pc.columns)) columns = pc.columns
|
||||
const pageContent = params.pageContent as { columns?: unknown[] } | unknown[]
|
||||
if (Array.isArray(pageContent)) {
|
||||
columns = pageContent
|
||||
} else if (pageContent && Array.isArray(pageContent.columns)) {
|
||||
columns = pageContent.columns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const payload: any = {
|
||||
displayName: params.listDisplayName,
|
||||
description: params.listDescription,
|
||||
list: { template: params.listTemplate || 'genericList' },
|
||||
const payload: {
|
||||
displayName: string
|
||||
description?: string
|
||||
list: { template: string }
|
||||
columns?: unknown[]
|
||||
} = {
|
||||
displayName: listDisplayName,
|
||||
list: { template: optionalTrim(params.listTemplate) || 'genericList' },
|
||||
}
|
||||
const listDescription = optionalTrim(params.listDescription)
|
||||
if (listDescription) payload.description = listDescription
|
||||
if (columns && columns.length > 0) payload.columns = columns
|
||||
|
||||
logger.info('Creating SharePoint list', {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
SharepointPage,
|
||||
SharepointToolParams,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointCreatePage')
|
||||
@@ -12,7 +13,7 @@ export const createPageTool: ToolConfig<SharepointToolParams, SharepointCreatePa
|
||||
id: 'sharepoint_create_page',
|
||||
name: 'Create SharePoint Page',
|
||||
description: 'Create a new page in a SharePoint site',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -60,8 +61,7 @@ export const createPageTool: ToolConfig<SharepointToolParams, SharepointCreatePa
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
// Use specific site if provided, otherwise use root site
|
||||
const siteId = params.siteSelector || params.siteId || 'root'
|
||||
const siteId = optionalTrim(params.siteSelector) || optionalTrim(params.siteId) || 'root'
|
||||
return `https://graph.microsoft.com/v1.0/sites/${siteId}/pages`
|
||||
},
|
||||
method: 'POST',
|
||||
@@ -71,16 +71,16 @@ export const createPageTool: ToolConfig<SharepointToolParams, SharepointCreatePa
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (!params.pageName) {
|
||||
const pageName = optionalTrim(params.pageName)
|
||||
if (!pageName) {
|
||||
throw new Error('Page name is required')
|
||||
}
|
||||
|
||||
const pageTitle = params.pageTitle || params.pageName
|
||||
const pageTitle = optionalTrim(params.pageTitle) || pageName
|
||||
|
||||
// Basic page structure required by Microsoft Graph API
|
||||
const pageData: SharepointPage = {
|
||||
'@odata.type': '#microsoft.graph.sitePage',
|
||||
name: params.pageName,
|
||||
name: pageName,
|
||||
title: pageTitle,
|
||||
publishingState: {
|
||||
level: 'draft',
|
||||
@@ -88,8 +88,8 @@ export const createPageTool: ToolConfig<SharepointToolParams, SharepointCreatePa
|
||||
pageLayout: 'article',
|
||||
}
|
||||
|
||||
// Add content if provided using the simple innerHtml approach from the documentation
|
||||
if (params.pageContent) {
|
||||
const pageContent = typeof params.pageContent === 'string' ? params.pageContent : undefined
|
||||
if (pageContent) {
|
||||
pageData.canvasLayout = {
|
||||
horizontalSections: [
|
||||
{
|
||||
@@ -103,7 +103,7 @@ export const createPageTool: ToolConfig<SharepointToolParams, SharepointCreatePa
|
||||
webparts: [
|
||||
{
|
||||
id: '6f9230af-2a98-4952-b205-9ede4f9ef548',
|
||||
innerHtml: `<p>${params.pageContent.replace(/"/g, '"').replace(/'/g, ''')}</p>`,
|
||||
innerHtml: `<p>${pageContent.replace(/"/g, '"').replace(/'/g, ''')}</p>`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
SharepointList,
|
||||
SharepointToolParams,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { assertGraphNextPageUrl, getGraphNextPageUrl, optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointGetList')
|
||||
@@ -12,7 +13,7 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
id: 'sharepoint_get_list',
|
||||
name: 'Get SharePoint List',
|
||||
description: 'Get metadata (and optionally columns/items) for a SharePoint list',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -45,14 +46,36 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
description:
|
||||
'The ID of the list to retrieve. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012',
|
||||
},
|
||||
includeColumns: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Whether to include column definitions when retrieving a specific list',
|
||||
},
|
||||
includeItems: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Whether to include list items when retrieving a specific list',
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full @odata.nextLink URL from a previous Microsoft Graph page response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const siteId = params.siteId || params.siteSelector || 'root'
|
||||
if (params.nextPageUrl) {
|
||||
return assertGraphNextPageUrl(params.nextPageUrl)
|
||||
}
|
||||
|
||||
// If neither listId nor listTitle provided, list all lists in the site
|
||||
if (!params.listId) {
|
||||
const siteId = optionalTrim(params.siteId) || optionalTrim(params.siteSelector) || 'root'
|
||||
const listId = optionalTrim(params.listId)
|
||||
|
||||
if (!listId) {
|
||||
const baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists`
|
||||
const url = new URL(baseUrl)
|
||||
const finalUrl = url.toString()
|
||||
@@ -63,11 +86,9 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
return finalUrl
|
||||
}
|
||||
|
||||
const listSegment = params.listId
|
||||
// Default to returning items when targeting a specific list unless explicitly disabled
|
||||
const listSegment = encodeURIComponent(listId)
|
||||
const wantsItems = typeof params.includeItems === 'boolean' ? params.includeItems : true
|
||||
|
||||
// If caller wants items for a specific list, prefer the items endpoint (no columns)
|
||||
if (wantsItems && !params.includeColumns) {
|
||||
const itemsUrl = new URL(
|
||||
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}/items`
|
||||
@@ -77,12 +98,11 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
logger.info('SharePoint Get List Items URL', {
|
||||
finalUrl: finalItemsUrl,
|
||||
siteId,
|
||||
listId: params.listId,
|
||||
listId,
|
||||
})
|
||||
return finalItemsUrl
|
||||
}
|
||||
|
||||
// Otherwise, fetch list metadata (optionally with columns/items via $expand)
|
||||
const baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}`
|
||||
const url = new URL(baseUrl)
|
||||
const expandParts: string[] = []
|
||||
@@ -94,7 +114,7 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
logger.info('SharePoint Get List URL', {
|
||||
finalUrl,
|
||||
siteId,
|
||||
listId: params.listId,
|
||||
listId,
|
||||
includeColumns: !!params.includeColumns,
|
||||
includeItems: wantsItems,
|
||||
})
|
||||
@@ -107,7 +127,7 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const data = await response.json()
|
||||
|
||||
// If the response is a collection of items (from the items endpoint)
|
||||
@@ -122,25 +142,18 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
fields: i.fields as Record<string, unknown>,
|
||||
}))
|
||||
|
||||
const nextLink: string | undefined = (data as any)['@odata.nextLink']
|
||||
const nextPageToken = nextLink
|
||||
? (() => {
|
||||
try {
|
||||
const u = new URL(nextLink)
|
||||
return u.searchParams.get('$skiptoken') || u.searchParams.get('$skip') || undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})()
|
||||
: undefined
|
||||
const nextPageUrl = getGraphNextPageUrl(data as Record<string, unknown>)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: { list: { items } as SharepointList, nextPageToken },
|
||||
output: {
|
||||
list: { id: optionalTrim(params?.listId) || '', items } as SharepointList,
|
||||
items,
|
||||
nextPageUrl,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a collection of lists (site-level)
|
||||
if (Array.isArray((data as any).value)) {
|
||||
const lists: SharepointList[] = (data as any).value.map((l: any) => ({
|
||||
id: l.id,
|
||||
@@ -152,25 +165,14 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
list: l.list,
|
||||
}))
|
||||
|
||||
const nextLink: string | undefined = (data as any)['@odata.nextLink']
|
||||
const nextPageToken = nextLink
|
||||
? (() => {
|
||||
try {
|
||||
const u = new URL(nextLink)
|
||||
return u.searchParams.get('$skiptoken') || u.searchParams.get('$skip') || undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})()
|
||||
: undefined
|
||||
const nextPageUrl = getGraphNextPageUrl(data as Record<string, unknown>)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: { lists, nextPageToken },
|
||||
output: { lists, nextPageUrl },
|
||||
}
|
||||
}
|
||||
|
||||
// Single list response (with optional expands)
|
||||
const list: SharepointList = {
|
||||
id: data.id,
|
||||
displayName: data.displayName ?? data.name,
|
||||
@@ -242,5 +244,21 @@ export const getListTool: ToolConfig<SharepointToolParams, SharepointGetListResp
|
||||
description: 'All lists in the site when no listId/title provided',
|
||||
items: { type: 'object' },
|
||||
},
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'List items with expanded fields when reading list items',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
fields: { type: 'object', description: 'Field values for the item' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
description: 'Full Microsoft Graph @odata.nextLink URL for the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,3 +15,5 @@ export const sharepointReadPageTool = readPageTool
|
||||
export const sharepointUpdateListItemTool = updateListItemTool
|
||||
export const sharepointAddListItemTool = addListItemTool
|
||||
export const sharepointUploadFileTool = uploadFileTool
|
||||
|
||||
export * from '@/tools/sharepoint/types'
|
||||
|
||||
@@ -3,13 +3,14 @@ import type {
|
||||
SharepointSite,
|
||||
SharepointToolParams,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { assertGraphNextPageUrl, getGraphNextPageUrl, optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteResponse> = {
|
||||
id: 'sharepoint_list_sites',
|
||||
name: 'List SharePoint Sites',
|
||||
description: 'List details of all SharePoint sites',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -29,6 +30,12 @@ export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteR
|
||||
visibility: 'user-only',
|
||||
description: 'Select the SharePoint site',
|
||||
},
|
||||
siteId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'The ID of the SharePoint site (internal use)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -36,27 +43,34 @@ export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteR
|
||||
description:
|
||||
'The group ID for accessing a group team site. Example: a GUID like 12345678-1234-1234-1234-123456789012',
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full @odata.nextLink URL from a previous Microsoft Graph page response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
let baseUrl: string
|
||||
if (params.nextPageUrl) {
|
||||
return assertGraphNextPageUrl(params.nextPageUrl)
|
||||
}
|
||||
|
||||
if (params.groupId) {
|
||||
// Access group team site
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/groups/${params.groupId}/sites/root`
|
||||
} else if (params.siteId || params.siteSelector) {
|
||||
// Access specific site by ID
|
||||
const siteId = params.siteId || params.siteSelector
|
||||
let baseUrl: string
|
||||
const groupId = optionalTrim(params.groupId)
|
||||
const siteId = optionalTrim(params.siteId) || optionalTrim(params.siteSelector)
|
||||
|
||||
if (groupId) {
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/groups/${groupId}/sites/root`
|
||||
} else if (siteId) {
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}`
|
||||
} else {
|
||||
// get all sites
|
||||
baseUrl = 'https://graph.microsoft.com/v1.0/sites?search=*'
|
||||
}
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
|
||||
// Use Microsoft Graph $select parameter to get site details
|
||||
url.searchParams.append(
|
||||
'$select',
|
||||
'id,name,displayName,webUrl,description,createdDateTime,lastModifiedDateTime,isPersonalSite,root,siteCollection'
|
||||
@@ -71,12 +85,10 @@ export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteR
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
// Check if this is a search result (multiple sites) or single site
|
||||
if (data.value && Array.isArray(data.value)) {
|
||||
// Multiple sites from search
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
@@ -89,10 +101,11 @@ export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteR
|
||||
createdDateTime: site.createdDateTime,
|
||||
lastModifiedDateTime: site.lastModifiedDateTime,
|
||||
})),
|
||||
nextPageUrl: getGraphNextPageUrl(data as Record<string, unknown>),
|
||||
},
|
||||
}
|
||||
}
|
||||
// Single site response
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
@@ -155,5 +168,10 @@ export const listSitesTool: ToolConfig<SharepointToolParams, SharepointReadSiteR
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
description: 'Full Microsoft Graph @odata.nextLink URL for the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@ import type {
|
||||
SharepointReadPageResponse,
|
||||
SharepointToolParams,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { cleanODataMetadata, extractTextFromCanvasLayout } from '@/tools/sharepoint/utils'
|
||||
import {
|
||||
assertGraphNextPageUrl,
|
||||
cleanODataMetadata,
|
||||
escapeODataString,
|
||||
extractTextFromCanvasLayout,
|
||||
getGraphNextPageUrl,
|
||||
optionalTrim,
|
||||
} from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointReadPage')
|
||||
@@ -15,7 +22,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
id: 'sharepoint_read_page',
|
||||
name: 'Read SharePoint Page',
|
||||
description: 'Read a specific page from a SharePoint site',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -62,48 +69,56 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
description:
|
||||
'Maximum number of pages to return when listing all pages (default: 10, max: 50)',
|
||||
},
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full @odata.nextLink URL from a previous Microsoft Graph page response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
// Use specific site if provided, otherwise use root site
|
||||
const siteId = params.siteId || params.siteSelector || 'root'
|
||||
if (params.nextPageUrl) {
|
||||
return assertGraphNextPageUrl(params.nextPageUrl)
|
||||
}
|
||||
|
||||
const siteId = optionalTrim(params.siteId) || optionalTrim(params.siteSelector) || 'root'
|
||||
const pageId = optionalTrim(params.pageId)
|
||||
|
||||
let baseUrl: string
|
||||
if (params.pageId) {
|
||||
// Read specific page by ID
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages/${params.pageId}`
|
||||
if (pageId) {
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages/${pageId}/microsoft.graph.sitePage`
|
||||
} else {
|
||||
// List all pages (with optional filtering by name)
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages`
|
||||
baseUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages/microsoft.graph.sitePage`
|
||||
}
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
|
||||
// Use Microsoft Graph $select parameter to get page details
|
||||
// Only include valid properties for SharePoint pages
|
||||
url.searchParams.append(
|
||||
'$select',
|
||||
'id,name,title,webUrl,pageLayout,createdDateTime,lastModifiedDateTime'
|
||||
'id,name,title,webUrl,pageLayout,description,createdDateTime,lastModifiedDateTime'
|
||||
)
|
||||
|
||||
// If searching by name, add filter
|
||||
if (params.pageName && !params.pageId) {
|
||||
// Try to handle both with and without .aspx extension
|
||||
const pageName = params.pageName
|
||||
if (params.pageName && !pageId) {
|
||||
const pageName = params.pageName.trim()
|
||||
const pageNameWithAspx = pageName.endsWith('.aspx') ? pageName : `${pageName}.aspx`
|
||||
const escapedPageName = escapeODataString(pageName)
|
||||
const escapedPageNameWithAspx = escapeODataString(pageNameWithAspx)
|
||||
|
||||
// Search for exact match first, then with .aspx if needed
|
||||
url.searchParams.append('$filter', `name eq '${pageName}' or name eq '${pageNameWithAspx}'`)
|
||||
url.searchParams.append('$top', '10') // Get more results to find matches
|
||||
} else if (!params.pageId && !params.pageName) {
|
||||
// When listing all pages, apply maxPages limit
|
||||
const maxPages = Math.min(params.maxPages || 10, 50) // Default 10, max 50
|
||||
url.searchParams.append(
|
||||
'$filter',
|
||||
`name eq '${escapedPageName}' or name eq '${escapedPageNameWithAspx}'`
|
||||
)
|
||||
url.searchParams.append('$top', '10')
|
||||
} else if (!pageId && !params.pageName) {
|
||||
const requestedMaxPages =
|
||||
typeof params.maxPages === 'number' ? params.maxPages : Number(params.maxPages || 10)
|
||||
const maxPages = Math.min(Number.isFinite(requestedMaxPages) ? requestedMaxPages : 10, 50)
|
||||
url.searchParams.append('$top', maxPages.toString())
|
||||
}
|
||||
|
||||
// Only expand content when getting a specific page by ID
|
||||
if (params.pageId) {
|
||||
if (pageId) {
|
||||
url.searchParams.append('$expand', 'canvasLayout')
|
||||
}
|
||||
|
||||
@@ -112,7 +127,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
logger.info('SharePoint API URL', {
|
||||
finalUrl,
|
||||
siteId,
|
||||
pageId: params.pageId,
|
||||
pageId,
|
||||
pageName: params.pageName,
|
||||
searchParams: Object.fromEntries(url.searchParams),
|
||||
})
|
||||
@@ -138,7 +153,6 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
})
|
||||
|
||||
if (params?.pageId) {
|
||||
// Direct page access - return single page
|
||||
const pageData = data
|
||||
const contentData = {
|
||||
content: extractTextFromCanvasLayout(data.canvasLayout),
|
||||
@@ -154,6 +168,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: pageData.title || pageData.name!,
|
||||
webUrl: pageData.webUrl!,
|
||||
pageLayout: pageData.pageLayout,
|
||||
description: pageData.description ?? null,
|
||||
createdDateTime: pageData.createdDateTime,
|
||||
lastModifiedDateTime: pageData.lastModifiedDateTime,
|
||||
},
|
||||
@@ -161,7 +176,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
},
|
||||
}
|
||||
}
|
||||
// Multiple pages or search by name
|
||||
|
||||
if (!data.value || data.value.length === 0) {
|
||||
logger.info('No pages found', {
|
||||
searchName: params?.pageName,
|
||||
@@ -189,7 +204,6 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
})
|
||||
|
||||
if (params?.pageName) {
|
||||
// Search by name - return single page (first match)
|
||||
const pageData = data.value[0]
|
||||
const siteId = params?.siteId || params?.siteSelector || 'root'
|
||||
const contentUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages/${pageData.id}/microsoft.graph.sitePage?$expand=canvasLayout`
|
||||
@@ -230,6 +244,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: pageData.title || pageData.name,
|
||||
webUrl: pageData.webUrl,
|
||||
pageLayout: pageData.pageLayout,
|
||||
description: pageData.description ?? null,
|
||||
createdDateTime: pageData.createdDateTime,
|
||||
lastModifiedDateTime: pageData.lastModifiedDateTime,
|
||||
},
|
||||
@@ -237,16 +252,16 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
},
|
||||
}
|
||||
}
|
||||
// List all pages - return multiple pages with content
|
||||
|
||||
const siteId = params?.siteId || params?.siteSelector || 'root'
|
||||
const pagesWithContent = []
|
||||
const nextPageUrl = getGraphNextPageUrl(data)
|
||||
|
||||
logger.info('Fetching content for all pages', {
|
||||
totalPages: data.value.length,
|
||||
siteId,
|
||||
})
|
||||
|
||||
// Fetch content for each page
|
||||
for (const pageInfo of data.value) {
|
||||
const contentUrl = `https://graph.microsoft.com/v1.0/sites/${siteId}/pages/${pageInfo.id}/microsoft.graph.sitePage?$expand=canvasLayout`
|
||||
|
||||
@@ -280,6 +295,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: pageInfo.title || pageInfo.name,
|
||||
webUrl: pageInfo.webUrl,
|
||||
pageLayout: pageInfo.pageLayout,
|
||||
description: pageInfo.description ?? null,
|
||||
createdDateTime: pageInfo.createdDateTime,
|
||||
lastModifiedDateTime: pageInfo.lastModifiedDateTime,
|
||||
},
|
||||
@@ -292,7 +308,6 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
error: toError(error).message,
|
||||
})
|
||||
|
||||
// Still add the page without content
|
||||
pagesWithContent.push({
|
||||
page: {
|
||||
id: pageInfo.id,
|
||||
@@ -300,6 +315,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: pageInfo.title || pageInfo.name,
|
||||
webUrl: pageInfo.webUrl,
|
||||
pageLayout: pageInfo.pageLayout,
|
||||
description: pageInfo.description ?? null,
|
||||
createdDateTime: pageInfo.createdDateTime,
|
||||
lastModifiedDateTime: pageInfo.lastModifiedDateTime,
|
||||
},
|
||||
@@ -320,6 +336,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
output: {
|
||||
pages: pagesWithContent,
|
||||
totalPages: pagesWithContent.length,
|
||||
nextPageUrl,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -334,6 +351,7 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: { type: 'string', description: 'The title of the page' },
|
||||
webUrl: { type: 'string', description: 'The URL to access the page' },
|
||||
pageLayout: { type: 'string', description: 'The layout type of the page' },
|
||||
description: { type: 'string', description: 'The description of the page', optional: true },
|
||||
createdDateTime: { type: 'string', description: 'When the page was created' },
|
||||
lastModifiedDateTime: { type: 'string', description: 'When the page was last modified' },
|
||||
},
|
||||
@@ -352,6 +370,11 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
title: { type: 'string', description: 'The title of the page' },
|
||||
webUrl: { type: 'string', description: 'The URL to access the page' },
|
||||
pageLayout: { type: 'string', description: 'The layout type of the page' },
|
||||
description: {
|
||||
type: 'string',
|
||||
description: 'The description of the page',
|
||||
optional: true,
|
||||
},
|
||||
createdDateTime: { type: 'string', description: 'When the page was created' },
|
||||
lastModifiedDateTime: {
|
||||
type: 'string',
|
||||
@@ -381,5 +404,10 @@ export const readPageTool: ToolConfig<SharepointToolParams, SharepointReadPageRe
|
||||
},
|
||||
},
|
||||
totalPages: { type: 'number', description: 'Total number of pages found' },
|
||||
nextPageUrl: {
|
||||
type: 'string',
|
||||
description: 'Full Microsoft Graph @odata.nextLink URL for the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface SharepointSite {
|
||||
name: string
|
||||
displayName: string
|
||||
webUrl: string
|
||||
description?: string
|
||||
description?: string | null
|
||||
createdDateTime?: string
|
||||
lastModifiedDateTime?: string
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export interface SharepointPage {
|
||||
id?: string
|
||||
name: string
|
||||
title: string
|
||||
description?: string | null
|
||||
webUrl?: string
|
||||
pageLayout?: string
|
||||
createdDateTime?: string
|
||||
@@ -95,7 +96,7 @@ export interface SharepointList {
|
||||
export interface SharepointListSitesResponse extends ToolResponse {
|
||||
output: {
|
||||
sites: SharepointSite[]
|
||||
nextPageToken?: string
|
||||
nextPageUrl?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +117,7 @@ export interface SharepointReadPageResponse extends ToolResponse {
|
||||
pages?: SharepointPageWithContent[]
|
||||
content?: SharepointPageContent
|
||||
totalPages?: number
|
||||
nextPageUrl?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ export interface SharepointReadSiteResponse extends ToolResponse {
|
||||
name: string
|
||||
displayName: string
|
||||
webUrl: string
|
||||
description?: string
|
||||
description?: string | null
|
||||
createdDateTime?: string
|
||||
lastModifiedDateTime?: string
|
||||
isPersonalSite?: boolean
|
||||
@@ -142,10 +144,11 @@ export interface SharepointReadSiteResponse extends ToolResponse {
|
||||
name: string
|
||||
displayName: string
|
||||
webUrl: string
|
||||
description?: string
|
||||
description?: string | null
|
||||
createdDateTime?: string
|
||||
lastModifiedDateTime?: string
|
||||
}>
|
||||
nextPageUrl?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,12 +158,12 @@ export interface SharepointToolParams {
|
||||
siteSelector?: string
|
||||
pageId?: string
|
||||
pageName?: string
|
||||
pageContent?: string
|
||||
pageContent?: string | unknown[] | { columns?: unknown[] }
|
||||
pageTitle?: string
|
||||
publishingState?: string
|
||||
query?: string
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
nextPageUrl?: string
|
||||
hostname?: string
|
||||
serverRelativePath?: string
|
||||
groupId?: string
|
||||
@@ -188,6 +191,7 @@ export interface GraphApiResponse {
|
||||
id?: string
|
||||
name?: string
|
||||
title?: string
|
||||
description?: string | null
|
||||
webUrl?: string
|
||||
pageLayout?: string
|
||||
createdDateTime?: string
|
||||
@@ -203,6 +207,7 @@ export interface GraphApiPageItem {
|
||||
id: string
|
||||
name: string
|
||||
title?: string
|
||||
description?: string | null
|
||||
webUrl?: string
|
||||
pageLayout?: string
|
||||
createdDateTime?: string
|
||||
@@ -227,36 +232,6 @@ export interface CanvasLayout {
|
||||
}>
|
||||
}
|
||||
|
||||
export interface SharepointReadSiteResponse extends ToolResponse {
|
||||
output: {
|
||||
site?: {
|
||||
id: string
|
||||
name: string
|
||||
displayName: string
|
||||
webUrl: string
|
||||
description?: string
|
||||
createdDateTime?: string
|
||||
lastModifiedDateTime?: string
|
||||
isPersonalSite?: boolean
|
||||
root?: {
|
||||
serverRelativeUrl: string
|
||||
}
|
||||
siteCollection?: {
|
||||
hostname: string
|
||||
}
|
||||
}
|
||||
sites?: Array<{
|
||||
id: string
|
||||
name: string
|
||||
displayName: string
|
||||
webUrl: string
|
||||
description?: string
|
||||
createdDateTime?: string
|
||||
lastModifiedDateTime?: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export type SharepointResponse =
|
||||
| SharepointListSitesResponse
|
||||
| SharepointCreatePageResponse
|
||||
@@ -272,7 +247,8 @@ export interface SharepointGetListResponse extends ToolResponse {
|
||||
output: {
|
||||
list?: SharepointList
|
||||
lists?: SharepointList[]
|
||||
nextPageToken?: string
|
||||
items?: SharepointListItem[]
|
||||
nextPageUrl?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,9 +285,25 @@ export interface SharepointUploadedFile {
|
||||
lastModifiedDateTime?: string
|
||||
}
|
||||
|
||||
export interface SharepointSkippedFile {
|
||||
name: string
|
||||
size: number
|
||||
limit: number
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface SharepointUploadError {
|
||||
name: string
|
||||
error: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
export interface SharepointUploadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
uploadedFiles: SharepointUploadedFile[]
|
||||
fileCount: number
|
||||
skippedFiles?: SharepointSkippedFile[]
|
||||
skippedCount?: number
|
||||
errors?: SharepointUploadError[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
SharepointToolParams,
|
||||
SharepointUpdateListItemResponse,
|
||||
} from '@/tools/sharepoint/types'
|
||||
import { optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('SharePointUpdateListItem')
|
||||
@@ -14,7 +15,7 @@ export const updateListItemTool: ToolConfig<
|
||||
id: 'sharepoint_update_list',
|
||||
name: 'Update SharePoint List Item',
|
||||
description: 'Update the properties (fields) on a SharePoint list item',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -42,7 +43,7 @@ export const updateListItemTool: ToolConfig<
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The ID of the list containing the item. Example: b!abc123def456 or a GUID like 12345678-1234-1234-1234-123456789012',
|
||||
@@ -54,7 +55,7 @@ export const updateListItemTool: ToolConfig<
|
||||
description: 'The ID of the list item to update. Example: 1, 42, or 123',
|
||||
},
|
||||
listItemFields: {
|
||||
type: 'object',
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Field values to update on the list item',
|
||||
@@ -63,13 +64,15 @@ export const updateListItemTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const siteId = params.siteId || params.siteSelector || 'root'
|
||||
if (!params.itemId) throw new Error('itemId is required')
|
||||
if (!params.listId) {
|
||||
const siteId = optionalTrim(params.siteId) || optionalTrim(params.siteSelector) || 'root'
|
||||
const itemId = optionalTrim(params.itemId)
|
||||
const listId = optionalTrim(params.listId)
|
||||
if (!itemId) throw new Error('itemId is required')
|
||||
if (!listId) {
|
||||
throw new Error('listId must be provided')
|
||||
}
|
||||
const listSegment = params.listId
|
||||
return `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}/items/${params.itemId}/fields`
|
||||
const listSegment = encodeURIComponent(listId)
|
||||
return `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}/items/${encodeURIComponent(itemId)}/fields`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { SharepointToolParams, SharepointUploadFileResponse } from '@/tools/sharepoint/types'
|
||||
import { optionalTrim } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const uploadFileTool: ToolConfig<SharepointToolParams, SharepointUploadFileResponse> = {
|
||||
id: 'sharepoint_upload_file',
|
||||
name: 'Upload File to SharePoint',
|
||||
description: 'Upload files to a SharePoint document library',
|
||||
version: '1.0',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
@@ -47,7 +48,7 @@ export const uploadFileTool: ToolConfig<SharepointToolParams, SharepointUploadFi
|
||||
},
|
||||
files: {
|
||||
type: 'file[]',
|
||||
required: false,
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Files to upload to SharePoint',
|
||||
},
|
||||
@@ -62,10 +63,10 @@ export const uploadFileTool: ToolConfig<SharepointToolParams, SharepointUploadFi
|
||||
body: (params: SharepointToolParams) => {
|
||||
return {
|
||||
accessToken: params.accessToken,
|
||||
siteId: params.siteId || 'root',
|
||||
driveId: params.driveId || null,
|
||||
folderPath: params.folderPath || null,
|
||||
fileName: params.fileName || null,
|
||||
siteId: optionalTrim(params.siteId) || 'root',
|
||||
driveId: optionalTrim(params.driveId) || null,
|
||||
folderPath: optionalTrim(params.folderPath) || null,
|
||||
fileName: optionalTrim(params.fileName) || null,
|
||||
files: params.files || null,
|
||||
}
|
||||
},
|
||||
@@ -73,15 +74,17 @@ export const uploadFileTool: ToolConfig<SharepointToolParams, SharepointUploadFi
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload files to SharePoint')
|
||||
}
|
||||
const output = data.output ?? {}
|
||||
return {
|
||||
success: true,
|
||||
success: Boolean(data.success),
|
||||
output: {
|
||||
uploadedFiles: data.output.uploadedFiles,
|
||||
fileCount: data.output.fileCount,
|
||||
uploadedFiles: output.uploadedFiles ?? [],
|
||||
fileCount: output.fileCount ?? 0,
|
||||
skippedFiles: output.skippedFiles ?? [],
|
||||
skippedCount: output.skippedCount ?? 0,
|
||||
errors: output.errors ?? [],
|
||||
},
|
||||
error: data.success ? undefined : data.error || 'Failed to upload files to SharePoint',
|
||||
}
|
||||
},
|
||||
|
||||
@@ -105,5 +108,38 @@ export const uploadFileTool: ToolConfig<SharepointToolParams, SharepointUploadFi
|
||||
type: 'number',
|
||||
description: 'Number of files uploaded',
|
||||
},
|
||||
skippedFiles: {
|
||||
type: 'array',
|
||||
description: 'Files that were skipped before upload',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
limit: { type: 'number', description: 'Upload size limit in bytes' },
|
||||
reason: { type: 'string', description: 'Reason the file was skipped' },
|
||||
},
|
||||
},
|
||||
},
|
||||
skippedCount: {
|
||||
type: 'number',
|
||||
description: 'Number of files skipped',
|
||||
},
|
||||
errors: {
|
||||
type: 'array',
|
||||
description: 'Per-file upload errors',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
error: { type: 'string', description: 'Error message' },
|
||||
status: {
|
||||
type: 'number',
|
||||
description: 'HTTP status from Microsoft Graph',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,30 @@ import type { CanvasLayout } from '@/tools/sharepoint/types'
|
||||
|
||||
const logger = createLogger('SharepointUtils')
|
||||
|
||||
export function optionalTrim(value: unknown): string | undefined {
|
||||
if (value === undefined || value === null) return undefined
|
||||
const trimmed = String(value).trim()
|
||||
return trimmed || undefined
|
||||
}
|
||||
|
||||
export function escapeODataString(value: string): string {
|
||||
return value.replace(/'/g, "''")
|
||||
}
|
||||
|
||||
export function getGraphNextPageUrl(data: object): string | undefined {
|
||||
const nextLink = (data as Record<string, unknown>)['@odata.nextLink']
|
||||
return typeof nextLink === 'string' ? nextLink : undefined
|
||||
}
|
||||
|
||||
export function assertGraphNextPageUrl(nextPageUrl: string): string {
|
||||
const trimmed = nextPageUrl.trim()
|
||||
const url = new URL(trimmed)
|
||||
if (url.origin !== 'https://graph.microsoft.com') {
|
||||
throw new Error('nextPageUrl must be a Microsoft Graph @odata.nextLink URL')
|
||||
}
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
function stripHtmlTags(html: string): string {
|
||||
let text = html
|
||||
let previous: string
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailAccountAddedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailAccountAddedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_account_added',
|
||||
name: 'Email Bison Email Account Added',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a sender email account is added to Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_account_added',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Account Added'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_account_added'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailAccountAddedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailAccountDisconnectedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailAccountDisconnectedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_account_disconnected',
|
||||
name: 'Email Bison Email Account Disconnected',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a sender email account disconnects in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_account_disconnected',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Account Disconnected'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_account_disconnected'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailAccountDisconnectedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailAccountReconnectedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailAccountReconnectedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_account_reconnected',
|
||||
name: 'Email Bison Email Account Reconnected',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a sender email account reconnects in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_account_reconnected',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Account Reconnected'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_account_reconnected'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailAccountReconnectedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailAccountRemovedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailAccountRemovedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_account_removed',
|
||||
name: 'Email Bison Email Account Removed',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a sender email account is removed from Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_account_removed',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Account Removed'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_account_removed'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailAccountRemovedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailBouncedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailBouncedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_bounced',
|
||||
name: 'Email Bison Email Bounced',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when an Email Bison campaign email bounces',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_bounced',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Bounced'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_bounced'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailBouncedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailOpenedOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailOpenedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_opened',
|
||||
name: 'Email Bison Email Opened',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when an Email Bison campaign email is opened',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_opened',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Opened'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_opened'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailOpenedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonEmailSentOutputs,
|
||||
buildEmailBisonExtraFields,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonEmailSentTrigger: TriggerConfig = {
|
||||
id: 'emailbison_email_sent',
|
||||
name: 'Email Bison Email Sent',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a campaign email is sent in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_email_sent',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
includeDropdown: true,
|
||||
setupInstructions: emailBisonSetupInstructions('Email Sent'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_email_sent'),
|
||||
}),
|
||||
outputs: buildEmailBisonEmailSentOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export { emailBisonEmailAccountAddedTrigger } from '@/triggers/emailbison/email_account_added'
|
||||
export { emailBisonEmailAccountDisconnectedTrigger } from '@/triggers/emailbison/email_account_disconnected'
|
||||
export { emailBisonEmailAccountReconnectedTrigger } from '@/triggers/emailbison/email_account_reconnected'
|
||||
export { emailBisonEmailAccountRemovedTrigger } from '@/triggers/emailbison/email_account_removed'
|
||||
export { emailBisonEmailBouncedTrigger } from '@/triggers/emailbison/email_bounced'
|
||||
export { emailBisonEmailOpenedTrigger } from '@/triggers/emailbison/email_opened'
|
||||
export { emailBisonEmailSentTrigger } from '@/triggers/emailbison/email_sent'
|
||||
export { emailBisonLeadFirstContactedTrigger } from '@/triggers/emailbison/lead_first_contacted'
|
||||
export { emailBisonLeadInterestedTrigger } from '@/triggers/emailbison/lead_interested'
|
||||
export { emailBisonLeadRepliedTrigger } from '@/triggers/emailbison/lead_replied'
|
||||
export { emailBisonLeadUnsubscribedTrigger } from '@/triggers/emailbison/lead_unsubscribed'
|
||||
export { emailBisonManualEmailSentTrigger } from '@/triggers/emailbison/manual_email_sent'
|
||||
export { emailBisonTagAttachedTrigger } from '@/triggers/emailbison/tag_attached'
|
||||
export { emailBisonTagRemovedTrigger } from '@/triggers/emailbison/tag_removed'
|
||||
export { emailBisonUntrackedReplyReceivedTrigger } from '@/triggers/emailbison/untracked_reply_received'
|
||||
export { emailBisonWarmupDisabledCausingBouncesTrigger } from '@/triggers/emailbison/warmup_disabled_causing_bounces'
|
||||
export { emailBisonWarmupDisabledReceivingBouncesTrigger } from '@/triggers/emailbison/warmup_disabled_receiving_bounces'
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonLeadFirstContactedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonLeadFirstContactedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_lead_first_contacted',
|
||||
name: 'Email Bison Contact First Emailed',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a contact receives their first campaign email in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_lead_first_contacted',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Contact First Emailed'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_lead_first_contacted'),
|
||||
}),
|
||||
outputs: buildEmailBisonLeadFirstContactedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonLeadInterestedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonLeadInterestedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_lead_interested',
|
||||
name: 'Email Bison Contact Interested',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a reply is marked interested in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_lead_interested',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Contact Interested'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_lead_interested'),
|
||||
}),
|
||||
outputs: buildEmailBisonLeadInterestedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonLeadRepliedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonLeadRepliedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_lead_replied',
|
||||
name: 'Email Bison Contact Replied',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a campaign lead replies in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_lead_replied',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Contact Replied'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_lead_replied'),
|
||||
}),
|
||||
outputs: buildEmailBisonLeadRepliedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonLeadUnsubscribedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonLeadUnsubscribedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_lead_unsubscribed',
|
||||
name: 'Email Bison Contact Unsubscribed',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a contact unsubscribes in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_lead_unsubscribed',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Contact Unsubscribed'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_lead_unsubscribed'),
|
||||
}),
|
||||
outputs: buildEmailBisonLeadUnsubscribedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonManualEmailSentOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonManualEmailSentTrigger: TriggerConfig = {
|
||||
id: 'emailbison_manual_email_sent',
|
||||
name: 'Email Bison Manual Email Sent',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a manual email is sent in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_manual_email_sent',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Manual Email Sent'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_manual_email_sent'),
|
||||
}),
|
||||
outputs: buildEmailBisonManualEmailSentOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonTagAttachedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonTagAttachedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_tag_attached',
|
||||
name: 'Email Bison Tag Attached',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a custom tag is attached to a taggable in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_tag_attached',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Tag Attached'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_tag_attached'),
|
||||
}),
|
||||
outputs: buildEmailBisonTagAttachedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonTagRemovedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonTagRemovedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_tag_removed',
|
||||
name: 'Email Bison Tag Removed',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when a custom tag is removed from a taggable in Email Bison',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_tag_removed',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Tag Removed'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_tag_removed'),
|
||||
}),
|
||||
outputs: buildEmailBisonTagRemovedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonUntrackedReplyReceivedOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonUntrackedReplyReceivedTrigger: TriggerConfig = {
|
||||
id: 'emailbison_untracked_reply_received',
|
||||
name: 'Email Bison Untracked Reply Received',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when Email Bison receives a reply not tied to a scheduled campaign email',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_untracked_reply_received',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Untracked Reply Received'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_untracked_reply_received'),
|
||||
}),
|
||||
outputs: buildEmailBisonUntrackedReplyReceivedOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { TriggerOutput } from '@/triggers/types'
|
||||
|
||||
export const EMAILBISON_TRIGGER_TO_EVENT_TYPE = {
|
||||
emailbison_email_sent: 'email_sent',
|
||||
emailbison_lead_first_contacted: 'lead_first_contacted',
|
||||
emailbison_lead_replied: 'lead_replied',
|
||||
emailbison_lead_interested: 'lead_interested',
|
||||
emailbison_lead_unsubscribed: 'lead_unsubscribed',
|
||||
emailbison_untracked_reply_received: 'untracked_reply_received',
|
||||
emailbison_email_opened: 'email_opened',
|
||||
emailbison_email_bounced: 'email_bounced',
|
||||
emailbison_email_account_added: 'email_account_added',
|
||||
emailbison_email_account_removed: 'email_account_removed',
|
||||
emailbison_email_account_disconnected: 'email_account_disconnected',
|
||||
emailbison_email_account_reconnected: 'email_account_reconnected',
|
||||
emailbison_manual_email_sent: 'manual_email_sent',
|
||||
emailbison_tag_attached: 'tag_attached',
|
||||
emailbison_tag_removed: 'tag_removed',
|
||||
emailbison_warmup_disabled_receiving_bounces: 'warmup_disabled_receiving_bounces',
|
||||
emailbison_warmup_disabled_causing_bounces: 'warmup_disabled_causing_bounces',
|
||||
} as const
|
||||
|
||||
export const emailBisonTriggerOptions = [
|
||||
{ label: 'Email Sent', id: 'emailbison_email_sent' },
|
||||
{ label: 'Contact First Emailed', id: 'emailbison_lead_first_contacted' },
|
||||
{ label: 'Contact Replied', id: 'emailbison_lead_replied' },
|
||||
{ label: 'Contact Interested', id: 'emailbison_lead_interested' },
|
||||
{ label: 'Contact Unsubscribed', id: 'emailbison_lead_unsubscribed' },
|
||||
{ label: 'Untracked Reply Received', id: 'emailbison_untracked_reply_received' },
|
||||
{ label: 'Email Opened', id: 'emailbison_email_opened' },
|
||||
{ label: 'Email Bounced', id: 'emailbison_email_bounced' },
|
||||
{ label: 'Email Account Added', id: 'emailbison_email_account_added' },
|
||||
{ label: 'Email Account Removed', id: 'emailbison_email_account_removed' },
|
||||
{ label: 'Email Account Disconnected', id: 'emailbison_email_account_disconnected' },
|
||||
{ label: 'Email Account Reconnected', id: 'emailbison_email_account_reconnected' },
|
||||
{ label: 'Manual Email Sent', id: 'emailbison_manual_email_sent' },
|
||||
{ label: 'Tag Attached', id: 'emailbison_tag_attached' },
|
||||
{ label: 'Tag Removed', id: 'emailbison_tag_removed' },
|
||||
{
|
||||
label: 'Warmup Disabled Receiving Bounces',
|
||||
id: 'emailbison_warmup_disabled_receiving_bounces',
|
||||
},
|
||||
{
|
||||
label: 'Warmup Disabled Causing Bounces',
|
||||
id: 'emailbison_warmup_disabled_causing_bounces',
|
||||
},
|
||||
]
|
||||
|
||||
export function emailBisonSetupInstructions(eventType: string): string {
|
||||
const instructions = [
|
||||
'Create an Email Bison API token in <strong>Settings > Developer API</strong>.',
|
||||
'Enter the <strong>Instance URL</strong> from Email Bison’s webhook payload, Full API Reference, or exported Postman collection.',
|
||||
`Click <strong>Save Configuration</strong> to automatically create an Email Bison webhook for <strong>${eventType}</strong>.`,
|
||||
'The webhook will be automatically deleted from Email Bison when this trigger is removed.',
|
||||
]
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
export function buildEmailBisonExtraFields(triggerId: string): SubBlockConfig[] {
|
||||
return [
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Email Bison API token',
|
||||
password: true,
|
||||
required: true,
|
||||
paramVisibility: 'user-only',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
},
|
||||
{
|
||||
id: 'apiBaseUrl',
|
||||
title: 'Instance URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://your-emailbison-workspace.com',
|
||||
required: true,
|
||||
paramVisibility: 'user-only',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function buildEmailBisonOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
eventType: { type: 'string', description: 'Email Bison webhook event type' },
|
||||
eventName: { type: 'string', description: 'Human-readable Email Bison event name' },
|
||||
instanceUrl: { type: 'string', description: 'Email Bison instance URL' },
|
||||
workspaceId: { type: 'number', description: 'Email Bison workspace ID' },
|
||||
workspaceName: { type: 'string', description: 'Email Bison workspace name' },
|
||||
event: { type: 'json', description: 'Raw Email Bison event metadata object' },
|
||||
data: { type: 'json', description: 'Raw Email Bison event data object' },
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailSentOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
scheduledEmail: {
|
||||
id: { type: 'number', description: 'Scheduled email ID' },
|
||||
lead_id: { type: 'number', description: 'Lead ID' },
|
||||
sequence_step_id: { type: 'number', description: 'Sequence step ID' },
|
||||
sequence_step_order: { type: 'number', description: 'Sequence step order' },
|
||||
sequence_step_variant: { type: 'number', description: 'Sequence step variant' },
|
||||
email_subject: { type: 'string', description: 'Email subject' },
|
||||
email_body: { type: 'string', description: 'Email body HTML' },
|
||||
status: { type: 'string', description: 'Scheduled email status' },
|
||||
scheduled_date_est: { type: 'string', description: 'Scheduled date in EST' },
|
||||
scheduled_date_local: { type: 'string', description: 'Scheduled date in local timezone' },
|
||||
local_timezone: { type: 'string', description: 'Scheduled email local timezone' },
|
||||
sent_at: { type: 'string', description: 'Email sent timestamp' },
|
||||
opens: { type: 'number', description: 'Open count' },
|
||||
replies: { type: 'number', description: 'Reply count' },
|
||||
unique_opens: { type: 'number', description: 'Unique open count' },
|
||||
unique_replies: { type: 'number', description: 'Unique reply count' },
|
||||
interested: { type: 'string', description: 'Interested status' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
},
|
||||
campaignEvent: {
|
||||
id: { type: 'number', description: 'Campaign event ID' },
|
||||
event_type: { type: 'string', description: 'Campaign event type' },
|
||||
created_at_local: { type: 'string', description: 'Campaign event local creation timestamp' },
|
||||
local_timezone: { type: 'string', description: 'Campaign event local timezone' },
|
||||
created_at: { type: 'string', description: 'Campaign event creation timestamp' },
|
||||
},
|
||||
lead: {
|
||||
id: { type: 'number', description: 'Lead ID' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
status: { type: 'string', description: 'Lead status' },
|
||||
title: { type: 'string', description: 'Lead title' },
|
||||
company: { type: 'string', description: 'Lead company' },
|
||||
custom_variables: { type: 'json', description: 'Lead custom variables' },
|
||||
emails_sent: { type: 'number', description: 'Lead emails sent count' },
|
||||
opens: { type: 'number', description: 'Lead open count' },
|
||||
unique_opens: { type: 'number', description: 'Lead unique open count' },
|
||||
replies: { type: 'number', description: 'Lead reply count' },
|
||||
unique_replies: { type: 'number', description: 'Lead unique reply count' },
|
||||
bounces: { type: 'number', description: 'Lead bounce count' },
|
||||
},
|
||||
campaign: {
|
||||
id: { type: 'number', description: 'Campaign ID' },
|
||||
name: { type: 'string', description: 'Campaign name' },
|
||||
},
|
||||
senderEmail: {
|
||||
id: { type: 'number', description: 'Sender email ID' },
|
||||
name: { type: 'string', description: 'Sender email name' },
|
||||
email: { type: 'string', description: 'Sender email address' },
|
||||
status: { type: 'string', description: 'Sender email status' },
|
||||
account_type: { type: 'string', description: 'Sender email connection type' },
|
||||
daily_limit: { type: 'number', description: 'Sender email daily limit' },
|
||||
emails_sent: { type: 'number', description: 'Sender email sent count' },
|
||||
replied: { type: 'number', description: 'Sender email replied count' },
|
||||
opened: { type: 'number', description: 'Sender email opened count' },
|
||||
unsubscribed: { type: 'number', description: 'Sender email unsubscribed count' },
|
||||
bounced: { type: 'number', description: 'Sender email bounced count' },
|
||||
unique_replies: { type: 'number', description: 'Sender email unique reply count' },
|
||||
unique_opens: { type: 'number', description: 'Sender email unique open count' },
|
||||
total_leads_contacted: { type: 'number', description: 'Sender email total leads contacted' },
|
||||
interested: { type: 'number', description: 'Sender email interested count' },
|
||||
created_at: { type: 'string', description: 'Sender email creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Sender email update timestamp' },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonLeadFirstContactedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailSentOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonLeadUnsubscribedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailSentOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailOpenedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailSentOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonLeadRepliedOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
reply: {
|
||||
id: { type: 'number', description: 'Reply ID' },
|
||||
uuid: { type: 'string', description: 'Reply UUID' },
|
||||
email_subject: { type: 'string', description: 'Reply email subject' },
|
||||
interested: { type: 'boolean', description: 'Whether the reply is marked interested' },
|
||||
automated_reply: { type: 'boolean', description: 'Whether the reply is automated' },
|
||||
html_body: { type: 'string', description: 'Reply HTML body' },
|
||||
text_body: { type: 'string', description: 'Reply plain text body' },
|
||||
raw_body: { type: 'string', description: 'Raw MIME reply body' },
|
||||
headers: { type: 'string', description: 'Encoded raw email headers' },
|
||||
date_received: { type: 'string', description: 'Reply received timestamp' },
|
||||
from_name: { type: 'string', description: 'Reply sender name' },
|
||||
from_email_address: { type: 'string', description: 'Reply sender email address' },
|
||||
primary_to_email_address: { type: 'string', description: 'Primary recipient email address' },
|
||||
to: { type: 'json', description: 'Reply To recipients' },
|
||||
cc: { type: 'json', description: 'Reply CC recipients' },
|
||||
bcc: { type: 'json', description: 'Reply BCC recipients' },
|
||||
parent_id: { type: 'number', description: 'Parent reply ID' },
|
||||
reply_type: { type: 'string', description: 'Reply type' },
|
||||
folder: { type: 'string', description: 'Reply folder' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
created_at: { type: 'string', description: 'Reply creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Reply update timestamp' },
|
||||
attachments: { type: 'json', description: 'Reply attachments' },
|
||||
},
|
||||
campaignEvent: {
|
||||
id: { type: 'number', description: 'Campaign event ID' },
|
||||
event_type: { type: 'string', description: 'Campaign event type' },
|
||||
created_at_local: { type: 'string', description: 'Campaign event local creation timestamp' },
|
||||
local_timezone: { type: 'string', description: 'Campaign event local timezone' },
|
||||
created_at: { type: 'string', description: 'Campaign event creation timestamp' },
|
||||
},
|
||||
lead: {
|
||||
id: { type: 'number', description: 'Lead ID' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
status: { type: 'string', description: 'Lead status' },
|
||||
title: { type: 'string', description: 'Lead title' },
|
||||
company: { type: 'string', description: 'Lead company' },
|
||||
custom_variables: { type: 'json', description: 'Lead custom variables' },
|
||||
emails_sent: { type: 'number', description: 'Lead emails sent count' },
|
||||
opens: { type: 'number', description: 'Lead open count' },
|
||||
unique_opens: { type: 'number', description: 'Lead unique open count' },
|
||||
replies: { type: 'number', description: 'Lead reply count' },
|
||||
unique_replies: { type: 'number', description: 'Lead unique reply count' },
|
||||
bounces: { type: 'number', description: 'Lead bounce count' },
|
||||
},
|
||||
campaign: {
|
||||
id: { type: 'number', description: 'Campaign ID' },
|
||||
name: { type: 'string', description: 'Campaign name' },
|
||||
},
|
||||
scheduledEmail: {
|
||||
id: { type: 'number', description: 'Scheduled email ID' },
|
||||
sequence_step_id: { type: 'number', description: 'Sequence step ID' },
|
||||
sequence_step_order: { type: 'number', description: 'Sequence step order' },
|
||||
sequence_step_variant: { type: 'number', description: 'Sequence step variant' },
|
||||
status: { type: 'string', description: 'Scheduled email status' },
|
||||
scheduled_date_est: { type: 'string', description: 'Scheduled date in EST' },
|
||||
scheduled_date_local: { type: 'string', description: 'Scheduled date in local timezone' },
|
||||
local_timezone: { type: 'string', description: 'Scheduled email local timezone' },
|
||||
sent_at: { type: 'string', description: 'Email sent timestamp' },
|
||||
opens: { type: 'number', description: 'Open count' },
|
||||
replies: { type: 'number', description: 'Reply count' },
|
||||
unique_opens: { type: 'number', description: 'Unique open count' },
|
||||
unique_replies: { type: 'number', description: 'Unique reply count' },
|
||||
interested: { type: 'string', description: 'Interested status' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
},
|
||||
senderEmail: {
|
||||
id: { type: 'number', description: 'Sender email ID' },
|
||||
name: { type: 'string', description: 'Sender email name' },
|
||||
email: { type: 'string', description: 'Sender email address' },
|
||||
status: { type: 'string', description: 'Sender email status' },
|
||||
account_type: { type: 'string', description: 'Sender email connection type' },
|
||||
daily_limit: { type: 'number', description: 'Sender email daily limit' },
|
||||
emails_sent: { type: 'number', description: 'Sender email sent count' },
|
||||
replied: { type: 'number', description: 'Sender email replied count' },
|
||||
opened: { type: 'number', description: 'Sender email opened count' },
|
||||
unsubscribed: { type: 'number', description: 'Sender email unsubscribed count' },
|
||||
bounced: { type: 'number', description: 'Sender email bounced count' },
|
||||
unique_replies: { type: 'number', description: 'Sender email unique reply count' },
|
||||
unique_opens: { type: 'number', description: 'Sender email unique open count' },
|
||||
total_leads_contacted: { type: 'number', description: 'Sender email total leads contacted' },
|
||||
interested: { type: 'number', description: 'Sender email interested count' },
|
||||
created_at: { type: 'string', description: 'Sender email creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Sender email update timestamp' },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonLeadInterestedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonLeadRepliedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailBouncedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonLeadRepliedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonManualEmailSentOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
reply: {
|
||||
id: { type: 'number', description: 'Reply ID' },
|
||||
email_subject: { type: 'string', description: 'Reply email subject' },
|
||||
interested: { type: 'boolean', description: 'Whether the reply is marked interested' },
|
||||
automated_reply: { type: 'boolean', description: 'Whether the reply is automated' },
|
||||
html_body: { type: 'string', description: 'Reply HTML body' },
|
||||
text_body: { type: 'string', description: 'Reply plain text body' },
|
||||
raw_body: { type: 'string', description: 'Raw MIME reply body' },
|
||||
headers: { type: 'string', description: 'Encoded raw email headers' },
|
||||
date_received: { type: 'string', description: 'Reply received timestamp' },
|
||||
reply_type: { type: 'string', description: 'Reply type' },
|
||||
from_name: { type: 'string', description: 'Reply sender name' },
|
||||
from_email_address: { type: 'string', description: 'Reply sender email address' },
|
||||
primary_to_email_address: { type: 'string', description: 'Primary recipient email address' },
|
||||
to: { type: 'json', description: 'Reply To recipients' },
|
||||
cc: { type: 'json', description: 'Reply CC recipients' },
|
||||
bcc: { type: 'json', description: 'Reply BCC recipients' },
|
||||
parent_id: { type: 'json', description: 'Parent reply ID' },
|
||||
folder: { type: 'string', description: 'Reply folder' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
created_at: { type: 'string', description: 'Reply creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Reply update timestamp' },
|
||||
attachments: { type: 'json', description: 'Reply attachments' },
|
||||
},
|
||||
lead: {
|
||||
id: { type: 'number', description: 'Lead ID' },
|
||||
email: { type: 'string', description: 'Lead email address' },
|
||||
first_name: { type: 'string', description: 'Lead first name' },
|
||||
last_name: { type: 'string', description: 'Lead last name' },
|
||||
status: { type: 'string', description: 'Lead status' },
|
||||
title: { type: 'string', description: 'Lead title' },
|
||||
company: { type: 'string', description: 'Lead company' },
|
||||
custom_variables: { type: 'json', description: 'Lead custom variables' },
|
||||
emails_sent: { type: 'number', description: 'Lead emails sent count' },
|
||||
opens: { type: 'number', description: 'Lead open count' },
|
||||
unique_opens: { type: 'number', description: 'Lead unique open count' },
|
||||
replies: { type: 'number', description: 'Lead reply count' },
|
||||
unique_replies: { type: 'number', description: 'Lead unique reply count' },
|
||||
bounces: { type: 'number', description: 'Lead bounce count' },
|
||||
},
|
||||
campaign: {
|
||||
id: { type: 'number', description: 'Campaign ID' },
|
||||
name: { type: 'string', description: 'Campaign name' },
|
||||
},
|
||||
scheduledEmail: {
|
||||
id: { type: 'number', description: 'Scheduled email ID' },
|
||||
sequence_step_id: { type: 'number', description: 'Sequence step ID' },
|
||||
sequence_step_order: { type: 'number', description: 'Sequence step order' },
|
||||
sequence_step_variant: { type: 'number', description: 'Sequence step variant' },
|
||||
status: { type: 'string', description: 'Scheduled email status' },
|
||||
scheduled_date_est: { type: 'string', description: 'Scheduled date in EST' },
|
||||
scheduled_date_local: { type: 'string', description: 'Scheduled date in local timezone' },
|
||||
local_timezone: { type: 'string', description: 'Scheduled email local timezone' },
|
||||
sent_at: { type: 'string', description: 'Email sent timestamp' },
|
||||
opens: { type: 'number', description: 'Open count' },
|
||||
replies: { type: 'number', description: 'Reply count' },
|
||||
unique_opens: { type: 'number', description: 'Unique open count' },
|
||||
unique_replies: { type: 'number', description: 'Unique reply count' },
|
||||
interested: { type: 'json', description: 'Interested status' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
},
|
||||
senderEmail: {
|
||||
id: { type: 'number', description: 'Sender email ID' },
|
||||
name: { type: 'string', description: 'Sender email name' },
|
||||
email: { type: 'string', description: 'Sender email address' },
|
||||
status: { type: 'string', description: 'Sender email status' },
|
||||
account_type: { type: 'string', description: 'Sender email connection type' },
|
||||
daily_limit: { type: 'number', description: 'Sender email daily limit' },
|
||||
emails_sent: { type: 'number', description: 'Sender email sent count' },
|
||||
replied: { type: 'number', description: 'Sender email replied count' },
|
||||
opened: { type: 'number', description: 'Sender email opened count' },
|
||||
unsubscribed: { type: 'number', description: 'Sender email unsubscribed count' },
|
||||
bounced: { type: 'number', description: 'Sender email bounced count' },
|
||||
unique_replies: { type: 'number', description: 'Sender email unique reply count' },
|
||||
unique_opens: { type: 'number', description: 'Sender email unique open count' },
|
||||
total_leads_contacted: { type: 'number', description: 'Sender email total leads contacted' },
|
||||
interested: { type: 'number', description: 'Sender email interested count' },
|
||||
created_at: { type: 'string', description: 'Sender email creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Sender email update timestamp' },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonTagAttachedOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
tagId: { type: 'number', description: 'Email Bison tag ID' },
|
||||
tagName: { type: 'string', description: 'Email Bison tag name' },
|
||||
taggableId: { type: 'number', description: 'ID of the tagged resource' },
|
||||
taggableType: { type: 'string', description: 'Type of the tagged resource' },
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonTagRemovedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonTagAttachedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailAccountAddedOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
senderEmail: {
|
||||
id: { type: 'number', description: 'Sender email ID' },
|
||||
name: { type: 'string', description: 'Sender email name' },
|
||||
email: { type: 'string', description: 'Sender email address' },
|
||||
status: { type: 'string', description: 'Sender email status' },
|
||||
account_type: { type: 'string', description: 'Sender email connection type' },
|
||||
daily_limit: { type: 'number', description: 'Sender email daily limit' },
|
||||
emails_sent: { type: 'number', description: 'Sender email sent count' },
|
||||
replied: { type: 'number', description: 'Sender email replied count' },
|
||||
opened: { type: 'number', description: 'Sender email opened count' },
|
||||
unsubscribed: { type: 'number', description: 'Sender email unsubscribed count' },
|
||||
bounced: { type: 'number', description: 'Sender email bounced count' },
|
||||
unique_replies: { type: 'number', description: 'Sender email unique reply count' },
|
||||
unique_opens: { type: 'number', description: 'Sender email unique open count' },
|
||||
total_leads_contacted: { type: 'number', description: 'Sender email total leads contacted' },
|
||||
interested: { type: 'number', description: 'Sender email interested count' },
|
||||
created_at: { type: 'string', description: 'Sender email creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Sender email update timestamp' },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailAccountRemovedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailAccountAddedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailAccountDisconnectedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailAccountAddedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonEmailAccountReconnectedOutputs(): Record<string, TriggerOutput> {
|
||||
return buildEmailBisonEmailAccountAddedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonWarmupDisabledReceivingBouncesOutputs(): Record<
|
||||
string,
|
||||
TriggerOutput
|
||||
> {
|
||||
return buildEmailBisonEmailAccountAddedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonWarmupDisabledCausingBouncesOutputs(): Record<
|
||||
string,
|
||||
TriggerOutput
|
||||
> {
|
||||
return buildEmailBisonEmailAccountAddedOutputs()
|
||||
}
|
||||
|
||||
export function buildEmailBisonUntrackedReplyReceivedOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
...buildEmailBisonOutputs(),
|
||||
reply: {
|
||||
id: { type: 'number', description: 'Reply ID' },
|
||||
uuid: { type: 'string', description: 'Reply UUID' },
|
||||
email_subject: { type: 'string', description: 'Reply email subject' },
|
||||
interested: { type: 'boolean', description: 'Whether the reply is marked interested' },
|
||||
automated_reply: { type: 'boolean', description: 'Whether the reply is automated' },
|
||||
html_body: { type: 'string', description: 'Reply HTML body' },
|
||||
text_body: { type: 'string', description: 'Reply plain text body' },
|
||||
raw_body: { type: 'string', description: 'Raw MIME reply body' },
|
||||
headers: { type: 'string', description: 'Encoded raw email headers' },
|
||||
date_received: { type: 'string', description: 'Reply received timestamp' },
|
||||
from_name: { type: 'string', description: 'Reply sender name' },
|
||||
from_email_address: { type: 'string', description: 'Reply sender email address' },
|
||||
primary_to_email_address: { type: 'string', description: 'Primary recipient email address' },
|
||||
to: { type: 'json', description: 'Reply To recipients' },
|
||||
cc: { type: 'json', description: 'Reply CC recipients' },
|
||||
bcc: { type: 'json', description: 'Reply BCC recipients' },
|
||||
parent_id: { type: 'number', description: 'Parent reply ID' },
|
||||
reply_type: { type: 'string', description: 'Reply type' },
|
||||
folder: { type: 'string', description: 'Reply folder' },
|
||||
raw_message_id: { type: 'string', description: 'Raw email message ID' },
|
||||
created_at: { type: 'string', description: 'Reply creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Reply update timestamp' },
|
||||
attachments: { type: 'json', description: 'Reply attachments' },
|
||||
},
|
||||
senderEmail: {
|
||||
id: { type: 'number', description: 'Sender email ID' },
|
||||
name: { type: 'string', description: 'Sender email name' },
|
||||
email: { type: 'string', description: 'Sender email address' },
|
||||
status: { type: 'string', description: 'Sender email status' },
|
||||
account_type: { type: 'string', description: 'Sender email connection type' },
|
||||
daily_limit: { type: 'number', description: 'Sender email daily limit' },
|
||||
emails_sent: { type: 'number', description: 'Sender email sent count' },
|
||||
replied: { type: 'number', description: 'Sender email replied count' },
|
||||
opened: { type: 'number', description: 'Sender email opened count' },
|
||||
unsubscribed: { type: 'number', description: 'Sender email unsubscribed count' },
|
||||
bounced: { type: 'number', description: 'Sender email bounced count' },
|
||||
unique_replies: { type: 'number', description: 'Sender email unique reply count' },
|
||||
unique_opens: { type: 'number', description: 'Sender email unique open count' },
|
||||
total_leads_contacted: { type: 'number', description: 'Sender email total leads contacted' },
|
||||
interested: { type: 'number', description: 'Sender email interested count' },
|
||||
created_at: { type: 'string', description: 'Sender email creation timestamp' },
|
||||
updated_at: { type: 'string', description: 'Sender email update timestamp' },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getEmailBisonEventTypeForTrigger(triggerId: string): string | undefined {
|
||||
return EMAILBISON_TRIGGER_TO_EVENT_TYPE[
|
||||
triggerId as keyof typeof EMAILBISON_TRIGGER_TO_EVENT_TYPE
|
||||
]
|
||||
}
|
||||
|
||||
export function isEmailBisonEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
|
||||
const expectedEventType = getEmailBisonEventTypeForTrigger(triggerId)
|
||||
if (!expectedEventType) return false
|
||||
|
||||
const event = body.event
|
||||
if (!isRecord(event)) return false
|
||||
|
||||
const actualEventType = event.type
|
||||
return typeof actualEventType === 'string' && actualEventType.toLowerCase() === expectedEventType
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonWarmupDisabledCausingBouncesOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonWarmupDisabledCausingBouncesTrigger: TriggerConfig = {
|
||||
id: 'emailbison_warmup_disabled_causing_bounces',
|
||||
name: 'Email Bison Warmup Disabled Causing Bounces',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when warmup is disabled for a sender email causing too many bounces',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_warmup_disabled_causing_bounces',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Warmup Disabled Causing Bounces'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_warmup_disabled_causing_bounces'),
|
||||
}),
|
||||
outputs: buildEmailBisonWarmupDisabledCausingBouncesOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { EmailBisonIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildEmailBisonExtraFields,
|
||||
buildEmailBisonWarmupDisabledReceivingBouncesOutputs,
|
||||
emailBisonSetupInstructions,
|
||||
emailBisonTriggerOptions,
|
||||
} from '@/triggers/emailbison/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const emailBisonWarmupDisabledReceivingBouncesTrigger: TriggerConfig = {
|
||||
id: 'emailbison_warmup_disabled_receiving_bounces',
|
||||
name: 'Email Bison Warmup Disabled Receiving Bounces',
|
||||
provider: 'emailbison',
|
||||
description: 'Trigger when warmup is disabled for a sender email receiving too many bounces',
|
||||
version: '1.0.0',
|
||||
icon: EmailBisonIcon,
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'emailbison_warmup_disabled_receiving_bounces',
|
||||
triggerOptions: emailBisonTriggerOptions,
|
||||
setupInstructions: emailBisonSetupInstructions('Warmup Disabled Receiving Bounces'),
|
||||
extraFields: buildEmailBisonExtraFields('emailbison_warmup_disabled_receiving_bounces'),
|
||||
}),
|
||||
outputs: buildEmailBisonWarmupDisabledReceivingBouncesOutputs(),
|
||||
webhook: { method: 'POST', headers: { 'Content-Type': 'application/json' } },
|
||||
}
|
||||
@@ -78,6 +78,25 @@ import {
|
||||
confluenceUserCreatedTrigger,
|
||||
confluenceWebhookTrigger,
|
||||
} from '@/triggers/confluence'
|
||||
import {
|
||||
emailBisonEmailAccountAddedTrigger,
|
||||
emailBisonEmailAccountDisconnectedTrigger,
|
||||
emailBisonEmailAccountReconnectedTrigger,
|
||||
emailBisonEmailAccountRemovedTrigger,
|
||||
emailBisonEmailBouncedTrigger,
|
||||
emailBisonEmailOpenedTrigger,
|
||||
emailBisonEmailSentTrigger,
|
||||
emailBisonLeadFirstContactedTrigger,
|
||||
emailBisonLeadInterestedTrigger,
|
||||
emailBisonLeadRepliedTrigger,
|
||||
emailBisonLeadUnsubscribedTrigger,
|
||||
emailBisonManualEmailSentTrigger,
|
||||
emailBisonTagAttachedTrigger,
|
||||
emailBisonTagRemovedTrigger,
|
||||
emailBisonUntrackedReplyReceivedTrigger,
|
||||
emailBisonWarmupDisabledCausingBouncesTrigger,
|
||||
emailBisonWarmupDisabledReceivingBouncesTrigger,
|
||||
} from '@/triggers/emailbison'
|
||||
import { fathomNewMeetingTrigger, fathomWebhookTrigger } from '@/triggers/fathom'
|
||||
import { firefliesTranscriptionCompleteTrigger } from '@/triggers/fireflies'
|
||||
import { genericWebhookTrigger } from '@/triggers/generic'
|
||||
@@ -379,6 +398,23 @@ export const TRIGGER_REGISTRY: TriggerRegistry = {
|
||||
confluence_space_removed: confluenceSpaceRemovedTrigger,
|
||||
confluence_page_permissions_updated: confluencePagePermissionsUpdatedTrigger,
|
||||
confluence_user_created: confluenceUserCreatedTrigger,
|
||||
emailbison_email_sent: emailBisonEmailSentTrigger,
|
||||
emailbison_lead_first_contacted: emailBisonLeadFirstContactedTrigger,
|
||||
emailbison_lead_replied: emailBisonLeadRepliedTrigger,
|
||||
emailbison_lead_interested: emailBisonLeadInterestedTrigger,
|
||||
emailbison_lead_unsubscribed: emailBisonLeadUnsubscribedTrigger,
|
||||
emailbison_untracked_reply_received: emailBisonUntrackedReplyReceivedTrigger,
|
||||
emailbison_email_opened: emailBisonEmailOpenedTrigger,
|
||||
emailbison_email_bounced: emailBisonEmailBouncedTrigger,
|
||||
emailbison_email_account_added: emailBisonEmailAccountAddedTrigger,
|
||||
emailbison_email_account_removed: emailBisonEmailAccountRemovedTrigger,
|
||||
emailbison_email_account_disconnected: emailBisonEmailAccountDisconnectedTrigger,
|
||||
emailbison_email_account_reconnected: emailBisonEmailAccountReconnectedTrigger,
|
||||
emailbison_manual_email_sent: emailBisonManualEmailSentTrigger,
|
||||
emailbison_tag_attached: emailBisonTagAttachedTrigger,
|
||||
emailbison_tag_removed: emailBisonTagRemovedTrigger,
|
||||
emailbison_warmup_disabled_receiving_bounces: emailBisonWarmupDisabledReceivingBouncesTrigger,
|
||||
emailbison_warmup_disabled_causing_bounces: emailBisonWarmupDisabledCausingBouncesTrigger,
|
||||
generic_webhook: genericWebhookTrigger,
|
||||
greenhouse_candidate_hired: greenhouseCandidateHiredTrigger,
|
||||
greenhouse_new_application: greenhouseNewApplicationTrigger,
|
||||
|
||||
Reference in New Issue
Block a user