diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index a99241ff32..9c9f00984a 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -4243,3 +4243,104 @@ export function IncidentioIcon(props: SVGProps) { ) } + +export function IntercomIcon(props: SVGProps) { + return ( + + + + + + ) +} + +export function MailchimpIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + ) +} + +export function ZendeskIcon(props: SVGProps) { + return ( + + + + + + ) +} + +export function PylonIcon(props: SVGProps) { + return ( + + + + + + + + + + + ) +} diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index 7f40c6b51e..ad748928c5 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -33,10 +33,12 @@ import { HunterIOIcon, ImageIcon, IncidentioIcon, + IntercomIcon, JinaAIIcon, JiraIcon, LinearIcon, LinkupIcon, + MailchimpIcon, Mem0Icon, MicrosoftExcelIcon, MicrosoftOneDriveIcon, @@ -57,6 +59,7 @@ import { PipedriveIcon, PostgresIcon, PosthogIcon, + PylonIcon, QdrantIcon, RedditIcon, ResendIcon, @@ -83,6 +86,7 @@ import { WikipediaIcon, xIcon, YouTubeIcon, + ZendeskIcon, ZepIcon, } from '@/components/icons' @@ -90,6 +94,7 @@ type IconComponent = ComponentType> export const blockTypeToIconMap: Record = { zep: ZepIcon, + zendesk: ZendeskIcon, youtube: YouTubeIcon, x: xIcon, wikipedia: WikipediaIcon, @@ -121,6 +126,7 @@ export const blockTypeToIconMap: Record = { resend: ResendIcon, reddit: RedditIcon, qdrant: QdrantIcon, + pylon: PylonIcon, posthog: PosthogIcon, postgresql: PostgresIcon, pipedrive: PipedriveIcon, @@ -140,11 +146,13 @@ export const blockTypeToIconMap: Record = { microsoft_excel: MicrosoftExcelIcon, memory: BrainIcon, mem0: Mem0Icon, + mailchimp: MailchimpIcon, linkup: LinkupIcon, linear: LinearIcon, knowledge: PackageSearchIcon, jira: JiraIcon, jina: JinaAIIcon, + intercom: IntercomIcon, incidentio: IncidentioIcon, image_generator: ImageIcon, hunter: HunterIOIcon, diff --git a/apps/docs/content/docs/en/tools/intercom.mdx b/apps/docs/content/docs/en/tools/intercom.mdx new file mode 100644 index 0000000000..8e5799ae5b --- /dev/null +++ b/apps/docs/content/docs/en/tools/intercom.mdx @@ -0,0 +1,358 @@ +--- +title: Intercom +description: Manage contacts, companies, conversations, tickets, and messages in Intercom +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Intercom](https://www.intercom.com/) is a leading customer communications platform that enables you to manage and automate your interactions with contacts, companies, conversations, tickets, and messages—all in one place. The Intercom integration in Sim lets your agents programmatically manage customer relationships, support requests, and conversations directly from your automated workflows. + +With the Intercom tools, you can: + +- **Contacts Management:** Create, get, update, list, search, and delete contacts—automate your CRM processes and keep your customer records up-to-date. +- **Company Management:** Create new companies, retrieve company details, and list all companies related to your users or business clients. +- **Conversation Handling:** Get, list, reply to, and search through conversations—allowing agents to track ongoing support threads, provide answers, and automate follow-up actions. +- **Ticket Management:** Create and retrieve tickets programmatically, helping you automate customer service, support issue tracking, and workflow escalations. +- **Send Messages:** Trigger messages to users or leads for onboarding, support, or marketing, all from within your workflow automation. + +By integrating Intercom tools into Sim, you empower your workflows to communicate directly with your users, automate customer support processes, manage leads, and streamline communications at scale. Whether you need to create new contacts, keep customer data synchronized, manage support tickets, or send personalized engagement messages, the Intercom tools provide a comprehensive way to manage customer interactions as part of your intelligent automations. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate Intercom into the workflow. Can create, get, update, list, search, and delete contacts; create, get, and list companies; get, list, reply, and search conversations; create and get tickets; and create messages. + + + +## Tools + +### `intercom_create_contact` + +Create a new contact in Intercom with email, external_id, or role + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | No | The contact's email address | +| `external_id` | string | No | A unique identifier for the contact provided by the client | +| `phone` | string | No | The contact's phone number | +| `name` | string | No | The contact's name | +| `avatar` | string | No | An avatar image URL for the contact | +| `signed_up_at` | number | No | The time the user signed up as a Unix timestamp | +| `last_seen_at` | number | No | The time the user was last seen as a Unix timestamp | +| `owner_id` | string | No | The id of an admin that has been assigned account ownership of the contact | +| `unsubscribed_from_emails` | boolean | No | Whether the contact is unsubscribed from emails | +| `custom_attributes` | string | No | Custom attributes as JSON object \(e.g., \{"attribute_name": "value"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created contact data | + +### `intercom_get_contact` + +Get a single contact by ID from Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | Contact ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Contact data | + +### `intercom_update_contact` + +Update an existing contact in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | Contact ID to update | +| `email` | string | No | The contact's email address | +| `phone` | string | No | The contact's phone number | +| `name` | string | No | The contact's name | +| `avatar` | string | No | An avatar image URL for the contact | +| `signed_up_at` | number | No | The time the user signed up as a Unix timestamp | +| `last_seen_at` | number | No | The time the user was last seen as a Unix timestamp | +| `owner_id` | string | No | The id of an admin that has been assigned account ownership of the contact | +| `unsubscribed_from_emails` | boolean | No | Whether the contact is unsubscribed from emails | +| `custom_attributes` | string | No | Custom attributes as JSON object \(e.g., \{"attribute_name": "value"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated contact data | + +### `intercom_list_contacts` + +List all contacts from Intercom with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `per_page` | number | No | Number of results per page \(max: 150\) | +| `starting_after` | string | No | Cursor for pagination - ID to start after | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of contacts | + +### `intercom_search_contacts` + +Search for contacts in Intercom using a query + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `query` | string | Yes | Search query \(e.g., \{"field":"email","operator":"=","value":"user@example.com"\}\) | +| `per_page` | number | No | Number of results per page \(max: 150\) | +| `starting_after` | string | No | Cursor for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `intercom_delete_contact` + +Delete a contact from Intercom by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | Contact ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion result | + +### `intercom_create_company` + +Create or update a company in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `company_id` | string | Yes | Your unique identifier for the company | +| `name` | string | No | The name of the company | +| `website` | string | No | The company website | +| `plan` | string | No | The company plan name | +| `size` | number | No | The number of employees in the company | +| `industry` | string | No | The industry the company operates in | +| `monthly_spend` | number | No | How much revenue the company generates for your business | +| `custom_attributes` | string | No | Custom attributes as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created or updated company data | + +### `intercom_get_company` + +Retrieve a single company by ID from Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `companyId` | string | Yes | Company ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Company data | + +### `intercom_list_companies` + +List all companies from Intercom with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `per_page` | number | No | Number of results per page | +| `page` | number | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of companies | + +### `intercom_get_conversation` + +Retrieve a single conversation by ID from Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | Conversation ID to retrieve | +| `display_as` | string | No | Set to "plaintext" to retrieve messages in plain text | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Conversation data | + +### `intercom_list_conversations` + +List all conversations from Intercom with pagination support + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `per_page` | number | No | Number of results per page \(max: 150\) | +| `starting_after` | string | No | Cursor for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of conversations | + +### `intercom_reply_conversation` + +Reply to a conversation as an admin in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | Conversation ID to reply to | +| `message_type` | string | Yes | Message type: "comment" or "note" | +| `body` | string | Yes | The text body of the reply | +| `admin_id` | string | Yes | The ID of the admin authoring the reply | +| `attachment_urls` | string | No | Comma-separated list of image URLs \(max 10\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated conversation with reply | + +### `intercom_search_conversations` + +Search for conversations in Intercom using a query + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `query` | string | Yes | Search query as JSON object | +| `per_page` | number | No | Number of results per page \(max: 150\) | +| `starting_after` | string | No | Cursor for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `intercom_create_ticket` + +Create a new ticket in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `ticket_type_id` | string | Yes | The ID of the ticket type | +| `contacts` | string | Yes | JSON array of contact identifiers \(e.g., \[\{"id": "contact_id"\}\]\) | +| `ticket_attributes` | string | Yes | JSON object with ticket attributes including _default_title_ and _default_description_ | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created ticket data | + +### `intercom_get_ticket` + +Retrieve a single ticket by ID from Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `ticketId` | string | Yes | Ticket ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Ticket data | + +### `intercom_create_message` + +Create and send a new admin-initiated message in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `message_type` | string | Yes | Message type: "inapp" or "email" | +| `subject` | string | No | The subject of the message \(for email type\) | +| `body` | string | Yes | The body of the message | +| `from_type` | string | Yes | Sender type: "admin" | +| `from_id` | string | Yes | The ID of the admin sending the message | +| `to_type` | string | Yes | Recipient type: "contact" | +| `to_id` | string | Yes | The ID of the contact receiving the message | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created message data | + + + +## Notes + +- Category: `tools` +- Type: `intercom` diff --git a/apps/docs/content/docs/en/tools/mailchimp.mdx b/apps/docs/content/docs/en/tools/mailchimp.mdx new file mode 100644 index 0000000000..4a5efcb821 --- /dev/null +++ b/apps/docs/content/docs/en/tools/mailchimp.mdx @@ -0,0 +1,1480 @@ +--- +title: Mailchimp +description: Manage audiences, campaigns, and marketing automation in Mailchimp +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Mailchimp](https://mailchimp.com/) is a powerful marketing automation platform that enables you to manage audiences, campaigns, and a wide range of marketing activities all in one place. Mailchimp’s robust API and integrations let you automate outreach, email marketing, reporting, and audience management directly from your workflows in Sim. + +With the Mailchimp tools in Sim, you can: + +- **Manage Audiences (Lists):** + - List and retrieve all your Mailchimp audiences (lists) for easy management. + - Get comprehensive information about a specific audience. + - Create new audiences and keep your segmentation up-to-date. + +- **List Members:** + - Access and manage list members (subscribers), retrieve member details, and keep your email lists synchronized. + +- **Campaign Management:** + - Automate campaign creation, send campaigns, and analyze campaign performance with comprehensive reporting. + +- **Marketing Automation:** + - Manage automated workflows, set up triggers, and schedule emails to streamline your nurture processes. + +- **Templates, Segments, and Tags:** + - Retrieve and manage your email templates for consistent branding. + - Access and update audience segments to target specific groups. + - Create and manage tags to further organize your contacts. + +- **Advanced List Controls:** + - Manage merge fields and interest categories (groups) to collect rich, structured data from your subscribers. + - Handle landing pages, signup forms, and other lead-capture tools to maximize conversions. + +- **Batch Operations and Reporting:** + - Run batch jobs for bulk operations and streamline large updates. + - Retrieve detailed reports on campaigns, automations, and audience growth to inform your marketing strategy. + +By using Mailchimp within Sim, your agents and workflows can automate email marketing at scale—growing your audience, personalizing outreach, optimizing engagement, and making data-driven decisions. Whether you’re syncing CRM records, triggering campaigns in response to in-product actions, or managing subscriber data, Mailchimp’s tools in Sim deliver complete programmatic control over your marketing automation. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate Mailchimp into the workflow. Can manage audiences (lists), list members, campaigns, automation workflows, templates, reports, segments, tags, merge fields, interest categories, landing pages, signup forms, and batch operations. + + + +## Tools + +### `mailchimp_get_audiences` + +Retrieve a list of audiences (lists) from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Audiences data and metadata | + +### `mailchimp_get_audience` + +Retrieve details of a specific audience (list) from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Audience data and metadata | + +### `mailchimp_create_audience` + +Create a new audience (list) in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `audienceName` | string | Yes | The name of the list | +| `contact` | string | Yes | JSON object of contact information | +| `permissionReminder` | string | Yes | Permission reminder text | +| `campaignDefaults` | string | Yes | JSON object of default campaign settings | +| `emailTypeOption` | string | Yes | Support multiple email formats | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created audience data | + +### `mailchimp_update_audience` + +Update an existing audience (list) in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `audienceName` | string | No | The name of the list | +| `permissionReminder` | string | No | Permission reminder text | +| `campaignDefaults` | string | No | JSON object of default campaign settings | +| `emailTypeOption` | string | No | Support multiple email formats | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated audience data | + +### `mailchimp_delete_audience` + +Delete an audience (list) from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_members` + +Retrieve a list of members from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `status` | string | No | Filter by status \(subscribed, unsubscribed, cleaned, pending\) | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Members data and metadata | + +### `mailchimp_get_member` + +Retrieve details of a specific member from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Member data and metadata | + +### `mailchimp_add_member` + +Add a new member to a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `emailAddress` | string | Yes | Member email address | +| `status` | string | Yes | Subscriber status | +| `mergeFields` | string | No | JSON object of merge fields | +| `interests` | string | No | JSON object of interests | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Added member data | + +### `mailchimp_add_or_update_member` + +Add a new member or update an existing member in a Mailchimp audience (upsert) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | +| `emailAddress` | string | Yes | Member email address | +| `statusIfNew` | string | Yes | Subscriber status if new member | +| `mergeFields` | string | No | JSON object of merge fields | +| `interests` | string | No | JSON object of interests | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Member data | + +### `mailchimp_update_member` + +Update an existing member in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | +| `emailAddress` | string | No | Member email address | +| `status` | string | No | Subscriber status | +| `mergeFields` | string | No | JSON object of merge fields | +| `interests` | string | No | JSON object of interests | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated member data | + +### `mailchimp_delete_member` + +Delete a member from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_archive_member` + +Permanently archive (delete) a member from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Archive confirmation | + +### `mailchimp_unarchive_member` + +Restore an archived member to a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | +| `emailAddress` | string | Yes | Member email address | +| `status` | string | Yes | Subscriber status | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Unarchived member data | + +### `mailchimp_get_campaigns` + +Retrieve a list of campaigns from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignType` | string | No | Filter by campaign type \(regular, plaintext, absplit, rss, variate\) | +| `status` | string | No | Filter by status \(save, paused, schedule, sending, sent\) | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaigns data and metadata | + +### `mailchimp_get_campaign` + +Retrieve details of a specific campaign from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaign data and metadata | + +### `mailchimp_create_campaign` + +Create a new campaign in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignType` | string | Yes | Campaign type | +| `campaignSettings` | string | Yes | JSON object of campaign settings | +| `recipients` | string | No | JSON object of recipients | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created campaign data | + +### `mailchimp_update_campaign` + +Update an existing campaign in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign | +| `campaignSettings` | string | No | JSON object of campaign settings | +| `recipients` | string | No | JSON object of recipients | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated campaign data | + +### `mailchimp_delete_campaign` + +Delete a campaign from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_send_campaign` + +Send a Mailchimp campaign + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign to send | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Send confirmation | + +### `mailchimp_schedule_campaign` + +Schedule a Mailchimp campaign to be sent at a specific time + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign to schedule | +| `scheduleTime` | string | Yes | ISO 8601 format date and time | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Schedule confirmation | + +### `mailchimp_unschedule_campaign` + +Unschedule a previously scheduled Mailchimp campaign + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign to unschedule | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Unschedule confirmation | + +### `mailchimp_replicate_campaign` + +Create a copy of an existing Mailchimp campaign + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign to replicate | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Replicated campaign data | + +### `mailchimp_get_campaign_content` + +Retrieve the HTML and plain-text content for a Mailchimp campaign + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaign content data | + +### `mailchimp_set_campaign_content` + +Set the content for a Mailchimp campaign + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign | +| `html` | string | No | The HTML content for the campaign | +| `plainText` | string | No | The plain-text content for the campaign | +| `templateId` | string | No | The ID of the template to use | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaign content data | + +### `mailchimp_get_automations` + +Retrieve a list of automations from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Automations data and metadata | + +### `mailchimp_get_automation` + +Retrieve details of a specific automation from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `workflowId` | string | Yes | The unique ID for the automation workflow | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Automation data and metadata | + +### `mailchimp_start_automation` + +Start all emails in a Mailchimp automation workflow + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `workflowId` | string | Yes | The unique ID for the automation workflow | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Start confirmation | + +### `mailchimp_pause_automation` + +Pause all emails in a Mailchimp automation workflow + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `workflowId` | string | Yes | The unique ID for the automation workflow | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Pause confirmation | + +### `mailchimp_add_subscriber_to_automation` + +Manually add a subscriber to a workflow email queue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `workflowId` | string | Yes | The unique ID for the automation workflow | +| `workflowEmailId` | string | Yes | The unique ID for the workflow email | +| `emailAddress` | string | Yes | Email address of the subscriber | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Subscriber queue data | + +### `mailchimp_get_templates` + +Retrieve a list of templates from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Templates data and metadata | + +### `mailchimp_get_template` + +Retrieve details of a specific template from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `templateId` | string | Yes | The unique ID for the template | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Template data and metadata | + +### `mailchimp_create_template` + +Create a new template in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `templateName` | string | Yes | The name of the template | +| `templateHtml` | string | Yes | The HTML content for the template | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created template data | + +### `mailchimp_update_template` + +Update an existing template in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `templateId` | string | Yes | The unique ID for the template | +| `templateName` | string | No | The name of the template | +| `templateHtml` | string | No | The HTML content for the template | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated template data | + +### `mailchimp_delete_template` + +Delete a template from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `templateId` | string | Yes | The unique ID for the template to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_campaign_reports` + +Retrieve a list of campaign reports from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaign reports data and metadata | + +### `mailchimp_get_campaign_report` + +Retrieve the report for a specific campaign from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `campaignId` | string | Yes | The unique ID for the campaign | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Campaign report data and metadata | + +### `mailchimp_get_segments` + +Retrieve a list of segments from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Segments data and metadata | + +### `mailchimp_get_segment` + +Retrieve details of a specific segment from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Segment data and metadata | + +### `mailchimp_create_segment` + +Create a new segment in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentName` | string | Yes | The name of the segment | +| `segmentOptions` | string | No | JSON object of segment options | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created segment data | + +### `mailchimp_update_segment` + +Update an existing segment in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment | +| `segmentName` | string | No | The name of the segment | +| `segmentOptions` | string | No | JSON object of segment options | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated segment data | + +### `mailchimp_delete_segment` + +Delete a segment from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_segment_members` + +Retrieve members of a specific segment from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Segment members data and metadata | + +### `mailchimp_add_segment_member` + +Add a member to a specific segment in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment | +| `emailAddress` | string | Yes | Email address of the member | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Added member data | + +### `mailchimp_remove_segment_member` + +Remove a member from a specific segment in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `segmentId` | string | Yes | The unique ID for the segment | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Removal confirmation | + +### `mailchimp_get_member_tags` + +Retrieve tags associated with a member in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Member tags data and metadata | + +### `mailchimp_add_member_tags` + +Add tags to a member in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | +| `tags` | string | Yes | JSON array of tags | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Tag addition confirmation | + +### `mailchimp_remove_member_tags` + +Remove tags from a member in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `subscriberEmail` | string | Yes | Member email address or MD5 hash | +| `tags` | string | Yes | JSON array of tags with inactive status | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Tag removal confirmation | + +### `mailchimp_get_merge_fields` + +Retrieve a list of merge fields from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Merge fields data and metadata | + +### `mailchimp_get_merge_field` + +Retrieve details of a specific merge field from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `mergeId` | string | Yes | The unique ID for the merge field | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Merge field data and metadata | + +### `mailchimp_create_merge_field` + +Create a new merge field in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `mergeName` | string | Yes | The name of the merge field | +| `mergeType` | string | Yes | The type of the merge field \(text, number, address, phone, date, url, imageurl, radio, dropdown, birthday, zip\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created merge field data | + +### `mailchimp_update_merge_field` + +Update an existing merge field in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `mergeId` | string | Yes | The unique ID for the merge field | +| `mergeName` | string | No | The name of the merge field | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated merge field data | + +### `mailchimp_delete_merge_field` + +Delete a merge field from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `mergeId` | string | Yes | The unique ID for the merge field to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_interest_categories` + +Retrieve a list of interest categories from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Interest categories data and metadata | + +### `mailchimp_get_interest_category` + +Retrieve details of a specific interest category from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Interest category data and metadata | + +### `mailchimp_create_interest_category` + +Create a new interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryTitle` | string | Yes | The title of the interest category | +| `interestCategoryType` | string | Yes | The type of interest category \(checkboxes, dropdown, radio, hidden\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created interest category data | + +### `mailchimp_update_interest_category` + +Update an existing interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `interestCategoryTitle` | string | No | The title of the interest category | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated interest category data | + +### `mailchimp_delete_interest_category` + +Delete an interest category from a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_interests` + +Retrieve a list of interests from an interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Interests data and metadata | + +### `mailchimp_get_interest` + +Retrieve details of a specific interest from an interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `interestId` | string | Yes | The unique ID for the interest | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Interest data and metadata | + +### `mailchimp_create_interest` + +Create a new interest in an interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `interestName` | string | Yes | The name of the interest | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created interest data | + +### `mailchimp_update_interest` + +Update an existing interest in an interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `interestId` | string | Yes | The unique ID for the interest | +| `interestName` | string | No | The name of the interest | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated interest data | + +### `mailchimp_delete_interest` + +Delete an interest from an interest category in a Mailchimp audience + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `listId` | string | Yes | The unique ID for the list | +| `interestCategoryId` | string | Yes | The unique ID for the interest category | +| `interestId` | string | Yes | The unique ID for the interest to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_get_landing_pages` + +Retrieve a list of landing pages from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Landing pages data and metadata | + +### `mailchimp_get_landing_page` + +Retrieve details of a specific landing page from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `pageId` | string | Yes | The unique ID for the landing page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Landing page data and metadata | + +### `mailchimp_create_landing_page` + +Create a new landing page in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `landingPageType` | string | Yes | The type of landing page \(signup\) | +| `landingPageTitle` | string | No | The title of the landing page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created landing page data | + +### `mailchimp_update_landing_page` + +Update an existing landing page in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `pageId` | string | Yes | The unique ID for the landing page | +| `landingPageTitle` | string | No | The title of the landing page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated landing page data | + +### `mailchimp_delete_landing_page` + +Delete a landing page from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `pageId` | string | Yes | The unique ID for the landing page to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `mailchimp_publish_landing_page` + +Publish a landing page in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `pageId` | string | Yes | The unique ID for the landing page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Publish confirmation | + +### `mailchimp_unpublish_landing_page` + +Unpublish a landing page in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `pageId` | string | Yes | The unique ID for the landing page | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Unpublish confirmation | + +### `mailchimp_get_batch_operations` + +Retrieve a list of batch operations from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `count` | string | No | Number of results \(default: 10, max: 1000\) | +| `offset` | string | No | Number of results to skip | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Batch operations data and metadata | + +### `mailchimp_get_batch_operation` + +Retrieve details of a specific batch operation from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `batchId` | string | Yes | The unique ID for the batch operation | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Batch operation data and metadata | + +### `mailchimp_create_batch_operation` + +Create a new batch operation in Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `operations` | string | Yes | JSON array of operations | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created batch operation data | + +### `mailchimp_delete_batch_operation` + +Delete a batch operation from Mailchimp + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Mailchimp API key with server prefix | +| `batchId` | string | Yes | The unique ID for the batch operation to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + + + +## Notes + +- Category: `tools` +- Type: `mailchimp` diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 49259abe7c..6f83cb4500 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -28,11 +28,13 @@ "hunter", "image_generator", "incidentio", + "intercom", "jina", "jira", "knowledge", "linear", "linkup", + "mailchimp", "mem0", "memory", "microsoft_excel", @@ -52,6 +54,7 @@ "pipedrive", "postgresql", "posthog", + "pylon", "qdrant", "reddit", "resend", @@ -83,6 +86,7 @@ "wikipedia", "x", "youtube", + "zendesk", "zep" ] } diff --git a/apps/docs/content/docs/en/tools/pylon.mdx b/apps/docs/content/docs/en/tools/pylon.mdx new file mode 100644 index 0000000000..ff81223f5d --- /dev/null +++ b/apps/docs/content/docs/en/tools/pylon.mdx @@ -0,0 +1,801 @@ +--- +title: Pylon +description: Manage customer support issues, accounts, contacts, users, teams, and tags in Pylon +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Pylon](https://usepylon.com/) is an advanced customer support and success platform designed to help you manage every aspect of your customer relationships—from support issues to accounts, contacts, users, teams, and beyond. Pylon empowers support and success teams to operate efficiently and programmatically with a rich API and comprehensive toolset. + +With Pylon in Sim, you can: + +- **Manage Support Issues:** + - List, create, get, update, and delete support issues for efficient case tracking. + - Search and snooze issues to help agents stay focused and organized. + - Handle issue followers and external issues for seamless collaboration with internal and external stakeholders. + +- **Full Account Management:** + - List, create, get, update, and delete customer accounts. + - Bulk update accounts programmatically. + - Search accounts to quickly find customer information relevant for support or outreach. + +- **Contact Management:** + - List, create, get, update, delete, and search contacts—manage everyone connected to your accounts. + +- **User and Team Operations:** + - List, get, update, and search users in your Pylon workspace. + - List, create, get, and update teams to structure your support organization and workflows. + +- **Tagging and Organization:** + - List, get, create, update, and delete tags for categorizing issues, accounts, or contacts. + +- **Message Handling:** + - Redact sensitive message content directly from your workflows for privacy and compliance. + +By integrating Pylon tools into Sim, your agents can automate every aspect of support operations: +- Automatically open, update, or triage new issues when customer events occur. +- Maintain synchronized account and contact data across your tech stack. +- Route conversations, handle escalations, and organize your support data using tags and teams. +- Ensure sensitive data is properly managed by redacting messages as needed. + +Pylon's endpoints provide granular control for full-lifecycle management of customer issues and relationships. Whether scaling a support desk, powering proactive customer success, or integrating with other systems, Pylon in Sim enables best-in-class CRM automation—securely, flexibly, and at scale. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +Integrate Pylon into the workflow. Manage issues (list, create, get, update, delete, search, snooze, followers, external issues), accounts (list, create, get, update, delete, bulk update, search), contacts (list, create, get, update, delete, search), users (list, get, update, search), teams (list, get, create, update), tags (list, get, create, update, delete), and messages (redact). + + + +## Tools + +### `pylon_list_issues` + +Retrieve a list of issues within a specified time range + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `startTime` | string | Yes | Start time in RFC3339 format \(e.g., 2024-01-01T00:00:00Z\) | +| `endTime` | string | Yes | End time in RFC3339 format \(e.g., 2024-01-31T23:59:59Z\) | +| `cursor` | string | No | Pagination cursor for next page of results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of issues | + +### `pylon_create_issue` + +Create a new issue with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `title` | string | Yes | Issue title | +| `bodyHtml` | string | Yes | Issue body in HTML format | +| `accountId` | string | No | Account ID to associate with issue | +| `assigneeId` | string | No | User ID to assign issue to | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created issue data | + +### `pylon_get_issue` + +Fetch a specific issue by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Issue data | + +### `pylon_update_issue` + +Update an existing issue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue to update | +| `state` | string | No | Issue state | +| `assigneeId` | string | No | User ID to assign issue to | +| `teamId` | string | No | Team ID to assign issue to | +| `tags` | string | No | Comma-separated tag IDs | +| `customFields` | string | No | Custom fields as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated issue data | + +### `pylon_delete_issue` + +Remove an issue by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion result | + +### `pylon_search_issues` + +Query issues using filters + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `filter` | string | Yes | Filter criteria as JSON string | +| `cursor` | string | No | Pagination cursor for next page of results | +| `limit` | number | No | Maximum number of results to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `pylon_snooze_issue` + +Postpone issue visibility until specified time + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue to snooze | +| `snoozeUntil` | string | Yes | RFC3339 timestamp when issue should reappear \(e.g., 2024-01-01T00:00:00Z\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Snoozed issue data | + +### `pylon_list_issue_followers` + +Get list of followers for a specific issue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Followers list | + +### `pylon_manage_issue_followers` + +Add or remove followers from an issue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the issue | +| `userIds` | string | No | Comma-separated user IDs to add/remove | +| `contactIds` | string | No | Comma-separated contact IDs to add/remove | +| `operation` | string | No | Operation to perform: "add" or "remove" \(default: "add"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated followers list | + +### `pylon_link_external_issue` + +Link an issue to an external system issue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | The ID of the Pylon issue | +| `externalIssueId` | string | Yes | The ID of the external issue | +| `source` | string | Yes | The source system \(e.g., "jira", "linear", "github"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Linked external issue data | + +### `pylon_list_accounts` + +Retrieve a paginated list of accounts + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `limit` | string | No | Number of accounts to return \(1-1000, default 100\) | +| `cursor` | string | No | Pagination cursor for next page of results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of accounts | + +### `pylon_create_account` + +Create a new account with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `name` | string | Yes | Account name | +| `domains` | string | No | Comma-separated list of domains | +| `primaryDomain` | string | No | Primary domain for the account | +| `customFields` | string | No | Custom fields as JSON object | +| `tags` | string | No | Comma-separated tag IDs | +| `channels` | string | No | Comma-separated channel IDs | +| `externalIds` | string | No | Comma-separated external IDs | +| `ownerId` | string | No | Owner user ID | +| `logoUrl` | string | No | URL to account logo | +| `subaccountIds` | string | No | Comma-separated subaccount IDs | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created account data | + +### `pylon_get_account` + +Retrieve a single account by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `accountId` | string | Yes | Account ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Account data | + +### `pylon_update_account` + +Update an existing account with new properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `accountId` | string | Yes | Account ID to update | +| `name` | string | No | Account name | +| `domains` | string | No | Comma-separated list of domains | +| `primaryDomain` | string | No | Primary domain for the account | +| `customFields` | string | No | Custom fields as JSON object | +| `tags` | string | No | Comma-separated tag IDs | +| `channels` | string | No | Comma-separated channel IDs | +| `externalIds` | string | No | Comma-separated external IDs | +| `ownerId` | string | No | Owner user ID | +| `logoUrl` | string | No | URL to account logo | +| `subaccountIds` | string | No | Comma-separated subaccount IDs | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated account data | + +### `pylon_delete_account` + +Remove an account by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `accountId` | string | Yes | Account ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deletion confirmation | + +### `pylon_bulk_update_accounts` + +Update multiple accounts at once + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `accountIds` | string | Yes | Comma-separated account IDs to update | +| `customFields` | string | No | Custom fields as JSON object | +| `tags` | string | No | Comma-separated tag IDs | +| `ownerId` | string | No | Owner user ID | +| `tagsApplyMode` | string | No | Tag application mode: append_only, remove_only, or replace | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk updated accounts data | + +### `pylon_search_accounts` + +Search accounts with custom filters + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `filter` | string | Yes | Filter as JSON string with field/operator/value structure | +| `limit` | string | No | Number of accounts to return \(1-1000, default 100\) | +| `cursor` | string | No | Pagination cursor for next page of results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `pylon_list_contacts` + +Retrieve a list of contacts + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `cursor` | string | No | Pagination cursor for next page of results | +| `limit` | string | No | Maximum number of contacts to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of contacts | + +### `pylon_create_contact` + +Create a new contact with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `name` | string | Yes | Contact name | +| `email` | string | No | Contact email address | +| `accountId` | string | No | Account ID to associate with contact | +| `accountExternalId` | string | No | External account ID to associate with contact | +| `avatarUrl` | string | No | URL for contact avatar image | +| `customFields` | string | No | Custom fields as JSON object | +| `portalRole` | string | No | Portal role for the contact | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created contact data | + +### `pylon_get_contact` + +Retrieve a specific contact by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `contactId` | string | Yes | Contact ID to retrieve | +| `cursor` | string | No | Pagination cursor for next page of results | +| `limit` | string | No | Maximum number of items to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Contact data | + +### `pylon_update_contact` + +Update an existing contact with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `contactId` | string | Yes | Contact ID to update | +| `name` | string | No | Contact name | +| `email` | string | No | Contact email address | +| `accountId` | string | No | Account ID to associate with contact | +| `accountExternalId` | string | No | External account ID to associate with contact | +| `avatarUrl` | string | No | URL for contact avatar image | +| `customFields` | string | No | Custom fields as JSON object | +| `portalRole` | string | No | Portal role for the contact | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated contact data | + +### `pylon_delete_contact` + +Delete a specific contact by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `contactId` | string | Yes | Contact ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Delete operation result | + +### `pylon_search_contacts` + +Search for contacts using a filter + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `filter` | string | Yes | Filter as JSON object | +| `limit` | string | No | Maximum number of contacts to return | +| `cursor` | string | No | Pagination cursor for next page of results | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `pylon_list_users` + +Retrieve a list of users + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of users | + +### `pylon_get_user` + +Retrieve a specific user by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `userId` | string | Yes | User ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | User data | + +### `pylon_update_user` + +Update an existing user with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `userId` | string | Yes | User ID to update | +| `roleId` | string | No | Role ID to assign to user | +| `status` | string | No | User status | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated user data | + +### `pylon_search_users` + +Search for users using a filter with email field + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `filter` | string | Yes | Filter as JSON object with email field | +| `cursor` | string | No | Pagination cursor for next page of results | +| `limit` | string | No | Maximum number of users to return | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `pylon_list_teams` + +Retrieve a list of teams + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of teams | + +### `pylon_get_team` + +Retrieve a specific team by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `teamId` | string | Yes | Team ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Team data | + +### `pylon_create_team` + +Create a new team with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `name` | string | No | Team name | +| `userIds` | string | No | Comma-separated user IDs to add as team members | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created team data | + +### `pylon_update_team` + +Update an existing team with specified properties (userIds replaces entire membership) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `teamId` | string | Yes | Team ID to update | +| `name` | string | No | Team name | +| `userIds` | string | No | Comma-separated user IDs \(replaces entire team membership\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated team data | + +### `pylon_list_tags` + +Retrieve a list of tags + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | List of tags | + +### `pylon_get_tag` + +Retrieve a specific tag by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `tagId` | string | Yes | Tag ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Tag data | + +### `pylon_create_tag` + +Create a new tag with specified properties (objectType: account/issue/contact) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `objectType` | string | Yes | Object type for tag \(account, issue, or contact\) | +| `value` | string | Yes | Tag value/name | +| `hexColor` | string | No | Hex color code for tag \(e.g., #FF5733\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created tag data | + +### `pylon_update_tag` + +Update an existing tag with specified properties + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `tagId` | string | Yes | Tag ID to update | +| `hexColor` | string | No | Hex color code for tag \(e.g., #FF5733\) | +| `value` | string | No | Tag value/name | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated tag data | + +### `pylon_delete_tag` + +Delete a specific tag by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `tagId` | string | Yes | Tag ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Delete operation result | + +### `pylon_redact_message` + +Redact a specific message within an issue + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiToken` | string | Yes | Pylon API token | +| `issueId` | string | Yes | Issue ID containing the message | +| `messageId` | string | Yes | Message ID to redact | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Redact operation result | + + + +## Notes + +- Category: `tools` +- Type: `pylon` diff --git a/apps/docs/content/docs/en/tools/zendesk.mdx b/apps/docs/content/docs/en/tools/zendesk.mdx new file mode 100644 index 0000000000..145936ad9a --- /dev/null +++ b/apps/docs/content/docs/en/tools/zendesk.mdx @@ -0,0 +1,646 @@ +--- +title: Zendesk +description: Manage support tickets, users, and organizations in Zendesk +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Zendesk](https://www.zendesk.com/) is a leading customer service and support platform that empowers organizations to efficiently manage support tickets, users, and organizations through a robust set of tools and APIs. The Zendesk integration in Sim lets your agents automate key support operations and synchronize your support data with the rest of your workflow. + +With Zendesk in Sim, you can: + +- **Manage Tickets:** + - Retrieve lists of support tickets with advanced filtering and sorting. + - Get detailed information on a single ticket for tracking and resolution. + - Create new tickets individually or in bulk to log customer issues programmatically. + - Update tickets or apply bulk updates to streamline complex workflows. + - Delete or merge tickets as cases are resolved or duplicates arise. + +- **User Management:** + - Retrieve lists of users or search users by criteria to keep your customer and agent directories up-to-date. + - Get detailed information on individual users or the current logged-in user. + - Create new users or onboard them in bulk, automating customer and agent provisioning. + - Update or bulk update user details to ensure information accuracy. + - Delete users as needed for privacy or account management. + +- **Organization Management:** + - List, search, and autocomplete organizations for streamlined support and account management. + - Get organization details and keep your database organized. + - Create, update, or delete organizations to reflect changes in your customer base. + - Perform bulk organization creation for large onboarding efforts. + +- **Advanced Search & Analytics:** + - Use versatile search endpoints to quickly locate tickets, users, or organizations by any field. + - Retrieve counts of search results to power reporting and analytics. + +By leveraging Zendesk’s Sim integration, your automated workflows can seamlessly handle support ticket triage, user onboarding/offboarding, company management, and keep your support operations running smoothly. Whether you’re integrating support with product, CRM, or automation systems, Zendesk tools in Sim provide robust, programmatic control to power best-in-class support at scale. +{/* MANUAL-CONTENT-END */} + +## Usage Instructions + +Integrate Zendesk into the workflow. Can get tickets, get ticket, create ticket, create tickets bulk, update ticket, update tickets bulk, delete ticket, merge tickets, get users, get user, get current user, search users, create user, create users bulk, update user, update users bulk, delete user, get organizations, get organization, autocomplete organizations, create organization, create organizations bulk, update organization, delete organization, search, search count. + + + +## Tools + +### `zendesk_get_tickets` + +Retrieve a list of tickets from Zendesk with optional filtering + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain \(e.g., "mycompany" for mycompany.zendesk.com\) | +| `status` | string | No | Filter by status \(new, open, pending, hold, solved, closed\) | +| `priority` | string | No | Filter by priority \(low, normal, high, urgent\) | +| `type` | string | No | Filter by type \(problem, incident, question, task\) | +| `assigneeId` | string | No | Filter by assignee user ID | +| `organizationId` | string | No | Filter by organization ID | +| `sortBy` | string | No | Sort field \(created_at, updated_at, priority, status\) | +| `sortOrder` | string | No | Sort order \(asc or desc\) | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Tickets data and metadata | + +### `zendesk_get_ticket` + +Get a single ticket by ID from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `ticketId` | string | Yes | Ticket ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Ticket data | + +### `zendesk_create_ticket` + +Create a new ticket in Zendesk with support for custom fields + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `subject` | string | No | Ticket subject \(optional - will be auto-generated if not provided\) | +| `description` | string | Yes | Ticket description \(first comment\) | +| `priority` | string | No | Priority \(low, normal, high, urgent\) | +| `status` | string | No | Status \(new, open, pending, hold, solved, closed\) | +| `type` | string | No | Type \(problem, incident, question, task\) | +| `tags` | string | No | Comma-separated tags | +| `assigneeId` | string | No | Assignee user ID | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created ticket data | + +### `zendesk_create_tickets_bulk` + +Create multiple tickets in Zendesk at once (max 100) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `tickets` | string | Yes | JSON array of ticket objects to create \(max 100\). Each ticket should have subject and comment properties. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk create job status | + +### `zendesk_update_ticket` + +Update an existing ticket in Zendesk with support for custom fields + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `ticketId` | string | Yes | Ticket ID to update | +| `subject` | string | No | New ticket subject | +| `comment` | string | No | Add a comment to the ticket | +| `priority` | string | No | Priority \(low, normal, high, urgent\) | +| `status` | string | No | Status \(new, open, pending, hold, solved, closed\) | +| `type` | string | No | Type \(problem, incident, question, task\) | +| `tags` | string | No | Comma-separated tags | +| `assigneeId` | string | No | Assignee user ID | +| `groupId` | string | No | Group ID | +| `customFields` | string | No | Custom fields as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated ticket data | + +### `zendesk_update_tickets_bulk` + +Update multiple tickets in Zendesk at once (max 100) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `ticketIds` | string | Yes | Comma-separated ticket IDs to update \(max 100\) | +| `status` | string | No | New status for all tickets | +| `priority` | string | No | New priority for all tickets | +| `assigneeId` | string | No | New assignee ID for all tickets | +| `groupId` | string | No | New group ID for all tickets | +| `tags` | string | No | Comma-separated tags to add to all tickets | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk update job status | + +### `zendesk_delete_ticket` + +Delete a ticket from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `ticketId` | string | Yes | Ticket ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Delete confirmation | + +### `zendesk_merge_tickets` + +Merge multiple tickets into a target ticket + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `targetTicketId` | string | Yes | Target ticket ID \(tickets will be merged into this one\) | +| `sourceTicketIds` | string | Yes | Comma-separated source ticket IDs to merge | +| `targetComment` | string | No | Comment to add to target ticket after merge | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Merge job status | + +### `zendesk_get_users` + +Retrieve a list of users from Zendesk with optional filtering + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain \(e.g., "mycompany" for mycompany.zendesk.com\) | +| `role` | string | No | Filter by role \(end-user, agent, admin\) | +| `permissionSet` | string | No | Filter by permission set ID | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Users data and metadata | + +### `zendesk_get_user` + +Get a single user by ID from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `userId` | string | Yes | User ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | User data | + +### `zendesk_get_current_user` + +Get the currently authenticated user from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Current user data | + +### `zendesk_search_users` + +Search for users in Zendesk using a query string + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `query` | string | No | Search query string | +| `externalId` | string | No | External ID to search by | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Users search results | + +### `zendesk_create_user` + +Create a new user in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `name` | string | Yes | User name | +| `userEmail` | string | No | User email | +| `role` | string | No | User role \(end-user, agent, admin\) | +| `phone` | string | No | User phone number | +| `organizationId` | string | No | Organization ID | +| `verified` | string | No | Set to "true" to skip email verification | +| `tags` | string | No | Comma-separated tags | +| `customFields` | string | No | Custom fields as JSON object \(e.g., \{"field_id": "value"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created user data | + +### `zendesk_create_users_bulk` + +Create multiple users in Zendesk using bulk import + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `users` | string | Yes | JSON array of user objects to create | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk creation job status | + +### `zendesk_update_user` + +Update an existing user in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `userId` | string | Yes | User ID to update | +| `name` | string | No | New user name | +| `userEmail` | string | No | New user email | +| `role` | string | No | User role \(end-user, agent, admin\) | +| `phone` | string | No | User phone number | +| `organizationId` | string | No | Organization ID | +| `verified` | string | No | Set to "true" to mark user as verified | +| `tags` | string | No | Comma-separated tags | +| `customFields` | string | No | Custom fields as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated user data | + +### `zendesk_update_users_bulk` + +Update multiple users in Zendesk using bulk update + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `users` | string | Yes | JSON array of user objects to update \(must include id field\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk update job status | + +### `zendesk_delete_user` + +Delete a user from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `userId` | string | Yes | User ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deleted user data | + +### `zendesk_get_organizations` + +Retrieve a list of organizations from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain \(e.g., "mycompany" for mycompany.zendesk.com\) | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Organizations data and metadata | + +### `zendesk_get_organization` + +Get a single organization by ID from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `organizationId` | string | Yes | Organization ID to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Organization data | + +### `zendesk_autocomplete_organizations` + +Autocomplete organizations in Zendesk by name prefix (for name matching/autocomplete) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `name` | string | Yes | Organization name to search for | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Organizations search results | + +### `zendesk_create_organization` + +Create a new organization in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `name` | string | Yes | Organization name | +| `domainNames` | string | No | Comma-separated domain names | +| `details` | string | No | Organization details | +| `notes` | string | No | Organization notes | +| `tags` | string | No | Comma-separated tags | +| `customFields` | string | No | Custom fields as JSON object \(e.g., \{"field_id": "value"\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Created organization data | + +### `zendesk_create_organizations_bulk` + +Create multiple organizations in Zendesk using bulk import + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `organizations` | string | Yes | JSON array of organization objects to create | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Bulk creation job status | + +### `zendesk_update_organization` + +Update an existing organization in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `organizationId` | string | Yes | Organization ID to update | +| `name` | string | No | New organization name | +| `domainNames` | string | No | Comma-separated domain names | +| `details` | string | No | Organization details | +| `notes` | string | No | Organization notes | +| `tags` | string | No | Comma-separated tags | +| `customFields` | string | No | Custom fields as JSON object | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Updated organization data | + +### `zendesk_delete_organization` + +Delete an organization from Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `organizationId` | string | Yes | Organization ID to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Deleted organization data | + +### `zendesk_search` + +Unified search across tickets, users, and organizations in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `query` | string | Yes | Search query string | +| `sortBy` | string | No | Sort field \(relevance, created_at, updated_at, priority, status, ticket_type\) | +| `sortOrder` | string | No | Sort order \(asc or desc\) | +| `perPage` | string | No | Results per page \(default: 100, max: 100\) | +| `page` | string | No | Page number | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search results | + +### `zendesk_search_count` + +Count the number of search results matching a query in Zendesk + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `email` | string | Yes | Your Zendesk email address | +| `apiToken` | string | Yes | Zendesk API token | +| `subdomain` | string | Yes | Your Zendesk subdomain | +| `query` | string | Yes | Search query string | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Operation success status | +| `output` | object | Search count result | + + + +## Notes + +- Category: `tools` +- Type: `zendesk` diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index 9b474d8022..bac04d13c1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -353,7 +353,7 @@ export function Editor() { blockId={currentBlockId} config={subBlock} isPreview={false} - subBlockValues={undefined} + subBlockValues={subBlockState} disabled={!userPermissions.canEdit} fieldDiffStatus={undefined} allowExpandInPreview={false} diff --git a/apps/sim/blocks/blocks/intercom.ts b/apps/sim/blocks/blocks/intercom.ts new file mode 100644 index 0000000000..5960d1b7c7 --- /dev/null +++ b/apps/sim/blocks/blocks/intercom.ts @@ -0,0 +1,571 @@ +import { IntercomIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const IntercomBlock: BlockConfig = { + type: 'intercom', + name: 'Intercom', + description: 'Manage contacts, companies, conversations, tickets, and messages in Intercom', + longDescription: + 'Integrate Intercom into the workflow. Can create, get, update, list, search, and delete contacts; create, get, and list companies; get, list, reply, and search conversations; create and get tickets; and create messages.', + docsLink: 'https://docs.sim.ai/tools/intercom', + authMode: AuthMode.ApiKey, + category: 'tools', + bgColor: '#E0E0E0', + icon: IntercomIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Create Contact', id: 'create_contact' }, + { label: 'Get Contact', id: 'get_contact' }, + { label: 'Update Contact', id: 'update_contact' }, + { label: 'List Contacts', id: 'list_contacts' }, + { label: 'Search Contacts', id: 'search_contacts' }, + { label: 'Delete Contact', id: 'delete_contact' }, + { label: 'Create Company', id: 'create_company' }, + { label: 'Get Company', id: 'get_company' }, + { label: 'List Companies', id: 'list_companies' }, + { label: 'Get Conversation', id: 'get_conversation' }, + { label: 'List Conversations', id: 'list_conversations' }, + { label: 'Reply to Conversation', id: 'reply_conversation' }, + { label: 'Search Conversations', id: 'search_conversations' }, + { label: 'Create Ticket', id: 'create_ticket' }, + { label: 'Get Ticket', id: 'get_ticket' }, + { label: 'Create Message', id: 'create_message' }, + ], + value: () => 'create_contact', + }, + { + id: 'accessToken', + title: 'Access Token', + type: 'short-input', + password: true, + placeholder: 'Enter your Intercom access token', + required: true, + }, + // Contact fields + { + id: 'contactId', + title: 'Contact ID', + type: 'short-input', + placeholder: 'Contact ID', + required: true, + condition: { + field: 'operation', + value: ['get_contact', 'update_contact', 'delete_contact'], + }, + }, + { + id: 'email', + title: 'Email', + type: 'short-input', + placeholder: 'Contact email', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'external_id', + title: 'External ID', + type: 'short-input', + placeholder: 'External identifier for the contact', + condition: { + field: 'operation', + value: ['create_contact'], + }, + }, + { + id: 'phone', + title: 'Phone', + type: 'short-input', + placeholder: 'Contact phone number', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'name', + title: 'Name', + type: 'short-input', + placeholder: 'Contact name', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'avatar', + title: 'Avatar URL', + type: 'short-input', + placeholder: 'Avatar image URL', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'signed_up_at', + title: 'Signed Up At', + type: 'short-input', + placeholder: 'Unix timestamp', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'last_seen_at', + title: 'Last Seen At', + type: 'short-input', + placeholder: 'Unix timestamp', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'owner_id', + title: 'Owner ID', + type: 'short-input', + placeholder: 'Admin ID', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'unsubscribed_from_emails', + title: 'Unsubscribed from Emails', + type: 'dropdown', + options: [ + { label: 'True', id: 'true' }, + { label: 'False', id: 'false' }, + ], + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'custom_attributes', + title: 'Custom Attributes', + type: 'long-input', + placeholder: 'JSON object with custom attributes', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'query', + title: 'Search Query', + type: 'long-input', + placeholder: 'JSON search query or text', + required: true, + condition: { + field: 'operation', + value: ['search_contacts', 'search_conversations'], + }, + }, + // Company fields + { + id: 'companyId', + title: 'Company ID', + type: 'short-input', + placeholder: 'Intercom company ID', + required: true, + condition: { + field: 'operation', + value: ['get_company'], + }, + }, + { + id: 'company_id', + title: 'Company ID (External)', + type: 'short-input', + placeholder: 'Your unique identifier for the company', + required: true, + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'company_name', + title: 'Company Name', + type: 'short-input', + placeholder: 'Company name', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'website', + title: 'Website', + type: 'short-input', + placeholder: 'Company website', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'plan', + title: 'Plan', + type: 'short-input', + placeholder: 'Subscription plan', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'size', + title: 'Size', + type: 'short-input', + placeholder: 'Number of employees', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'industry', + title: 'Industry', + type: 'short-input', + placeholder: 'Company industry', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + { + id: 'monthly_spend', + title: 'Monthly Spend', + type: 'short-input', + placeholder: 'Revenue amount', + condition: { + field: 'operation', + value: ['create_company'], + }, + }, + // Conversation fields + { + id: 'conversationId', + title: 'Conversation ID', + type: 'short-input', + placeholder: 'Conversation ID', + required: true, + condition: { + field: 'operation', + value: ['get_conversation', 'reply_conversation'], + }, + }, + { + id: 'display_as', + title: 'Display As', + type: 'dropdown', + options: [ + { label: 'HTML', id: 'html' }, + { label: 'Plain Text', id: 'plaintext' }, + ], + condition: { + field: 'operation', + value: ['get_conversation'], + }, + }, + { + id: 'message_type', + title: 'Message Type', + type: 'dropdown', + options: [ + { label: 'Comment', id: 'comment' }, + { label: 'Note', id: 'note' }, + ], + required: true, + condition: { + field: 'operation', + value: ['reply_conversation'], + }, + }, + { + id: 'body', + title: 'Message Body', + type: 'long-input', + placeholder: 'Message text', + required: true, + condition: { + field: 'operation', + value: ['reply_conversation', 'create_message'], + }, + }, + { + id: 'admin_id', + title: 'Admin ID', + type: 'short-input', + placeholder: 'ID of the admin sending the message', + required: true, + condition: { + field: 'operation', + value: ['reply_conversation'], + }, + }, + { + id: 'attachment_urls', + title: 'Attachment URLs', + type: 'short-input', + placeholder: 'Comma-separated image URLs (max 10)', + condition: { + field: 'operation', + value: ['reply_conversation'], + }, + }, + // Ticket fields + { + id: 'ticketId', + title: 'Ticket ID', + type: 'short-input', + placeholder: 'Ticket ID', + required: true, + condition: { + field: 'operation', + value: ['get_ticket'], + }, + }, + { + id: 'ticket_type_id', + title: 'Ticket Type ID', + type: 'short-input', + placeholder: 'ID of the ticket type', + required: true, + condition: { + field: 'operation', + value: ['create_ticket'], + }, + }, + { + id: 'contacts', + title: 'Contacts', + type: 'long-input', + placeholder: 'JSON array of contact identifiers', + required: true, + condition: { + field: 'operation', + value: ['create_ticket'], + }, + }, + { + id: 'ticket_attributes', + title: 'Ticket Attributes', + type: 'long-input', + placeholder: 'JSON object with ticket attributes', + required: true, + condition: { + field: 'operation', + value: ['create_ticket'], + }, + }, + // Message fields + { + id: 'message_type_msg', + title: 'Message Type', + type: 'dropdown', + options: [ + { label: 'In-App', id: 'inapp' }, + { label: 'Email', id: 'email' }, + ], + required: true, + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + { + id: 'subject', + title: 'Subject', + type: 'short-input', + placeholder: 'Email subject (for email type)', + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + { + id: 'from_type', + title: 'From Type', + type: 'dropdown', + options: [{ label: 'Admin', id: 'admin' }], + required: true, + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + { + id: 'from_id', + title: 'From ID', + type: 'short-input', + placeholder: 'Admin ID', + required: true, + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + { + id: 'to_type', + title: 'To Type', + type: 'dropdown', + options: [{ label: 'Contact', id: 'contact' }], + required: true, + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + { + id: 'to_id', + title: 'To ID', + type: 'short-input', + placeholder: 'Contact ID', + required: true, + condition: { + field: 'operation', + value: ['create_message'], + }, + }, + // Pagination fields + { + id: 'per_page', + title: 'Per Page', + type: 'short-input', + placeholder: 'Results per page (max: 150)', + condition: { + field: 'operation', + value: [ + 'list_contacts', + 'search_contacts', + 'list_companies', + 'list_conversations', + 'search_conversations', + ], + }, + }, + { + id: 'starting_after', + title: 'Starting After', + type: 'short-input', + placeholder: 'Cursor for pagination', + condition: { + field: 'operation', + value: ['list_contacts', 'search_contacts', 'list_conversations', 'search_conversations'], + }, + }, + { + id: 'page', + title: 'Page', + type: 'short-input', + placeholder: 'Page number', + condition: { + field: 'operation', + value: ['list_companies'], + }, + }, + ], + tools: { + access: [ + 'intercom_create_contact', + 'intercom_get_contact', + 'intercom_update_contact', + 'intercom_list_contacts', + 'intercom_search_contacts', + 'intercom_delete_contact', + 'intercom_create_company', + 'intercom_get_company', + 'intercom_list_companies', + 'intercom_get_conversation', + 'intercom_list_conversations', + 'intercom_reply_conversation', + 'intercom_search_conversations', + 'intercom_create_ticket', + 'intercom_get_ticket', + 'intercom_create_message', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'create_contact': + return 'intercom_create_contact' + case 'get_contact': + return 'intercom_get_contact' + case 'update_contact': + return 'intercom_update_contact' + case 'list_contacts': + return 'intercom_list_contacts' + case 'search_contacts': + return 'intercom_search_contacts' + case 'delete_contact': + return 'intercom_delete_contact' + case 'create_company': + return 'intercom_create_company' + case 'get_company': + return 'intercom_get_company' + case 'list_companies': + return 'intercom_list_companies' + case 'get_conversation': + return 'intercom_get_conversation' + case 'list_conversations': + return 'intercom_list_conversations' + case 'reply_conversation': + return 'intercom_reply_conversation' + case 'search_conversations': + return 'intercom_search_conversations' + case 'create_ticket': + return 'intercom_create_ticket' + case 'get_ticket': + return 'intercom_get_ticket' + case 'create_message': + return 'intercom_create_message' + default: + throw new Error(`Unknown operation: ${params.operation}`) + } + }, + params: (params) => { + const { operation, message_type_msg, company_name, ...rest } = params + const cleanParams: Record = {} + + // Special mapping for message_type in create_message + if (operation === 'create_message' && message_type_msg) { + cleanParams.message_type = message_type_msg + } + + // Special mapping for company name + if (operation === 'create_company' && company_name) { + cleanParams.name = company_name + } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + cleanParams[key] = value + } + }) + + return cleanParams + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + accessToken: { type: 'string', description: 'Intercom API access token' }, + }, + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { type: 'json', description: 'Operation result data' }, + }, +} diff --git a/apps/sim/blocks/blocks/mailchimp.ts b/apps/sim/blocks/blocks/mailchimp.ts new file mode 100644 index 0000000000..dec15a6531 --- /dev/null +++ b/apps/sim/blocks/blocks/mailchimp.ts @@ -0,0 +1,1056 @@ +import { MailchimpIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const MailchimpBlock: BlockConfig = { + type: 'mailchimp', + name: 'Mailchimp', + description: 'Manage audiences, campaigns, and marketing automation in Mailchimp', + longDescription: + 'Integrate Mailchimp into the workflow. Can manage audiences (lists), list members, campaigns, automation workflows, templates, reports, segments, tags, merge fields, interest categories, landing pages, signup forms, and batch operations.', + docsLink: 'https://docs.sim.ai/tools/mailchimp', + authMode: AuthMode.ApiKey, + category: 'tools', + bgColor: '#FFE01B', + icon: MailchimpIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + // Audience/List operations + { label: 'Get Audiences', id: 'get_audiences' }, + { label: 'Get Audience', id: 'get_audience' }, + { label: 'Create Audience', id: 'create_audience' }, + { label: 'Update Audience', id: 'update_audience' }, + { label: 'Delete Audience', id: 'delete_audience' }, + // Member operations + { label: 'Get Members', id: 'get_members' }, + { label: 'Get Member', id: 'get_member' }, + { label: 'Add Member', id: 'add_member' }, + { label: 'Add or Update Member', id: 'add_or_update_member' }, + { label: 'Update Member', id: 'update_member' }, + { label: 'Delete Member', id: 'delete_member' }, + { label: 'Archive Member', id: 'archive_member' }, + { label: 'Unarchive Member', id: 'unarchive_member' }, + // Campaign operations + { label: 'Get Campaigns', id: 'get_campaigns' }, + { label: 'Get Campaign', id: 'get_campaign' }, + { label: 'Create Campaign', id: 'create_campaign' }, + { label: 'Update Campaign', id: 'update_campaign' }, + { label: 'Delete Campaign', id: 'delete_campaign' }, + { label: 'Send Campaign', id: 'send_campaign' }, + { label: 'Schedule Campaign', id: 'schedule_campaign' }, + { label: 'Unschedule Campaign', id: 'unschedule_campaign' }, + { label: 'Replicate Campaign', id: 'replicate_campaign' }, + // Campaign content operations + { label: 'Get Campaign Content', id: 'get_campaign_content' }, + { label: 'Set Campaign Content', id: 'set_campaign_content' }, + // Automation operations + { label: 'Get Automations', id: 'get_automations' }, + { label: 'Get Automation', id: 'get_automation' }, + { label: 'Start Automation', id: 'start_automation' }, + { label: 'Pause Automation', id: 'pause_automation' }, + { label: 'Add Subscriber to Automation', id: 'add_subscriber_to_automation' }, + // Template operations + { label: 'Get Templates', id: 'get_templates' }, + { label: 'Get Template', id: 'get_template' }, + { label: 'Create Template', id: 'create_template' }, + { label: 'Update Template', id: 'update_template' }, + { label: 'Delete Template', id: 'delete_template' }, + // Report operations + { label: 'Get Campaign Reports', id: 'get_campaign_reports' }, + { label: 'Get Campaign Report', id: 'get_campaign_report' }, + // Segment operations + { label: 'Get Segments', id: 'get_segments' }, + { label: 'Get Segment', id: 'get_segment' }, + { label: 'Create Segment', id: 'create_segment' }, + { label: 'Update Segment', id: 'update_segment' }, + { label: 'Delete Segment', id: 'delete_segment' }, + { label: 'Get Segment Members', id: 'get_segment_members' }, + { label: 'Add Segment Member', id: 'add_segment_member' }, + { label: 'Remove Segment Member', id: 'remove_segment_member' }, + // Tag operations + { label: 'Get Member Tags', id: 'get_member_tags' }, + { label: 'Add Member Tags', id: 'add_member_tags' }, + { label: 'Remove Member Tags', id: 'remove_member_tags' }, + // Merge fields operations + { label: 'Get Merge Fields', id: 'get_merge_fields' }, + { label: 'Get Merge Field', id: 'get_merge_field' }, + { label: 'Create Merge Field', id: 'create_merge_field' }, + { label: 'Update Merge Field', id: 'update_merge_field' }, + { label: 'Delete Merge Field', id: 'delete_merge_field' }, + // Interest categories operations + { label: 'Get Interest Categories', id: 'get_interest_categories' }, + { label: 'Get Interest Category', id: 'get_interest_category' }, + { label: 'Create Interest Category', id: 'create_interest_category' }, + { label: 'Update Interest Category', id: 'update_interest_category' }, + { label: 'Delete Interest Category', id: 'delete_interest_category' }, + // Interest operations + { label: 'Get Interests', id: 'get_interests' }, + { label: 'Get Interest', id: 'get_interest' }, + { label: 'Create Interest', id: 'create_interest' }, + { label: 'Update Interest', id: 'update_interest' }, + { label: 'Delete Interest', id: 'delete_interest' }, + // Landing page operations + { label: 'Get Landing Pages', id: 'get_landing_pages' }, + { label: 'Get Landing Page', id: 'get_landing_page' }, + { label: 'Create Landing Page', id: 'create_landing_page' }, + { label: 'Update Landing Page', id: 'update_landing_page' }, + { label: 'Delete Landing Page', id: 'delete_landing_page' }, + { label: 'Publish Landing Page', id: 'publish_landing_page' }, + { label: 'Unpublish Landing Page', id: 'unpublish_landing_page' }, + // Batch operations + { label: 'Get Batch Operations', id: 'get_batch_operations' }, + { label: 'Get Batch Operation', id: 'get_batch_operation' }, + { label: 'Create Batch Operation', id: 'create_batch_operation' }, + { label: 'Delete Batch Operation', id: 'delete_batch_operation' }, + ], + value: () => 'get_audiences', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + password: true, + placeholder: 'Enter your Mailchimp API key (includes server prefix)', + required: true, + }, + // Audience/List fields + { + id: 'listId', + title: 'Audience ID', + type: 'short-input', + placeholder: 'Audience/List ID', + required: { + field: 'operation', + value: [ + 'get_audience', + 'update_audience', + 'delete_audience', + 'get_members', + 'get_member', + 'add_member', + 'add_or_update_member', + 'update_member', + 'delete_member', + 'archive_member', + 'unarchive_member', + 'get_segments', + 'get_segment', + 'create_segment', + 'update_segment', + 'delete_segment', + 'get_segment_members', + 'add_segment_member', + 'remove_segment_member', + 'get_member_tags', + 'add_member_tags', + 'remove_member_tags', + 'get_merge_fields', + 'get_merge_field', + 'create_merge_field', + 'update_merge_field', + 'delete_merge_field', + 'get_interest_categories', + 'get_interest_category', + 'create_interest_category', + 'update_interest_category', + 'delete_interest_category', + 'get_interests', + 'get_interest', + 'create_interest', + 'update_interest', + 'delete_interest', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_audience', + 'update_audience', + 'delete_audience', + 'get_members', + 'get_member', + 'add_member', + 'add_or_update_member', + 'update_member', + 'delete_member', + 'archive_member', + 'unarchive_member', + 'get_segments', + 'get_segment', + 'create_segment', + 'update_segment', + 'delete_segment', + 'get_segment_members', + 'add_segment_member', + 'remove_segment_member', + 'get_member_tags', + 'add_member_tags', + 'remove_member_tags', + 'get_merge_fields', + 'get_merge_field', + 'create_merge_field', + 'update_merge_field', + 'delete_merge_field', + 'get_interest_categories', + 'get_interest_category', + 'create_interest_category', + 'update_interest_category', + 'delete_interest_category', + 'get_interests', + 'get_interest', + 'create_interest', + 'update_interest', + 'delete_interest', + ], + }, + }, + { + id: 'audienceName', + title: 'Audience Name', + type: 'short-input', + placeholder: 'Name for the audience', + required: { + field: 'operation', + value: ['create_audience'], + }, + condition: { + field: 'operation', + value: ['create_audience', 'update_audience'], + }, + }, + { + id: 'contact', + title: 'Contact Information', + type: 'long-input', + placeholder: 'JSON object with company, address1, city, state, zip, country', + required: { + field: 'operation', + value: ['create_audience'], + }, + condition: { + field: 'operation', + value: ['create_audience', 'update_audience'], + }, + }, + { + id: 'permissionReminder', + title: 'Permission Reminder', + type: 'short-input', + placeholder: 'Permission reminder text', + required: { + field: 'operation', + value: ['create_audience'], + }, + condition: { + field: 'operation', + value: ['create_audience', 'update_audience'], + }, + }, + { + id: 'campaignDefaults', + title: 'Campaign Defaults', + type: 'long-input', + placeholder: 'JSON object with from_name, from_email, subject, language', + required: { + field: 'operation', + value: ['create_audience'], + }, + condition: { + field: 'operation', + value: ['create_audience', 'update_audience'], + }, + }, + { + id: 'emailTypeOption', + title: 'Email Type Option', + type: 'dropdown', + options: [ + { label: 'True', id: 'true' }, + { label: 'False', id: 'false' }, + ], + required: { + field: 'operation', + value: ['create_audience'], + }, + condition: { + field: 'operation', + value: ['create_audience', 'update_audience'], + }, + value: () => 'true', + }, + // Member fields + { + id: 'subscriberEmail', + title: 'Subscriber Email', + type: 'short-input', + placeholder: 'Email address or MD5 hash', + required: { + field: 'operation', + value: [ + 'get_member', + 'update_member', + 'delete_member', + 'archive_member', + 'unarchive_member', + 'get_member_tags', + 'add_member_tags', + 'remove_member_tags', + 'add_or_update_member', + 'remove_segment_member', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_member', + 'update_member', + 'delete_member', + 'archive_member', + 'unarchive_member', + 'get_member_tags', + 'add_member_tags', + 'remove_member_tags', + 'add_or_update_member', + 'remove_segment_member', + ], + }, + }, + { + id: 'emailAddress', + title: 'Email Address', + type: 'short-input', + placeholder: 'Member email address', + required: { + field: 'operation', + value: [ + 'add_member', + 'add_or_update_member', + 'unarchive_member', + 'add_segment_member', + 'add_subscriber_to_automation', + ], + }, + condition: { + field: 'operation', + value: [ + 'add_member', + 'add_or_update_member', + 'update_member', + 'unarchive_member', + 'add_segment_member', + 'add_subscriber_to_automation', + ], + }, + }, + { + id: 'status', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Subscribed', id: 'subscribed' }, + { label: 'Unsubscribed', id: 'unsubscribed' }, + { label: 'Cleaned', id: 'cleaned' }, + { label: 'Pending', id: 'pending' }, + { label: 'Transactional', id: 'transactional' }, + ], + required: { + field: 'operation', + value: ['add_member', 'unarchive_member'], + }, + condition: { + field: 'operation', + value: ['get_members', 'add_member', 'update_member', 'unarchive_member'], + }, + }, + { + id: 'statusIfNew', + title: 'Status If New', + type: 'dropdown', + options: [ + { label: 'Subscribed', id: 'subscribed' }, + { label: 'Unsubscribed', id: 'unsubscribed' }, + { label: 'Cleaned', id: 'cleaned' }, + { label: 'Pending', id: 'pending' }, + { label: 'Transactional', id: 'transactional' }, + ], + required: { + field: 'operation', + value: ['add_or_update_member'], + }, + condition: { + field: 'operation', + value: ['add_or_update_member'], + }, + }, + { + id: 'mergeFields', + title: 'Merge Fields', + type: 'long-input', + placeholder: 'JSON object with merge field values (e.g., {"FNAME": "John", "LNAME": "Doe"})', + condition: { + field: 'operation', + value: ['add_member', 'add_or_update_member', 'update_member'], + }, + }, + { + id: 'interests', + title: 'Interests', + type: 'long-input', + placeholder: 'JSON object with interest IDs and boolean values', + condition: { + field: 'operation', + value: ['add_member', 'add_or_update_member', 'update_member'], + }, + }, + { + id: 'tags', + title: 'Tags', + type: 'long-input', + placeholder: 'JSON array of tag objects with name and status', + required: { + field: 'operation', + value: ['add_member_tags', 'remove_member_tags'], + }, + condition: { + field: 'operation', + value: ['add_member_tags', 'remove_member_tags'], + }, + }, + // Campaign fields + { + id: 'campaignId', + title: 'Campaign ID', + type: 'short-input', + placeholder: 'Campaign ID', + required: { + field: 'operation', + value: [ + 'get_campaign', + 'update_campaign', + 'delete_campaign', + 'send_campaign', + 'schedule_campaign', + 'unschedule_campaign', + 'replicate_campaign', + 'get_campaign_content', + 'set_campaign_content', + 'get_campaign_report', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_campaign', + 'update_campaign', + 'delete_campaign', + 'send_campaign', + 'schedule_campaign', + 'unschedule_campaign', + 'replicate_campaign', + 'get_campaign_content', + 'set_campaign_content', + 'get_campaign_report', + ], + }, + }, + { + id: 'campaignType', + title: 'Campaign Type', + type: 'dropdown', + options: [ + { label: 'Regular', id: 'regular' }, + { label: 'Plain Text', id: 'plaintext' }, + { label: 'A/B Split', id: 'absplit' }, + { label: 'RSS', id: 'rss' }, + { label: 'Variate', id: 'variate' }, + ], + required: { + field: 'operation', + value: ['create_campaign'], + }, + condition: { + field: 'operation', + value: ['get_campaigns', 'create_campaign'], + }, + }, + { + id: 'campaignSettings', + title: 'Campaign Settings', + type: 'long-input', + placeholder: 'JSON object with subject_line, from_name, reply_to, etc. (required for create)', + required: { + field: 'operation', + value: ['create_campaign'], + }, + condition: { + field: 'operation', + value: ['create_campaign', 'update_campaign'], + }, + }, + { + id: 'recipients', + title: 'Recipients', + type: 'long-input', + placeholder: 'JSON object with list_id and optional segment_opts', + condition: { + field: 'operation', + value: ['create_campaign', 'update_campaign'], + }, + }, + { + id: 'scheduleTime', + title: 'Schedule Time', + type: 'short-input', + placeholder: 'ISO 8601 date-time (e.g., 2024-12-31T10:00:00+00:00)', + required: { + field: 'operation', + value: ['schedule_campaign'], + }, + condition: { + field: 'operation', + value: ['schedule_campaign'], + }, + }, + { + id: 'html', + title: 'HTML Content', + type: 'long-input', + placeholder: 'HTML content for the campaign', + condition: { + field: 'operation', + value: ['set_campaign_content'], + }, + }, + { + id: 'plainText', + title: 'Plain Text Content', + type: 'long-input', + placeholder: 'Plain text content for the campaign', + condition: { + field: 'operation', + value: ['set_campaign_content'], + }, + }, + { + id: 'templateId', + title: 'Template ID', + type: 'short-input', + placeholder: 'Template ID', + required: { + field: 'operation', + value: ['get_template', 'update_template', 'delete_template'], + }, + condition: { + field: 'operation', + value: ['get_template', 'update_template', 'delete_template', 'set_campaign_content'], + }, + }, + { + id: 'templateName', + title: 'Template Name', + type: 'short-input', + placeholder: 'Template name', + required: { + field: 'operation', + value: ['create_template'], + }, + condition: { + field: 'operation', + value: ['create_template', 'update_template'], + }, + }, + { + id: 'templateHtml', + title: 'Template HTML', + type: 'long-input', + placeholder: 'HTML content for the template', + required: { + field: 'operation', + value: ['create_template'], + }, + condition: { + field: 'operation', + value: ['create_template', 'update_template'], + }, + }, + // Automation fields + { + id: 'workflowId', + title: 'Workflow ID', + type: 'short-input', + placeholder: 'Automation workflow ID', + required: { + field: 'operation', + value: [ + 'get_automation', + 'start_automation', + 'pause_automation', + 'add_subscriber_to_automation', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_automation', + 'start_automation', + 'pause_automation', + 'add_subscriber_to_automation', + ], + }, + }, + { + id: 'workflowEmailId', + title: 'Workflow Email ID', + type: 'short-input', + placeholder: 'Workflow email ID', + required: { + field: 'operation', + value: ['add_subscriber_to_automation'], + }, + condition: { + field: 'operation', + value: ['add_subscriber_to_automation'], + }, + }, + // Segment fields + { + id: 'segmentId', + title: 'Segment ID', + type: 'short-input', + placeholder: 'Segment ID', + required: { + field: 'operation', + value: [ + 'get_segment', + 'update_segment', + 'delete_segment', + 'get_segment_members', + 'add_segment_member', + 'remove_segment_member', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_segment', + 'update_segment', + 'delete_segment', + 'get_segment_members', + 'add_segment_member', + 'remove_segment_member', + ], + }, + }, + { + id: 'segmentName', + title: 'Segment Name', + type: 'short-input', + placeholder: 'Segment name', + required: { + field: 'operation', + value: ['create_segment'], + }, + condition: { + field: 'operation', + value: ['create_segment', 'update_segment'], + }, + }, + { + id: 'segmentOptions', + title: 'Segment Options', + type: 'long-input', + placeholder: 'JSON object with conditions for segment', + condition: { + field: 'operation', + value: ['create_segment', 'update_segment'], + }, + }, + // Merge field fields + { + id: 'mergeId', + title: 'Merge Field ID', + type: 'short-input', + placeholder: 'Merge field ID', + required: { + field: 'operation', + value: ['get_merge_field', 'update_merge_field', 'delete_merge_field'], + }, + condition: { + field: 'operation', + value: ['get_merge_field', 'update_merge_field', 'delete_merge_field'], + }, + }, + { + id: 'mergeName', + title: 'Merge Field Name', + type: 'short-input', + placeholder: 'Merge field name', + required: { + field: 'operation', + value: ['create_merge_field'], + }, + condition: { + field: 'operation', + value: ['create_merge_field', 'update_merge_field'], + }, + }, + { + id: 'mergeType', + title: 'Merge Field Type', + type: 'dropdown', + options: [ + { label: 'Text', id: 'text' }, + { label: 'Number', id: 'number' }, + { label: 'Address', id: 'address' }, + { label: 'Phone', id: 'phone' }, + { label: 'Date', id: 'date' }, + { label: 'URL', id: 'url' }, + { label: 'Image URL', id: 'imageurl' }, + { label: 'Radio', id: 'radio' }, + { label: 'Dropdown', id: 'dropdown' }, + { label: 'Birthday', id: 'birthday' }, + { label: 'Zip', id: 'zip' }, + ], + required: { + field: 'operation', + value: ['create_merge_field'], + }, + condition: { + field: 'operation', + value: ['create_merge_field'], + }, + }, + // Interest category fields + { + id: 'interestCategoryId', + title: 'Interest Category ID', + type: 'short-input', + placeholder: 'Interest category ID', + required: { + field: 'operation', + value: [ + 'get_interest_category', + 'update_interest_category', + 'delete_interest_category', + 'get_interests', + 'get_interest', + 'create_interest', + 'update_interest', + 'delete_interest', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_interest_category', + 'update_interest_category', + 'delete_interest_category', + 'get_interests', + 'get_interest', + 'create_interest', + 'update_interest', + 'delete_interest', + ], + }, + }, + { + id: 'interestCategoryTitle', + title: 'Interest Category Title', + type: 'short-input', + placeholder: 'Interest category title', + required: { + field: 'operation', + value: ['create_interest_category'], + }, + condition: { + field: 'operation', + value: ['create_interest_category', 'update_interest_category'], + }, + }, + { + id: 'interestCategoryType', + title: 'Interest Category Type', + type: 'dropdown', + options: [ + { label: 'Checkboxes', id: 'checkboxes' }, + { label: 'Dropdown', id: 'dropdown' }, + { label: 'Radio', id: 'radio' }, + { label: 'Hidden', id: 'hidden' }, + ], + required: { + field: 'operation', + value: ['create_interest_category'], + }, + condition: { + field: 'operation', + value: ['create_interest_category'], + }, + }, + // Interest fields + { + id: 'interestId', + title: 'Interest ID', + type: 'short-input', + placeholder: 'Interest ID', + required: { + field: 'operation', + value: ['get_interest', 'update_interest', 'delete_interest'], + }, + condition: { + field: 'operation', + value: ['get_interest', 'update_interest', 'delete_interest'], + }, + }, + { + id: 'interestName', + title: 'Interest Name', + type: 'short-input', + placeholder: 'Interest name', + required: { + field: 'operation', + value: ['create_interest'], + }, + condition: { + field: 'operation', + value: ['create_interest', 'update_interest'], + }, + }, + // Landing page fields + { + id: 'pageId', + title: 'Landing Page ID', + type: 'short-input', + placeholder: 'Landing page ID', + required: { + field: 'operation', + value: [ + 'get_landing_page', + 'update_landing_page', + 'delete_landing_page', + 'publish_landing_page', + 'unpublish_landing_page', + ], + }, + condition: { + field: 'operation', + value: [ + 'get_landing_page', + 'update_landing_page', + 'delete_landing_page', + 'publish_landing_page', + 'unpublish_landing_page', + ], + }, + }, + { + id: 'landingPageTitle', + title: 'Landing Page Title', + type: 'short-input', + placeholder: 'Landing page title', + condition: { + field: 'operation', + value: ['create_landing_page', 'update_landing_page'], + }, + }, + { + id: 'landingPageType', + title: 'Landing Page Type', + type: 'dropdown', + options: [ + { label: 'Signup', id: 'signup' }, + { label: 'Click Through', id: 'click-through' }, + ], + required: { + field: 'operation', + value: ['create_landing_page'], + }, + condition: { + field: 'operation', + value: ['create_landing_page'], + }, + }, + // Batch operation fields + { + id: 'batchId', + title: 'Batch ID', + type: 'short-input', + placeholder: 'Batch operation ID', + required: { + field: 'operation', + value: ['get_batch_operation', 'delete_batch_operation'], + }, + condition: { + field: 'operation', + value: ['get_batch_operation', 'delete_batch_operation'], + }, + }, + { + id: 'operations', + title: 'Operations', + type: 'long-input', + placeholder: 'JSON array of operations with method, path, body, etc.', + required: { + field: 'operation', + value: ['create_batch_operation'], + }, + condition: { + field: 'operation', + value: ['create_batch_operation'], + }, + }, + // Pagination and filtering + { + id: 'count', + title: 'Count', + type: 'short-input', + placeholder: 'Number of results (default: 10, max: 1000)', + condition: { + field: 'operation', + value: [ + 'get_audiences', + 'get_members', + 'get_campaigns', + 'get_automations', + 'get_templates', + 'get_campaign_reports', + 'get_segments', + 'get_segment_members', + 'get_merge_fields', + 'get_interest_categories', + 'get_interests', + 'get_landing_pages', + 'get_batch_operations', + ], + }, + }, + { + id: 'offset', + title: 'Offset', + type: 'short-input', + placeholder: 'Number of results to skip', + condition: { + field: 'operation', + value: [ + 'get_audiences', + 'get_members', + 'get_campaigns', + 'get_automations', + 'get_templates', + 'get_campaign_reports', + 'get_segments', + 'get_segment_members', + 'get_merge_fields', + 'get_interest_categories', + 'get_interests', + 'get_landing_pages', + 'get_batch_operations', + ], + }, + }, + ], + tools: { + access: [ + 'mailchimp_get_audiences', + 'mailchimp_get_audience', + 'mailchimp_create_audience', + 'mailchimp_update_audience', + 'mailchimp_delete_audience', + 'mailchimp_get_members', + 'mailchimp_get_member', + 'mailchimp_add_member', + 'mailchimp_add_or_update_member', + 'mailchimp_update_member', + 'mailchimp_delete_member', + 'mailchimp_archive_member', + 'mailchimp_unarchive_member', + 'mailchimp_get_campaigns', + 'mailchimp_get_campaign', + 'mailchimp_create_campaign', + 'mailchimp_update_campaign', + 'mailchimp_delete_campaign', + 'mailchimp_send_campaign', + 'mailchimp_schedule_campaign', + 'mailchimp_unschedule_campaign', + 'mailchimp_replicate_campaign', + 'mailchimp_get_campaign_content', + 'mailchimp_set_campaign_content', + 'mailchimp_get_automations', + 'mailchimp_get_automation', + 'mailchimp_start_automation', + 'mailchimp_pause_automation', + 'mailchimp_add_subscriber_to_automation', + 'mailchimp_get_templates', + 'mailchimp_get_template', + 'mailchimp_create_template', + 'mailchimp_update_template', + 'mailchimp_delete_template', + 'mailchimp_get_campaign_reports', + 'mailchimp_get_campaign_report', + 'mailchimp_get_segments', + 'mailchimp_get_segment', + 'mailchimp_create_segment', + 'mailchimp_update_segment', + 'mailchimp_delete_segment', + 'mailchimp_get_segment_members', + 'mailchimp_add_segment_member', + 'mailchimp_remove_segment_member', + 'mailchimp_get_member_tags', + 'mailchimp_add_member_tags', + 'mailchimp_remove_member_tags', + 'mailchimp_get_merge_fields', + 'mailchimp_get_merge_field', + 'mailchimp_create_merge_field', + 'mailchimp_update_merge_field', + 'mailchimp_delete_merge_field', + 'mailchimp_get_interest_categories', + 'mailchimp_get_interest_category', + 'mailchimp_create_interest_category', + 'mailchimp_update_interest_category', + 'mailchimp_delete_interest_category', + 'mailchimp_get_interests', + 'mailchimp_get_interest', + 'mailchimp_create_interest', + 'mailchimp_update_interest', + 'mailchimp_delete_interest', + 'mailchimp_get_landing_pages', + 'mailchimp_get_landing_page', + 'mailchimp_create_landing_page', + 'mailchimp_update_landing_page', + 'mailchimp_delete_landing_page', + 'mailchimp_publish_landing_page', + 'mailchimp_unpublish_landing_page', + 'mailchimp_get_batch_operations', + 'mailchimp_get_batch_operation', + 'mailchimp_create_batch_operation', + 'mailchimp_delete_batch_operation', + ], + config: { + tool: (params) => { + return `mailchimp_${params.operation}` + }, + params: (params) => { + const { apiKey, operation, ...rest } = params + const cleanParams: Record = { apiKey } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + cleanParams[key] = value + } + }) + return cleanParams + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Mailchimp API key with server prefix' }, + }, + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { type: 'json', description: 'Operation result data' }, + }, +} diff --git a/apps/sim/blocks/blocks/pylon.ts b/apps/sim/blocks/blocks/pylon.ts new file mode 100644 index 0000000000..d6418ff3be --- /dev/null +++ b/apps/sim/blocks/blocks/pylon.ts @@ -0,0 +1,838 @@ +import { PylonIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const PylonBlock: BlockConfig = { + type: 'pylon', + name: 'Pylon', + description: + 'Manage customer support issues, accounts, contacts, users, teams, and tags in Pylon', + longDescription: + 'Integrate Pylon into the workflow. Manage issues (list, create, get, update, delete, search, snooze, followers, external issues), accounts (list, create, get, update, delete, bulk update, search), contacts (list, create, get, update, delete, search), users (list, get, update, search), teams (list, get, create, update), tags (list, get, create, update, delete), and messages (redact).', + docsLink: 'https://docs.usepylon.com/pylon-docs/developer/api', + authMode: AuthMode.ApiKey, + category: 'tools', + bgColor: '#E8F4FA', + icon: PylonIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + // Issue operations + { label: 'List Issues', id: 'list_issues' }, + { label: 'Create Issue', id: 'create_issue' }, + { label: 'Get Issue', id: 'get_issue' }, + { label: 'Update Issue', id: 'update_issue' }, + { label: 'Delete Issue', id: 'delete_issue' }, + { label: 'Search Issues', id: 'search_issues' }, + { label: 'Snooze Issue', id: 'snooze_issue' }, + { label: 'List Issue Followers', id: 'list_issue_followers' }, + { label: 'Manage Issue Followers', id: 'manage_issue_followers' }, + { label: 'Link External Issue', id: 'link_external_issue' }, + // Account operations + { label: 'List Accounts', id: 'list_accounts' }, + { label: 'Create Account', id: 'create_account' }, + { label: 'Get Account', id: 'get_account' }, + { label: 'Update Account', id: 'update_account' }, + { label: 'Delete Account', id: 'delete_account' }, + { label: 'Bulk Update Accounts', id: 'bulk_update_accounts' }, + { label: 'Search Accounts', id: 'search_accounts' }, + // Contact operations + { label: 'List Contacts', id: 'list_contacts' }, + { label: 'Create Contact', id: 'create_contact' }, + { label: 'Get Contact', id: 'get_contact' }, + { label: 'Update Contact', id: 'update_contact' }, + { label: 'Delete Contact', id: 'delete_contact' }, + { label: 'Search Contacts', id: 'search_contacts' }, + // User operations + { label: 'List Users', id: 'list_users' }, + { label: 'Get User', id: 'get_user' }, + { label: 'Update User', id: 'update_user' }, + { label: 'Search Users', id: 'search_users' }, + // Team operations + { label: 'List Teams', id: 'list_teams' }, + { label: 'Get Team', id: 'get_team' }, + { label: 'Create Team', id: 'create_team' }, + { label: 'Update Team', id: 'update_team' }, + // Tag operations + { label: 'List Tags', id: 'list_tags' }, + { label: 'Get Tag', id: 'get_tag' }, + { label: 'Create Tag', id: 'create_tag' }, + { label: 'Update Tag', id: 'update_tag' }, + { label: 'Delete Tag', id: 'delete_tag' }, + // Message operations + { label: 'Redact Message', id: 'redact_message' }, + ], + value: () => 'list_issues', + }, + { + id: 'apiToken', + title: 'API Token', + type: 'short-input', + password: true, + placeholder: 'Enter your Pylon API token', + required: true, + }, + // Issue fields + { + id: 'startTime', + title: 'Start Time', + type: 'short-input', + placeholder: 'RFC3339 format (e.g., 2024-01-01T00:00:00Z)', + required: true, + condition: { + field: 'operation', + value: ['list_issues'], + }, + }, + { + id: 'endTime', + title: 'End Time', + type: 'short-input', + placeholder: 'RFC3339 format (e.g., 2024-01-31T23:59:59Z)', + required: true, + condition: { + field: 'operation', + value: ['list_issues'], + }, + }, + { + id: 'issueId', + title: 'Issue ID', + type: 'short-input', + placeholder: 'Issue ID', + required: true, + condition: { + field: 'operation', + value: [ + 'get_issue', + 'update_issue', + 'delete_issue', + 'snooze_issue', + 'list_issue_followers', + 'manage_issue_followers', + 'link_external_issue', + 'redact_message', + ], + }, + }, + { + id: 'title', + title: 'Title', + type: 'short-input', + placeholder: 'Issue title', + required: { + field: 'operation', + value: ['create_issue'], + }, + condition: { + field: 'operation', + value: ['create_issue'], + }, + }, + { + id: 'bodyHtml', + title: 'Body HTML', + type: 'long-input', + placeholder: 'Issue body in HTML format', + required: true, + condition: { + field: 'operation', + value: ['create_issue'], + }, + }, + { + id: 'accountId', + title: 'Account ID', + type: 'short-input', + placeholder: 'Account ID', + condition: { + field: 'operation', + value: ['create_issue', 'update_issue', 'create_contact', 'update_contact'], + }, + }, + { + id: 'assigneeId', + title: 'Assignee ID', + type: 'short-input', + placeholder: 'User ID to assign to', + condition: { + field: 'operation', + value: ['create_issue', 'update_issue'], + }, + }, + { + id: 'teamId', + title: 'Team ID', + type: 'short-input', + placeholder: 'Team ID', + required: { + field: 'operation', + value: ['get_team', 'update_team'], + }, + condition: { + field: 'operation', + value: ['create_issue', 'update_issue', 'get_team', 'update_team'], + }, + }, + { + id: 'requesterId', + title: 'Requester ID', + type: 'short-input', + placeholder: 'Requester user ID', + condition: { + field: 'operation', + value: ['create_issue', 'update_issue'], + }, + }, + { + id: 'requesterEmail', + title: 'Requester Email', + type: 'short-input', + placeholder: 'Requester email address', + condition: { + field: 'operation', + value: ['create_issue'], + }, + }, + { + id: 'priority', + title: 'Priority', + type: 'short-input', + placeholder: 'Issue priority', + condition: { + field: 'operation', + value: ['create_issue', 'update_issue'], + }, + }, + { + id: 'state', + title: 'State', + type: 'short-input', + placeholder: 'Issue state', + condition: { + field: 'operation', + value: ['update_issue'], + }, + }, + { + id: 'tags', + title: 'Tags', + type: 'short-input', + placeholder: 'Comma-separated tag IDs', + condition: { + field: 'operation', + value: [ + 'create_issue', + 'update_issue', + 'create_account', + 'update_account', + 'bulk_update_accounts', + ], + }, + }, + { + id: 'customFields', + title: 'Custom Fields', + type: 'long-input', + placeholder: 'JSON object with custom fields', + condition: { + field: 'operation', + value: [ + 'create_issue', + 'update_issue', + 'create_account', + 'update_account', + 'bulk_update_accounts', + 'create_contact', + 'update_contact', + ], + }, + }, + { + id: 'attachmentUrls', + title: 'Attachment URLs', + type: 'short-input', + placeholder: 'Comma-separated attachment URLs', + condition: { + field: 'operation', + value: ['create_issue'], + }, + }, + { + id: 'customerPortalVisible', + title: 'Customer Portal Visible', + type: 'short-input', + placeholder: 'true or false', + condition: { + field: 'operation', + value: ['update_issue'], + }, + }, + { + id: 'snoozeUntil', + title: 'Snooze Until', + type: 'short-input', + placeholder: 'RFC3339 timestamp', + required: true, + condition: { + field: 'operation', + value: ['snooze_issue'], + }, + }, + { + id: 'userIds', + title: 'User IDs', + type: 'short-input', + placeholder: 'Comma-separated user IDs', + condition: { + field: 'operation', + value: ['manage_issue_followers', 'create_team', 'update_team'], + }, + }, + { + id: 'contactIds', + title: 'Contact IDs', + type: 'short-input', + placeholder: 'Comma-separated contact IDs', + condition: { + field: 'operation', + value: ['manage_issue_followers'], + }, + }, + { + id: 'followerOperation', + title: 'Follower Operation', + type: 'dropdown', + options: [ + { label: 'Add', id: 'add' }, + { label: 'Remove', id: 'remove' }, + ], + condition: { + field: 'operation', + value: ['manage_issue_followers'], + }, + }, + { + id: 'externalIssueId', + title: 'External Issue ID', + type: 'short-input', + placeholder: 'External issue identifier', + required: true, + condition: { + field: 'operation', + value: ['link_external_issue'], + }, + }, + { + id: 'source', + title: 'Source', + type: 'short-input', + placeholder: 'Source system (e.g., linear, jira)', + required: true, + condition: { + field: 'operation', + value: ['link_external_issue'], + }, + }, + // Account fields + { + id: 'name', + title: 'Name', + type: 'short-input', + placeholder: 'Name', + required: { + field: 'operation', + value: ['create_account', 'create_contact'], + }, + condition: { + field: 'operation', + value: [ + 'create_account', + 'update_account', + 'create_contact', + 'update_contact', + 'create_team', + 'update_team', + ], + }, + }, + { + id: 'accountIdField', + title: 'Account ID', + type: 'short-input', + placeholder: 'Account ID', + required: true, + condition: { + field: 'operation', + value: ['get_account', 'update_account', 'delete_account'], + }, + }, + { + id: 'accountIds', + title: 'Account IDs', + type: 'short-input', + placeholder: 'Comma-separated account IDs', + required: true, + condition: { + field: 'operation', + value: ['bulk_update_accounts'], + }, + }, + { + id: 'domains', + title: 'Domains', + type: 'short-input', + placeholder: 'Comma-separated domain names', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'primaryDomain', + title: 'Primary Domain', + type: 'short-input', + placeholder: 'Primary domain name', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'channels', + title: 'Channels', + type: 'short-input', + placeholder: 'Channels', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'externalIds', + title: 'External IDs', + type: 'short-input', + placeholder: 'External IDs', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'ownerId', + title: 'Owner ID', + type: 'short-input', + placeholder: 'Owner user ID', + condition: { + field: 'operation', + value: ['create_account', 'update_account', 'bulk_update_accounts'], + }, + }, + { + id: 'logoUrl', + title: 'Logo URL', + type: 'short-input', + placeholder: 'Account logo URL', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'subaccountIds', + title: 'Subaccount IDs', + type: 'short-input', + placeholder: 'Comma-separated subaccount IDs', + condition: { + field: 'operation', + value: ['create_account', 'update_account'], + }, + }, + { + id: 'tagsApplyMode', + title: 'Tags Apply Mode', + type: 'dropdown', + options: [ + { label: 'Append Only', id: 'append_only' }, + { label: 'Remove Only', id: 'remove_only' }, + { label: 'Replace', id: 'replace' }, + ], + condition: { + field: 'operation', + value: ['bulk_update_accounts'], + }, + }, + // Contact fields + { + id: 'contactId', + title: 'Contact ID', + type: 'short-input', + placeholder: 'Contact ID', + required: true, + condition: { + field: 'operation', + value: ['get_contact', 'update_contact', 'delete_contact'], + }, + }, + { + id: 'email', + title: 'Email', + type: 'short-input', + placeholder: 'Email address', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'accountExternalId', + title: 'Account External ID', + type: 'short-input', + placeholder: 'External account ID', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'avatarUrl', + title: 'Avatar URL', + type: 'short-input', + placeholder: 'Square PNG/JPG image URL', + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + { + id: 'portalRole', + title: 'Portal Role', + type: 'dropdown', + options: [ + { label: 'No Access', id: 'no_access' }, + { label: 'Member', id: 'member' }, + { label: 'Admin', id: 'admin' }, + ], + condition: { + field: 'operation', + value: ['create_contact', 'update_contact'], + }, + }, + // User fields + { + id: 'userId', + title: 'User ID', + type: 'short-input', + placeholder: 'User ID', + required: true, + condition: { + field: 'operation', + value: ['get_user', 'update_user'], + }, + }, + { + id: 'roleId', + title: 'Role ID', + type: 'short-input', + placeholder: 'Role ID', + condition: { + field: 'operation', + value: ['update_user'], + }, + }, + { + id: 'status', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Active', id: 'active' }, + { label: 'Away', id: 'away' }, + { label: 'Out of Office', id: 'out_of_office' }, + ], + condition: { + field: 'operation', + value: ['update_user'], + }, + }, + // Tag fields + { + id: 'tagId', + title: 'Tag ID', + type: 'short-input', + placeholder: 'Tag ID', + required: true, + condition: { + field: 'operation', + value: ['get_tag', 'update_tag', 'delete_tag'], + }, + }, + { + id: 'objectType', + title: 'Object Type', + type: 'dropdown', + options: [ + { label: 'Account', id: 'account' }, + { label: 'Issue', id: 'issue' }, + { label: 'Contact', id: 'contact' }, + ], + required: true, + condition: { + field: 'operation', + value: ['create_tag'], + }, + }, + { + id: 'value', + title: 'Value', + type: 'short-input', + placeholder: 'Tag value/name', + required: { + field: 'operation', + value: ['create_tag'], + }, + condition: { + field: 'operation', + value: ['create_tag', 'update_tag'], + }, + }, + { + id: 'hexColor', + title: 'Hex Color', + type: 'short-input', + placeholder: 'Hex color code (e.g., #3a89ce)', + condition: { + field: 'operation', + value: ['create_tag', 'update_tag'], + }, + }, + // Message fields + { + id: 'messageId', + title: 'Message ID', + type: 'short-input', + placeholder: 'Message ID', + required: true, + condition: { + field: 'operation', + value: ['redact_message'], + }, + }, + // Search and pagination fields + { + id: 'filter', + title: 'Filter', + type: 'long-input', + placeholder: 'JSON filter object', + required: { + field: 'operation', + value: ['search_accounts', 'search_contacts', 'search_users'], + }, + condition: { + field: 'operation', + value: ['search_issues', 'search_accounts', 'search_contacts', 'search_users'], + }, + }, + { + id: 'limit', + title: 'Limit', + type: 'short-input', + placeholder: 'Results per page (1-1000, default: 100)', + condition: { + field: 'operation', + value: [ + 'list_accounts', + 'list_contacts', + 'get_contact', + 'search_issues', + 'search_accounts', + 'search_contacts', + 'search_users', + ], + }, + }, + { + id: 'cursor', + title: 'Cursor', + type: 'short-input', + placeholder: 'Pagination cursor', + condition: { + field: 'operation', + value: [ + 'list_issues', + 'list_accounts', + 'list_contacts', + 'get_contact', + 'search_issues', + 'search_accounts', + 'search_contacts', + 'search_users', + ], + }, + }, + ], + tools: { + access: [ + 'pylon_list_issues', + 'pylon_create_issue', + 'pylon_get_issue', + 'pylon_update_issue', + 'pylon_delete_issue', + 'pylon_search_issues', + 'pylon_snooze_issue', + 'pylon_list_issue_followers', + 'pylon_manage_issue_followers', + 'pylon_link_external_issue', + 'pylon_list_accounts', + 'pylon_create_account', + 'pylon_get_account', + 'pylon_update_account', + 'pylon_delete_account', + 'pylon_bulk_update_accounts', + 'pylon_search_accounts', + 'pylon_list_contacts', + 'pylon_create_contact', + 'pylon_get_contact', + 'pylon_update_contact', + 'pylon_delete_contact', + 'pylon_search_contacts', + 'pylon_list_users', + 'pylon_get_user', + 'pylon_update_user', + 'pylon_search_users', + 'pylon_list_teams', + 'pylon_get_team', + 'pylon_create_team', + 'pylon_update_team', + 'pylon_list_tags', + 'pylon_get_tag', + 'pylon_create_tag', + 'pylon_update_tag', + 'pylon_delete_tag', + 'pylon_redact_message', + ], + config: { + tool: (params) => { + switch (params.operation) { + // Issue operations + case 'list_issues': + return 'pylon_list_issues' + case 'create_issue': + return 'pylon_create_issue' + case 'get_issue': + return 'pylon_get_issue' + case 'update_issue': + return 'pylon_update_issue' + case 'delete_issue': + return 'pylon_delete_issue' + case 'search_issues': + return 'pylon_search_issues' + case 'snooze_issue': + return 'pylon_snooze_issue' + case 'list_issue_followers': + return 'pylon_list_issue_followers' + case 'manage_issue_followers': + return 'pylon_manage_issue_followers' + case 'link_external_issue': + return 'pylon_link_external_issue' + // Account operations + case 'list_accounts': + return 'pylon_list_accounts' + case 'create_account': + return 'pylon_create_account' + case 'get_account': + return 'pylon_get_account' + case 'update_account': + return 'pylon_update_account' + case 'delete_account': + return 'pylon_delete_account' + case 'bulk_update_accounts': + return 'pylon_bulk_update_accounts' + case 'search_accounts': + return 'pylon_search_accounts' + // Contact operations + case 'list_contacts': + return 'pylon_list_contacts' + case 'create_contact': + return 'pylon_create_contact' + case 'get_contact': + return 'pylon_get_contact' + case 'update_contact': + return 'pylon_update_contact' + case 'delete_contact': + return 'pylon_delete_contact' + case 'search_contacts': + return 'pylon_search_contacts' + // User operations + case 'list_users': + return 'pylon_list_users' + case 'get_user': + return 'pylon_get_user' + case 'update_user': + return 'pylon_update_user' + case 'search_users': + return 'pylon_search_users' + // Team operations + case 'list_teams': + return 'pylon_list_teams' + case 'get_team': + return 'pylon_get_team' + case 'create_team': + return 'pylon_create_team' + case 'update_team': + return 'pylon_update_team' + // Tag operations + case 'list_tags': + return 'pylon_list_tags' + case 'get_tag': + return 'pylon_get_tag' + case 'create_tag': + return 'pylon_create_tag' + case 'update_tag': + return 'pylon_update_tag' + case 'delete_tag': + return 'pylon_delete_tag' + // Message operations + case 'redact_message': + return 'pylon_redact_message' + default: + throw new Error(`Unknown operation: ${params.operation}`) + } + }, + params: (params) => { + const { apiToken, operation, ...rest } = params + const cleanParams: Record = { apiToken } + + // Handle parameter mapping + if (params.accountIdField) { + cleanParams.accountId = params.accountIdField + } + if (params.followerOperation) { + cleanParams.operation = params.followerOperation + } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + // Skip mapped fields + if (key === 'accountIdField' || key === 'followerOperation') { + return + } + cleanParams[key] = value + } + }) + + return cleanParams + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiToken: { type: 'string', description: 'Pylon API token' }, + }, + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { type: 'json', description: 'Operation result data' }, + }, +} diff --git a/apps/sim/blocks/blocks/zendesk.ts b/apps/sim/blocks/blocks/zendesk.ts new file mode 100644 index 0000000000..d0f956cc75 --- /dev/null +++ b/apps/sim/blocks/blocks/zendesk.ts @@ -0,0 +1,510 @@ +import { ZendeskIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' + +export const ZendeskBlock: BlockConfig = { + type: 'zendesk', + name: 'Zendesk', + description: 'Manage support tickets, users, and organizations in Zendesk', + longDescription: + 'Integrate Zendesk into the workflow. Can get tickets, get ticket, create ticket, create tickets bulk, update ticket, update tickets bulk, delete ticket, merge tickets, get users, get user, get current user, search users, create user, create users bulk, update user, update users bulk, delete user, get organizations, get organization, autocomplete organizations, create organization, create organizations bulk, update organization, delete organization, search, search count.', + docsLink: 'https://docs.sim.ai/tools/zendesk', + authMode: AuthMode.ApiKey, + category: 'tools', + bgColor: '#E0E0E0', + icon: ZendeskIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Get Tickets', id: 'get_tickets' }, + { label: 'Get Ticket', id: 'get_ticket' }, + { label: 'Create Ticket', id: 'create_ticket' }, + { label: 'Create Tickets Bulk', id: 'create_tickets_bulk' }, + { label: 'Update Ticket', id: 'update_ticket' }, + { label: 'Update Tickets Bulk', id: 'update_tickets_bulk' }, + { label: 'Delete Ticket', id: 'delete_ticket' }, + { label: 'Merge Tickets', id: 'merge_tickets' }, + { label: 'Get Users', id: 'get_users' }, + { label: 'Get User', id: 'get_user' }, + { label: 'Get Current User', id: 'get_current_user' }, + { label: 'Search Users', id: 'search_users' }, + { label: 'Create User', id: 'create_user' }, + { label: 'Create Users Bulk', id: 'create_users_bulk' }, + { label: 'Update User', id: 'update_user' }, + { label: 'Update Users Bulk', id: 'update_users_bulk' }, + { label: 'Delete User', id: 'delete_user' }, + { label: 'Get Organizations', id: 'get_organizations' }, + { label: 'Get Organization', id: 'get_organization' }, + { label: 'Autocomplete Organizations', id: 'autocomplete_organizations' }, + { label: 'Create Organization', id: 'create_organization' }, + { label: 'Create Organizations Bulk', id: 'create_organizations_bulk' }, + { label: 'Update Organization', id: 'update_organization' }, + { label: 'Delete Organization', id: 'delete_organization' }, + { label: 'Search', id: 'search' }, + { label: 'Search Count', id: 'search_count' }, + ], + value: () => 'get_tickets', + }, + { + id: 'email', + title: 'Email', + type: 'short-input', + placeholder: 'Your Zendesk email address', + required: true, + description: 'The email address associated with your Zendesk account', + }, + { + id: 'apiToken', + title: 'API Token', + type: 'short-input', + password: true, + placeholder: 'Enter your Zendesk API token', + required: true, + }, + { + id: 'subdomain', + title: 'Subdomain', + type: 'short-input', + placeholder: 'Your Zendesk subdomain (e.g., "mycompany")', + required: true, + description: 'The subdomain from your Zendesk URL (mycompany.zendesk.com)', + }, + // Ticket fields + { + id: 'ticketId', + title: 'Ticket ID', + type: 'short-input', + placeholder: 'Ticket ID', + required: true, + condition: { + field: 'operation', + value: ['get_ticket', 'update_ticket', 'delete_ticket'], + }, + }, + { + id: 'subject', + title: 'Subject', + type: 'short-input', + placeholder: 'Ticket subject', + condition: { + field: 'operation', + value: ['create_ticket', 'update_ticket'], + }, + }, + { + id: 'description', + title: 'Description', + type: 'long-input', + placeholder: 'Ticket description', + required: { + field: 'operation', + value: ['create_ticket'], + }, + condition: { + field: 'operation', + value: ['create_ticket', 'update_ticket'], + }, + }, + { + id: 'status', + title: 'Status', + type: 'short-input', + placeholder: 'Status (new, open, pending, hold, solved, closed)', + condition: { + field: 'operation', + value: ['get_tickets', 'create_ticket', 'update_ticket'], + }, + }, + { + id: 'priority', + title: 'Priority', + type: 'short-input', + placeholder: 'Priority (low, normal, high, urgent)', + condition: { + field: 'operation', + value: ['get_tickets', 'create_ticket', 'update_ticket'], + }, + }, + { + id: 'type', + title: 'Type', + type: 'short-input', + placeholder: 'Type (problem, incident, question, task)', + condition: { + field: 'operation', + value: ['get_tickets', 'create_ticket', 'update_ticket'], + }, + }, + { + id: 'tags', + title: 'Tags', + type: 'short-input', + placeholder: 'Comma-separated tags', + condition: { + field: 'operation', + value: ['create_ticket', 'update_ticket'], + }, + }, + { + id: 'assigneeId', + title: 'Assignee ID', + type: 'short-input', + placeholder: 'User ID to assign ticket to', + condition: { + field: 'operation', + value: ['get_tickets', 'create_ticket', 'update_ticket'], + }, + }, + { + id: 'groupId', + title: 'Group ID', + type: 'short-input', + placeholder: 'Group ID', + condition: { + field: 'operation', + value: ['create_ticket', 'update_ticket'], + }, + }, + { + id: 'customFields', + title: 'Custom Fields', + type: 'long-input', + placeholder: 'JSON object with custom fields', + condition: { + field: 'operation', + value: ['create_ticket', 'update_ticket'], + }, + }, + { + id: 'tickets', + title: 'Tickets', + type: 'long-input', + placeholder: 'JSON array of ticket objects', + required: true, + condition: { + field: 'operation', + value: ['create_tickets_bulk', 'update_tickets_bulk'], + }, + }, + { + id: 'targetTicketId', + title: 'Target Ticket ID', + type: 'short-input', + placeholder: 'Ticket ID to merge into', + required: true, + condition: { + field: 'operation', + value: ['merge_tickets'], + }, + }, + { + id: 'sourceTicketIds', + title: 'Source Ticket IDs', + type: 'short-input', + placeholder: 'Comma-separated ticket IDs to merge', + required: true, + condition: { + field: 'operation', + value: ['merge_tickets'], + }, + }, + // User fields + { + id: 'userId', + title: 'User ID', + type: 'short-input', + placeholder: 'User ID', + required: true, + condition: { + field: 'operation', + value: ['get_user', 'update_user', 'delete_user'], + }, + }, + { + id: 'name', + title: 'Name', + type: 'short-input', + placeholder: 'User name', + required: { + field: 'operation', + value: ['create_user'], + }, + condition: { + field: 'operation', + value: ['create_user', 'update_user'], + }, + }, + { + id: 'userEmail', + title: 'Email', + type: 'short-input', + placeholder: 'User email', + condition: { + field: 'operation', + value: ['create_user', 'update_user'], + }, + }, + { + id: 'users', + title: 'Users', + type: 'long-input', + placeholder: 'JSON array of user objects', + required: true, + condition: { + field: 'operation', + value: ['create_users_bulk', 'update_users_bulk'], + }, + }, + // Organization fields + { + id: 'organizationId', + title: 'Organization ID', + type: 'short-input', + placeholder: 'Organization ID', + required: { + field: 'operation', + value: ['get_organization', 'delete_organization'], + }, + condition: { + field: 'operation', + value: [ + 'get_tickets', + 'create_ticket', + 'get_organization', + 'delete_organization', + 'update_organization', + 'create_user', + 'update_user', + ], + }, + }, + { + id: 'organizationName', + title: 'Organization Name', + type: 'short-input', + placeholder: 'Organization name', + required: { + field: 'operation', + value: ['autocomplete_organizations'], + }, + condition: { + field: 'operation', + value: ['autocomplete_organizations', 'create_organization', 'update_organization'], + }, + }, + { + id: 'organizations', + title: 'Organizations', + type: 'long-input', + placeholder: 'JSON array of organization objects', + required: true, + condition: { + field: 'operation', + value: ['create_organizations_bulk'], + }, + }, + // Search fields + { + id: 'query', + title: 'Query', + type: 'short-input', + placeholder: 'Search query', + required: { + field: 'operation', + value: ['search', 'search_count'], + }, + condition: { + field: 'operation', + value: ['search_users', 'search', 'search_count'], + }, + }, + { + id: 'sortBy', + title: 'Sort By', + type: 'dropdown', + options: [ + { label: 'Relevance', id: 'relevance' }, + { label: 'Created At', id: 'created_at' }, + { label: 'Updated At', id: 'updated_at' }, + { label: 'Priority', id: 'priority' }, + { label: 'Status', id: 'status' }, + { label: 'Ticket Type', id: 'ticket_type' }, + ], + condition: { + field: 'operation', + value: ['search'], + }, + }, + { + id: 'sortOrder', + title: 'Sort Order', + type: 'dropdown', + options: [ + { label: 'Ascending', id: 'asc' }, + { label: 'Descending', id: 'desc' }, + ], + condition: { + field: 'operation', + value: ['search'], + }, + }, + // Pagination fields + { + id: 'perPage', + title: 'Per Page', + type: 'short-input', + placeholder: 'Results per page (default: 100, max: 100)', + condition: { + field: 'operation', + value: [ + 'get_tickets', + 'get_users', + 'get_organizations', + 'search_users', + 'autocomplete_organizations', + 'search', + ], + }, + }, + { + id: 'page', + title: 'Page', + type: 'short-input', + placeholder: 'Page number', + condition: { + field: 'operation', + value: [ + 'get_tickets', + 'get_users', + 'get_organizations', + 'search_users', + 'autocomplete_organizations', + 'search', + ], + }, + }, + ], + tools: { + access: [ + 'zendesk_get_tickets', + 'zendesk_get_ticket', + 'zendesk_create_ticket', + 'zendesk_create_tickets_bulk', + 'zendesk_update_ticket', + 'zendesk_update_tickets_bulk', + 'zendesk_delete_ticket', + 'zendesk_merge_tickets', + 'zendesk_get_users', + 'zendesk_get_user', + 'zendesk_get_current_user', + 'zendesk_search_users', + 'zendesk_create_user', + 'zendesk_create_users_bulk', + 'zendesk_update_user', + 'zendesk_update_users_bulk', + 'zendesk_delete_user', + 'zendesk_get_organizations', + 'zendesk_get_organization', + 'zendesk_autocomplete_organizations', + 'zendesk_create_organization', + 'zendesk_create_organizations_bulk', + 'zendesk_update_organization', + 'zendesk_delete_organization', + 'zendesk_search', + 'zendesk_search_count', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'get_tickets': + return 'zendesk_get_tickets' + case 'get_ticket': + return 'zendesk_get_ticket' + case 'create_ticket': + return 'zendesk_create_ticket' + case 'create_tickets_bulk': + return 'zendesk_create_tickets_bulk' + case 'update_ticket': + return 'zendesk_update_ticket' + case 'update_tickets_bulk': + return 'zendesk_update_tickets_bulk' + case 'delete_ticket': + return 'zendesk_delete_ticket' + case 'merge_tickets': + return 'zendesk_merge_tickets' + case 'get_users': + return 'zendesk_get_users' + case 'get_user': + return 'zendesk_get_user' + case 'get_current_user': + return 'zendesk_get_current_user' + case 'search_users': + return 'zendesk_search_users' + case 'create_user': + return 'zendesk_create_user' + case 'create_users_bulk': + return 'zendesk_create_users_bulk' + case 'update_user': + return 'zendesk_update_user' + case 'update_users_bulk': + return 'zendesk_update_users_bulk' + case 'delete_user': + return 'zendesk_delete_user' + case 'get_organizations': + return 'zendesk_get_organizations' + case 'get_organization': + return 'zendesk_get_organization' + case 'autocomplete_organizations': + return 'zendesk_autocomplete_organizations' + case 'create_organization': + return 'zendesk_create_organization' + case 'create_organizations_bulk': + return 'zendesk_create_organizations_bulk' + case 'update_organization': + return 'zendesk_update_organization' + case 'delete_organization': + return 'zendesk_delete_organization' + case 'search': + return 'zendesk_search' + case 'search_count': + return 'zendesk_search_count' + default: + throw new Error(`Unknown operation: ${params.operation}`) + } + }, + params: (params) => { + const { apiToken, operation, ...rest } = params + const cleanParams: Record = { apiToken } + + // Special mapping for autocomplete_organizations + if (operation === 'autocomplete_organizations' && params.organizationName) { + cleanParams.name = params.organizationName + } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + // Skip organizationName for autocomplete_organizations as it's mapped to 'name' + if (operation === 'autocomplete_organizations' && key === 'organizationName') { + return + } + cleanParams[key] = value + } + }) + return cleanParams + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + email: { type: 'string', description: 'Zendesk email address' }, + apiToken: { type: 'string', description: 'Zendesk API token' }, + subdomain: { type: 'string', description: 'Zendesk subdomain' }, + }, + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { type: 'json', description: 'Operation result data' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 189b09bc85..db663ba3ec 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -36,11 +36,13 @@ import { HunterBlock } from '@/blocks/blocks/hunter' import { ImageGeneratorBlock } from '@/blocks/blocks/image_generator' import { IncidentioBlock } from '@/blocks/blocks/incidentio' import { InputTriggerBlock } from '@/blocks/blocks/input_trigger' +import { IntercomBlock } from '@/blocks/blocks/intercom' import { JinaBlock } from '@/blocks/blocks/jina' import { JiraBlock } from '@/blocks/blocks/jira' import { KnowledgeBlock } from '@/blocks/blocks/knowledge' import { LinearBlock } from '@/blocks/blocks/linear' import { LinkupBlock } from '@/blocks/blocks/linkup' +import { MailchimpBlock } from '@/blocks/blocks/mailchimp' import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger' import { McpBlock } from '@/blocks/blocks/mcp' import { Mem0Block } from '@/blocks/blocks/mem0' @@ -63,6 +65,7 @@ import { PineconeBlock } from '@/blocks/blocks/pinecone' import { PipedriveBlock } from '@/blocks/blocks/pipedrive' import { PostgreSQLBlock } from '@/blocks/blocks/postgresql' import { PostHogBlock } from '@/blocks/blocks/posthog' +import { PylonBlock } from '@/blocks/blocks/pylon' import { QdrantBlock } from '@/blocks/blocks/qdrant' import { RedditBlock } from '@/blocks/blocks/reddit' import { ResendBlock } from '@/blocks/blocks/resend' @@ -104,6 +107,7 @@ import { WorkflowBlock } from '@/blocks/blocks/workflow' import { WorkflowInputBlock } from '@/blocks/blocks/workflow_input' import { XBlock } from '@/blocks/blocks/x' import { YouTubeBlock } from '@/blocks/blocks/youtube' +import { ZendeskBlock } from '@/blocks/blocks/zendesk' import { ZepBlock } from '@/blocks/blocks/zep' import type { BlockConfig } from '@/blocks/types' @@ -149,6 +153,7 @@ export const registry: Record = { knowledge: KnowledgeBlock, linear: LinearBlock, linkup: LinkupBlock, + mailchimp: MailchimpBlock, mcp: McpBlock, mem0: Mem0Block, zep: ZepBlock, @@ -170,6 +175,7 @@ export const registry: Record = { pinecone: PineconeBlock, pipedrive: PipedriveBlock, postgresql: PostgreSQLBlock, + pylon: PylonBlock, qdrant: QdrantBlock, resend: ResendBlock, memory: MemoryBlock, @@ -180,6 +186,8 @@ export const registry: Record = { s3: S3Block, salesforce: SalesforceBlock, sentry: SentryBlock, + intercom: IntercomBlock, + zendesk: ZendeskBlock, serper: SerperBlock, sharepoint: SharepointBlock, stagehand: StagehandBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index a99241ff32..9c9f00984a 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -4243,3 +4243,104 @@ export function IncidentioIcon(props: SVGProps) { ) } + +export function IntercomIcon(props: SVGProps) { + return ( + + + + + + ) +} + +export function MailchimpIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + ) +} + +export function ZendeskIcon(props: SVGProps) { + return ( + + + + + + ) +} + +export function PylonIcon(props: SVGProps) { + return ( + + + + + + + + + + + ) +} diff --git a/apps/sim/tools/intercom/create_company.ts b/apps/sim/tools/intercom/create_company.ts new file mode 100644 index 0000000000..0da0df49fa --- /dev/null +++ b/apps/sim/tools/intercom/create_company.ts @@ -0,0 +1,163 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomCreateCompany') + +export interface IntercomCreateCompanyParams { + accessToken: string + company_id: string + name?: string + website?: string + plan?: string + size?: number + industry?: string + monthly_spend?: number + custom_attributes?: string +} + +export interface IntercomCreateCompanyResponse { + success: boolean + output: { + company: any + metadata: { + operation: 'create_company' + companyId: string + } + success: boolean + } +} + +export const intercomCreateCompanyTool: ToolConfig< + IntercomCreateCompanyParams, + IntercomCreateCompanyResponse +> = { + id: 'intercom_create_company', + name: 'Create Company in Intercom', + description: 'Create or update a company in Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + company_id: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your unique identifier for the company', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the company', + }, + website: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The company website', + }, + plan: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The company plan name', + }, + size: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'The number of employees in the company', + }, + industry: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The industry the company operates in', + }, + monthly_spend: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'How much revenue the company generates for your business', + }, + custom_attributes: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom attributes as JSON object', + }, + }, + + request: { + url: () => buildIntercomUrl('/companies'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const company: any = { + company_id: params.company_id, + } + + if (params.name) company.name = params.name + if (params.website) company.website = params.website + if (params.plan) company.plan = params.plan + if (params.size) company.size = params.size + if (params.industry) company.industry = params.industry + if (params.monthly_spend) company.monthly_spend = params.monthly_spend + + if (params.custom_attributes) { + try { + company.custom_attributes = JSON.parse(params.custom_attributes) + } catch (error) { + logger.warn('Failed to parse custom attributes', { error }) + } + } + + return company + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_company') + } + + const data = await response.json() + + return { + success: true, + output: { + company: data, + metadata: { + operation: 'create_company' as const, + companyId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created or updated company data', + properties: { + company: { type: 'object', description: 'Company object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/create_contact.ts b/apps/sim/tools/intercom/create_contact.ts new file mode 100644 index 0000000000..81359fdb04 --- /dev/null +++ b/apps/sim/tools/intercom/create_contact.ts @@ -0,0 +1,178 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomCreateContact') + +export interface IntercomCreateContactParams { + accessToken: string + email?: string + external_id?: string + phone?: string + name?: string + avatar?: string + signed_up_at?: number + last_seen_at?: number + owner_id?: string + unsubscribed_from_emails?: boolean + custom_attributes?: string +} + +export interface IntercomCreateContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'create_contact' + contactId: string + } + success: boolean + } +} + +export const intercomCreateContactTool: ToolConfig< + IntercomCreateContactParams, + IntercomCreateContactResponse +> = { + id: 'intercom_create_contact', + name: 'Create Contact in Intercom', + description: 'Create a new contact in Intercom with email, external_id, or role', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + email: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's email address", + }, + external_id: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'A unique identifier for the contact provided by the client', + }, + phone: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's phone number", + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's name", + }, + avatar: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'An avatar image URL for the contact', + }, + signed_up_at: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'The time the user signed up as a Unix timestamp', + }, + last_seen_at: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'The time the user was last seen as a Unix timestamp', + }, + owner_id: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The id of an admin that has been assigned account ownership of the contact', + }, + unsubscribed_from_emails: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: 'Whether the contact is unsubscribed from emails', + }, + custom_attributes: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom attributes as JSON object (e.g., {"attribute_name": "value"})', + }, + }, + + request: { + url: () => buildIntercomUrl('/contacts'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const contact: any = {} + + if (params.email) contact.email = params.email + if (params.external_id) contact.external_id = params.external_id + if (params.phone) contact.phone = params.phone + if (params.name) contact.name = params.name + if (params.avatar) contact.avatar = params.avatar + if (params.signed_up_at) contact.signed_up_at = params.signed_up_at + if (params.last_seen_at) contact.last_seen_at = params.last_seen_at + if (params.owner_id) contact.owner_id = params.owner_id + if (params.unsubscribed_from_emails !== undefined) + contact.unsubscribed_from_emails = params.unsubscribed_from_emails + + if (params.custom_attributes) { + try { + contact.custom_attributes = JSON.parse(params.custom_attributes) + } catch (error) { + logger.warn('Failed to parse custom attributes', { error }) + } + } + + return contact + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data, + metadata: { + operation: 'create_contact' as const, + contactId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created contact data', + properties: { + contact: { type: 'object', description: 'Created contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/create_message.ts b/apps/sim/tools/intercom/create_message.ts new file mode 100644 index 0000000000..bbbd486ccb --- /dev/null +++ b/apps/sim/tools/intercom/create_message.ts @@ -0,0 +1,153 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomCreateMessage') + +export interface IntercomCreateMessageParams { + accessToken: string + message_type: string + subject?: string + body: string + from_type: string + from_id: string + to_type: string + to_id: string +} + +export interface IntercomCreateMessageResponse { + success: boolean + output: { + message: any + metadata: { + operation: 'create_message' + messageId: string + } + success: boolean + } +} + +export const intercomCreateMessageTool: ToolConfig< + IntercomCreateMessageParams, + IntercomCreateMessageResponse +> = { + id: 'intercom_create_message', + name: 'Create Message in Intercom', + description: 'Create and send a new admin-initiated message in Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + message_type: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Message type: "inapp" or "email"', + }, + subject: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The subject of the message (for email type)', + }, + body: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The body of the message', + }, + from_type: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Sender type: "admin"', + }, + from_id: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the admin sending the message', + }, + to_type: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Recipient type: "contact"', + }, + to_id: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the contact receiving the message', + }, + }, + + request: { + url: () => buildIntercomUrl('/messages'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const message: any = { + message_type: params.message_type, + body: params.body, + from: { + type: params.from_type, + id: params.from_id, + }, + to: { + type: params.to_type, + id: params.to_id, + }, + } + + if (params.subject && params.message_type === 'email') { + message.subject = params.subject + } + + return message + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_message') + } + + const data = await response.json() + + return { + success: true, + output: { + message: data, + metadata: { + operation: 'create_message' as const, + messageId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created message data', + properties: { + message: { type: 'object', description: 'Created message object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/create_ticket.ts b/apps/sim/tools/intercom/create_ticket.ts new file mode 100644 index 0000000000..8ec1a665ea --- /dev/null +++ b/apps/sim/tools/intercom/create_ticket.ts @@ -0,0 +1,127 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomCreateTicket') + +export interface IntercomCreateTicketParams { + accessToken: string + ticket_type_id: string + contacts: string + ticket_attributes: string +} + +export interface IntercomCreateTicketResponse { + success: boolean + output: { + ticket: any + metadata: { + operation: 'create_ticket' + ticketId: string + } + success: boolean + } +} + +export const intercomCreateTicketTool: ToolConfig< + IntercomCreateTicketParams, + IntercomCreateTicketResponse +> = { + id: 'intercom_create_ticket', + name: 'Create Ticket in Intercom', + description: 'Create a new ticket in Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + ticket_type_id: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the ticket type', + }, + contacts: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of contact identifiers (e.g., [{"id": "contact_id"}])', + }, + ticket_attributes: { + type: 'string', + required: true, + visibility: 'user-only', + description: + 'JSON object with ticket attributes including _default_title_ and _default_description_', + }, + }, + + request: { + url: () => buildIntercomUrl('/tickets'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const ticket: any = { + ticket_type_id: params.ticket_type_id, + } + + try { + ticket.contacts = JSON.parse(params.contacts) + } catch (error) { + logger.warn('Failed to parse contacts, using as single contact ID', { error }) + ticket.contacts = [{ id: params.contacts }] + } + + try { + ticket.ticket_attributes = JSON.parse(params.ticket_attributes) + } catch (error) { + logger.error('Failed to parse ticket attributes', { error }) + throw new Error('ticket_attributes must be a valid JSON object') + } + + return ticket + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: data, + metadata: { + operation: 'create_ticket' as const, + ticketId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created ticket data', + properties: { + ticket: { type: 'object', description: 'Created ticket object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/delete_contact.ts b/apps/sim/tools/intercom/delete_contact.ts new file mode 100644 index 0000000000..bb74789257 --- /dev/null +++ b/apps/sim/tools/intercom/delete_contact.ts @@ -0,0 +1,92 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomDeleteContact') + +export interface IntercomDeleteContactParams { + accessToken: string + contactId: string +} + +export interface IntercomDeleteContactResponse { + success: boolean + output: { + id: string + deleted: boolean + metadata: { + operation: 'delete_contact' + } + success: boolean + } +} + +export const intercomDeleteContactTool: ToolConfig< + IntercomDeleteContactParams, + IntercomDeleteContactResponse +> = { + id: 'intercom_delete_contact', + name: 'Delete Contact from Intercom', + description: 'Delete a contact from Intercom by ID', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to delete', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/contacts/${params.contactId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'delete_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + deleted: true, + metadata: { + operation: 'delete_contact' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion result', + properties: { + id: { type: 'string', description: 'ID of deleted contact' }, + deleted: { type: 'boolean', description: 'Deletion status' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/get_company.ts b/apps/sim/tools/intercom/get_company.ts new file mode 100644 index 0000000000..1cce9c4354 --- /dev/null +++ b/apps/sim/tools/intercom/get_company.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomGetCompany') + +export interface IntercomGetCompanyParams { + accessToken: string + companyId: string +} + +export interface IntercomGetCompanyResponse { + success: boolean + output: { + company: any + metadata: { + operation: 'get_company' + } + success: boolean + } +} + +export const intercomGetCompanyTool: ToolConfig< + IntercomGetCompanyParams, + IntercomGetCompanyResponse +> = { + id: 'intercom_get_company', + name: 'Get Company from Intercom', + description: 'Retrieve a single company by ID from Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + companyId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Company ID to retrieve', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/companies/${params.companyId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'get_company') + } + + const data = await response.json() + + return { + success: true, + output: { + company: data, + metadata: { + operation: 'get_company' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Company data', + properties: { + company: { type: 'object', description: 'Company object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/get_contact.ts b/apps/sim/tools/intercom/get_contact.ts new file mode 100644 index 0000000000..19fd3f336d --- /dev/null +++ b/apps/sim/tools/intercom/get_contact.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomGetContact') + +export interface IntercomGetContactParams { + accessToken: string + contactId: string +} + +export interface IntercomGetContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'get_contact' + } + success: boolean + } +} + +export const intercomGetContactTool: ToolConfig< + IntercomGetContactParams, + IntercomGetContactResponse +> = { + id: 'intercom_get_contact', + name: 'Get Single Contact from Intercom', + description: 'Get a single contact by ID from Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to retrieve', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/contacts/${params.contactId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'get_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data, + metadata: { + operation: 'get_contact' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Contact data', + properties: { + contact: { type: 'object', description: 'Contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/get_conversation.ts b/apps/sim/tools/intercom/get_conversation.ts new file mode 100644 index 0000000000..97289bdca8 --- /dev/null +++ b/apps/sim/tools/intercom/get_conversation.ts @@ -0,0 +1,103 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomGetConversation') + +export interface IntercomGetConversationParams { + accessToken: string + conversationId: string + display_as?: string +} + +export interface IntercomGetConversationResponse { + success: boolean + output: { + conversation: any + metadata: { + operation: 'get_conversation' + } + success: boolean + } +} + +export const intercomGetConversationTool: ToolConfig< + IntercomGetConversationParams, + IntercomGetConversationResponse +> = { + id: 'intercom_get_conversation', + name: 'Get Conversation from Intercom', + description: 'Retrieve a single conversation by ID from Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Conversation ID to retrieve', + }, + display_as: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Set to "plaintext" to retrieve messages in plain text', + }, + }, + + request: { + url: (params) => { + const url = buildIntercomUrl(`/conversations/${params.conversationId}`) + if (params.display_as) { + return `${url}?display_as=${params.display_as}` + } + return url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'get_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: data, + metadata: { + operation: 'get_conversation' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Conversation data', + properties: { + conversation: { type: 'object', description: 'Conversation object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/get_ticket.ts b/apps/sim/tools/intercom/get_ticket.ts new file mode 100644 index 0000000000..0c5c66d925 --- /dev/null +++ b/apps/sim/tools/intercom/get_ticket.ts @@ -0,0 +1,88 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomGetTicket') + +export interface IntercomGetTicketParams { + accessToken: string + ticketId: string +} + +export interface IntercomGetTicketResponse { + success: boolean + output: { + ticket: any + metadata: { + operation: 'get_ticket' + } + success: boolean + } +} + +export const intercomGetTicketTool: ToolConfig = + { + id: 'intercom_get_ticket', + name: 'Get Ticket from Intercom', + description: 'Retrieve a single ticket by ID from Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + ticketId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Ticket ID to retrieve', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/tickets/${params.ticketId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'get_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: data, + metadata: { + operation: 'get_ticket' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Ticket data', + properties: { + ticket: { type: 'object', description: 'Ticket object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/intercom/index.ts b/apps/sim/tools/intercom/index.ts new file mode 100644 index 0000000000..dab0671ba9 --- /dev/null +++ b/apps/sim/tools/intercom/index.ts @@ -0,0 +1,22 @@ +// Contact tools + +// Company tools +export { intercomCreateCompanyTool } from './create_company' +export { intercomCreateContactTool } from './create_contact' +// Message tools +export { intercomCreateMessageTool } from './create_message' +// Ticket tools +export { intercomCreateTicketTool } from './create_ticket' +export { intercomDeleteContactTool } from './delete_contact' +export { intercomGetCompanyTool } from './get_company' +export { intercomGetContactTool } from './get_contact' +// Conversation tools +export { intercomGetConversationTool } from './get_conversation' +export { intercomGetTicketTool } from './get_ticket' +export { intercomListCompaniesTool } from './list_companies' +export { intercomListContactsTool } from './list_contacts' +export { intercomListConversationsTool } from './list_conversations' +export { intercomReplyConversationTool } from './reply_conversation' +export { intercomSearchContactsTool } from './search_contacts' +export { intercomSearchConversationsTool } from './search_conversations' +export { intercomUpdateContactTool } from './update_contact' diff --git a/apps/sim/tools/intercom/list_companies.ts b/apps/sim/tools/intercom/list_companies.ts new file mode 100644 index 0000000000..8d5a3f32cc --- /dev/null +++ b/apps/sim/tools/intercom/list_companies.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomListCompanies') + +export interface IntercomListCompaniesParams { + accessToken: string + per_page?: number + page?: number +} + +export interface IntercomListCompaniesResponse { + success: boolean + output: { + companies: any[] + pages?: any + metadata: { + operation: 'list_companies' + total_count?: number + } + success: boolean + } +} + +export const intercomListCompaniesTool: ToolConfig< + IntercomListCompaniesParams, + IntercomListCompaniesResponse +> = { + id: 'intercom_list_companies', + name: 'List Companies from Intercom', + description: 'List all companies from Intercom with pagination support', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results per page', + }, + page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const url = buildIntercomUrl('/companies/list') + const queryParams = new URLSearchParams() + + if (params.per_page) queryParams.append('per_page', params.per_page.toString()) + if (params.page) queryParams.append('page', params.page.toString()) + + const queryString = queryParams.toString() + return queryString ? `${url}?${queryString}` : url + }, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'list_companies') + } + + const data = await response.json() + + return { + success: true, + output: { + companies: data.data || data.companies || [], + pages: data.pages, + metadata: { + operation: 'list_companies' as const, + total_count: data.total_count, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of companies', + properties: { + companies: { type: 'array', description: 'Array of company objects' }, + pages: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/list_contacts.ts b/apps/sim/tools/intercom/list_contacts.ts new file mode 100644 index 0000000000..7f5dd01820 --- /dev/null +++ b/apps/sim/tools/intercom/list_contacts.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomListContacts') + +export interface IntercomListContactsParams { + accessToken: string + per_page?: number + starting_after?: string +} + +export interface IntercomListContactsResponse { + success: boolean + output: { + contacts: any[] + pages?: any + metadata: { + operation: 'list_contacts' + total_count?: number + } + success: boolean + } +} + +export const intercomListContactsTool: ToolConfig< + IntercomListContactsParams, + IntercomListContactsResponse +> = { + id: 'intercom_list_contacts', + name: 'List Contacts from Intercom', + description: 'List all contacts from Intercom with pagination support', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results per page (max: 150)', + }, + starting_after: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Cursor for pagination - ID to start after', + }, + }, + + request: { + url: (params) => { + const url = buildIntercomUrl('/contacts') + const queryParams = new URLSearchParams() + + if (params.per_page) queryParams.append('per_page', params.per_page.toString()) + if (params.starting_after) queryParams.append('starting_after', params.starting_after) + + const queryString = queryParams.toString() + return queryString ? `${url}?${queryString}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'list_contacts') + } + + const data = await response.json() + + return { + success: true, + output: { + contacts: data.data || [], + pages: data.pages, + metadata: { + operation: 'list_contacts' as const, + total_count: data.total_count, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of contacts', + properties: { + contacts: { type: 'array', description: 'Array of contact objects' }, + pages: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/list_conversations.ts b/apps/sim/tools/intercom/list_conversations.ts new file mode 100644 index 0000000000..ebb4f6b7f7 --- /dev/null +++ b/apps/sim/tools/intercom/list_conversations.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomListConversations') + +export interface IntercomListConversationsParams { + accessToken: string + per_page?: number + starting_after?: string +} + +export interface IntercomListConversationsResponse { + success: boolean + output: { + conversations: any[] + pages?: any + metadata: { + operation: 'list_conversations' + total_count?: number + } + success: boolean + } +} + +export const intercomListConversationsTool: ToolConfig< + IntercomListConversationsParams, + IntercomListConversationsResponse +> = { + id: 'intercom_list_conversations', + name: 'List Conversations from Intercom', + description: 'List all conversations from Intercom with pagination support', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results per page (max: 150)', + }, + starting_after: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Cursor for pagination', + }, + }, + + request: { + url: (params) => { + const url = buildIntercomUrl('/conversations') + const queryParams = new URLSearchParams() + + if (params.per_page) queryParams.append('per_page', params.per_page.toString()) + if (params.starting_after) queryParams.append('starting_after', params.starting_after) + + const queryString = queryParams.toString() + return queryString ? `${url}?${queryString}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'list_conversations') + } + + const data = await response.json() + + return { + success: true, + output: { + conversations: data.conversations || [], + pages: data.pages, + metadata: { + operation: 'list_conversations' as const, + total_count: data.total_count, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of conversations', + properties: { + conversations: { type: 'array', description: 'Array of conversation objects' }, + pages: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/reply_conversation.ts b/apps/sim/tools/intercom/reply_conversation.ts new file mode 100644 index 0000000000..1a894b0eac --- /dev/null +++ b/apps/sim/tools/intercom/reply_conversation.ts @@ -0,0 +1,137 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomReplyConversation') + +export interface IntercomReplyConversationParams { + accessToken: string + conversationId: string + message_type: string + body: string + admin_id?: string + attachment_urls?: string +} + +export interface IntercomReplyConversationResponse { + success: boolean + output: { + conversation: any + metadata: { + operation: 'reply_conversation' + conversationId: string + } + success: boolean + } +} + +export const intercomReplyConversationTool: ToolConfig< + IntercomReplyConversationParams, + IntercomReplyConversationResponse +> = { + id: 'intercom_reply_conversation', + name: 'Reply to Conversation in Intercom', + description: 'Reply to a conversation as an admin in Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Conversation ID to reply to', + }, + message_type: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Message type: "comment" or "note"', + }, + body: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The text body of the reply', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the admin authoring the reply', + }, + attachment_urls: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated list of image URLs (max 10)', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/conversations/${params.conversationId}/reply`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const reply: any = { + message_type: params.message_type, + type: 'admin', + body: params.body, + } + + if (params.admin_id) reply.admin_id = params.admin_id + + if (params.attachment_urls) { + reply.attachment_urls = params.attachment_urls + .split(',') + .map((url) => url.trim()) + .slice(0, 10) + } + + return reply + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'reply_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: data, + metadata: { + operation: 'reply_conversation' as const, + conversationId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated conversation with reply', + properties: { + conversation: { type: 'object', description: 'Updated conversation object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/search_contacts.ts b/apps/sim/tools/intercom/search_contacts.ts new file mode 100644 index 0000000000..4dab34158b --- /dev/null +++ b/apps/sim/tools/intercom/search_contacts.ts @@ -0,0 +1,131 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomSearchContacts') + +export interface IntercomSearchContactsParams { + accessToken: string + query: string + per_page?: number + starting_after?: string +} + +export interface IntercomSearchContactsResponse { + success: boolean + output: { + contacts: any[] + pages?: any + metadata: { + operation: 'search_contacts' + total_count?: number + } + success: boolean + } +} + +export const intercomSearchContactsTool: ToolConfig< + IntercomSearchContactsParams, + IntercomSearchContactsResponse +> = { + id: 'intercom_search_contacts', + name: 'Search Contacts in Intercom', + description: 'Search for contacts in Intercom using a query', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + query: { + type: 'string', + required: true, + visibility: 'user-only', + description: + 'Search query (e.g., {"field":"email","operator":"=","value":"user@example.com"})', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results per page (max: 150)', + }, + starting_after: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Cursor for pagination', + }, + }, + + request: { + url: () => buildIntercomUrl('/contacts/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + let query + try { + query = JSON.parse(params.query) + } catch (error) { + // If not JSON, treat as simple text search + query = { + field: 'name', + operator: '~', + value: params.query, + } + } + + const body: any = { query } + + if (params.per_page) body.pagination = { per_page: params.per_page } + if (params.starting_after) + body.pagination = { ...body.pagination, starting_after: params.starting_after } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'search_contacts') + } + + const data = await response.json() + + return { + success: true, + output: { + contacts: data.data || [], + pages: data.pages, + metadata: { + operation: 'search_contacts' as const, + total_count: data.total_count, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + contacts: { type: 'array', description: 'Array of matching contact objects' }, + pages: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/search_conversations.ts b/apps/sim/tools/intercom/search_conversations.ts new file mode 100644 index 0000000000..976ac43120 --- /dev/null +++ b/apps/sim/tools/intercom/search_conversations.ts @@ -0,0 +1,130 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomSearchConversations') + +export interface IntercomSearchConversationsParams { + accessToken: string + query: string + per_page?: number + starting_after?: string +} + +export interface IntercomSearchConversationsResponse { + success: boolean + output: { + conversations: any[] + pages?: any + metadata: { + operation: 'search_conversations' + total_count?: number + } + success: boolean + } +} + +export const intercomSearchConversationsTool: ToolConfig< + IntercomSearchConversationsParams, + IntercomSearchConversationsResponse +> = { + id: 'intercom_search_conversations', + name: 'Search Conversations in Intercom', + description: 'Search for conversations in Intercom using a query', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + query: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Search query as JSON object', + }, + per_page: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results per page (max: 150)', + }, + starting_after: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Cursor for pagination', + }, + }, + + request: { + url: () => buildIntercomUrl('/conversations/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + let query + try { + query = JSON.parse(params.query) + } catch (error) { + logger.warn('Failed to parse search query, using default', { error }) + query = { + field: 'updated_at', + operator: '>', + value: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours + } + } + + const body: any = { query } + + if (params.per_page) body.pagination = { per_page: params.per_page } + if (params.starting_after) + body.pagination = { ...body.pagination, starting_after: params.starting_after } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'search_conversations') + } + + const data = await response.json() + + return { + success: true, + output: { + conversations: data.conversations || [], + pages: data.pages, + metadata: { + operation: 'search_conversations' as const, + total_count: data.total_count, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + conversations: { type: 'array', description: 'Array of matching conversation objects' }, + pages: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/types.ts b/apps/sim/tools/intercom/types.ts new file mode 100644 index 0000000000..0059700c8c --- /dev/null +++ b/apps/sim/tools/intercom/types.ts @@ -0,0 +1,47 @@ +import { createLogger } from '@/lib/logs/console/logger' + +const logger = createLogger('Intercom') + +// Base params for Intercom API +export interface IntercomBaseParams { + accessToken: string // OAuth token or API token (hidden) +} + +export interface IntercomPaginationParams { + per_page?: number + starting_after?: string // Cursor for pagination +} + +export interface IntercomPagingInfo { + next?: { + page: number + starting_after: string + } | null + total_count?: number +} + +export interface IntercomResponse { + success: boolean + output: { + data?: T + pages?: IntercomPagingInfo + metadata: { + operation: string + [key: string]: any + } + success: boolean + } +} + +// Helper function to build Intercom API URLs +export function buildIntercomUrl(path: string): string { + return `https://api.intercom.io${path}` +} + +// Helper function for consistent error handling +export function handleIntercomError(data: any, status: number, operation: string): never { + logger.error(`Intercom API request failed for ${operation}`, { data, status }) + + const errorMessage = data.errors?.[0]?.message || data.error || data.message || 'Unknown error' + throw new Error(`Intercom ${operation} failed: ${errorMessage}`) +} diff --git a/apps/sim/tools/intercom/update_contact.ts b/apps/sim/tools/intercom/update_contact.ts new file mode 100644 index 0000000000..1fb3c7b042 --- /dev/null +++ b/apps/sim/tools/intercom/update_contact.ts @@ -0,0 +1,177 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildIntercomUrl, handleIntercomError } from './types' + +const logger = createLogger('IntercomUpdateContact') + +export interface IntercomUpdateContactParams { + accessToken: string + contactId: string + email?: string + phone?: string + name?: string + avatar?: string + signed_up_at?: number + last_seen_at?: number + owner_id?: string + unsubscribed_from_emails?: boolean + custom_attributes?: string +} + +export interface IntercomUpdateContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'update_contact' + contactId: string + } + success: boolean + } +} + +export const intercomUpdateContactTool: ToolConfig< + IntercomUpdateContactParams, + IntercomUpdateContactResponse +> = { + id: 'intercom_update_contact', + name: 'Update Contact in Intercom', + description: 'Update an existing contact in Intercom', + version: '1.0.0', + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to update', + }, + email: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's email address", + }, + phone: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's phone number", + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: "The contact's name", + }, + avatar: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'An avatar image URL for the contact', + }, + signed_up_at: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'The time the user signed up as a Unix timestamp', + }, + last_seen_at: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'The time the user was last seen as a Unix timestamp', + }, + owner_id: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The id of an admin that has been assigned account ownership of the contact', + }, + unsubscribed_from_emails: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: 'Whether the contact is unsubscribed from emails', + }, + custom_attributes: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom attributes as JSON object (e.g., {"attribute_name": "value"})', + }, + }, + + request: { + url: (params) => buildIntercomUrl(`/contacts/${params.contactId}`), + method: 'PUT', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params) => { + const contact: any = {} + + if (params.email) contact.email = params.email + if (params.phone) contact.phone = params.phone + if (params.name) contact.name = params.name + if (params.avatar) contact.avatar = params.avatar + if (params.signed_up_at) contact.signed_up_at = params.signed_up_at + if (params.last_seen_at) contact.last_seen_at = params.last_seen_at + if (params.owner_id) contact.owner_id = params.owner_id + if (params.unsubscribed_from_emails !== undefined) + contact.unsubscribed_from_emails = params.unsubscribed_from_emails + + if (params.custom_attributes) { + try { + contact.custom_attributes = JSON.parse(params.custom_attributes) + } catch (error) { + logger.warn('Failed to parse custom attributes', { error }) + } + } + + return contact + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'update_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data, + metadata: { + operation: 'update_contact' as const, + contactId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated contact data', + properties: { + contact: { type: 'object', description: 'Updated contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/add_member.ts b/apps/sim/tools/mailchimp/add_member.ts new file mode 100644 index 0000000000..226ab6dceb --- /dev/null +++ b/apps/sim/tools/mailchimp/add_member.ts @@ -0,0 +1,142 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpAddMember') + +export interface MailchimpAddMemberParams { + apiKey: string + listId: string + emailAddress: string + status: string + mergeFields?: string + interests?: string +} + +export interface MailchimpAddMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'add_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpAddMemberTool: ToolConfig< + MailchimpAddMemberParams, + MailchimpAddMemberResponse +> = { + id: 'mailchimp_add_member', + name: 'Add Member to Mailchimp Audience', + description: 'Add a new member to a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + emailAddress: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address', + }, + status: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Subscriber status', + }, + mergeFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of merge fields', + }, + interests: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of interests', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + email_address: params.emailAddress, + status: params.status, + } + + if (params.mergeFields) { + try { + body.merge_fields = JSON.parse(params.mergeFields) + } catch (error) { + logger.warn('Failed to parse merge fields', { error }) + } + } + + if (params.interests) { + try { + body.interests = JSON.parse(params.interests) + } catch (error) { + logger.warn('Failed to parse interests', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'add_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'add_member' as const, + subscriberHash: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Added member data', + properties: { + member: { type: 'object', description: 'Added member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/add_member_tags.ts b/apps/sim/tools/mailchimp/add_member_tags.ts new file mode 100644 index 0000000000..6d3a140f8e --- /dev/null +++ b/apps/sim/tools/mailchimp/add_member_tags.ts @@ -0,0 +1,113 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpAddMemberTags') + +export interface MailchimpAddMemberTagsParams { + apiKey: string + listId: string + subscriberEmail: string + tags: string +} + +export interface MailchimpAddMemberTagsResponse { + success: boolean + output: { + metadata: { + operation: 'add_member_tags' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpAddMemberTagsTool: ToolConfig< + MailchimpAddMemberTagsParams, + MailchimpAddMemberTagsResponse +> = { + id: 'mailchimp_add_member_tags', + name: 'Add Tags to Member in Mailchimp', + description: 'Add tags to a member in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + tags: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of tags', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/members/${params.subscriberEmail}/tags` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + let tags = [] + try { + tags = JSON.parse(params.tags) + } catch (error) { + logger.warn('Failed to parse tags', { error }) + } + + return { tags } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'add_member_tags') + } + + return { + success: true, + output: { + metadata: { + operation: 'add_member_tags' as const, + subscriberHash: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Tag addition confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/add_or_update_member.ts b/apps/sim/tools/mailchimp/add_or_update_member.ts new file mode 100644 index 0000000000..ae5a786544 --- /dev/null +++ b/apps/sim/tools/mailchimp/add_or_update_member.ts @@ -0,0 +1,150 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpAddOrUpdateMember') + +export interface MailchimpAddOrUpdateMemberParams { + apiKey: string + listId: string + subscriberEmail: string + emailAddress: string + statusIfNew: string + mergeFields?: string + interests?: string +} + +export interface MailchimpAddOrUpdateMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'add_or_update_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpAddOrUpdateMemberTool: ToolConfig< + MailchimpAddOrUpdateMemberParams, + MailchimpAddOrUpdateMemberResponse +> = { + id: 'mailchimp_add_or_update_member', + name: 'Add or Update Member in Mailchimp Audience', + description: 'Add a new member or update an existing member in a Mailchimp audience (upsert)', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + emailAddress: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address', + }, + statusIfNew: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Subscriber status if new member', + }, + mergeFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of merge fields', + }, + interests: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of interests', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members/${params.subscriberEmail}`), + method: 'PUT', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + email_address: params.emailAddress, + status_if_new: params.statusIfNew, + } + + if (params.mergeFields) { + try { + body.merge_fields = JSON.parse(params.mergeFields) + } catch (error) { + logger.warn('Failed to parse merge fields', { error }) + } + } + + if (params.interests) { + try { + body.interests = JSON.parse(params.interests) + } catch (error) { + logger.warn('Failed to parse interests', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'add_or_update_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'add_or_update_member' as const, + subscriberHash: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Member data', + properties: { + member: { type: 'object', description: 'Member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/add_segment_member.ts b/apps/sim/tools/mailchimp/add_segment_member.ts new file mode 100644 index 0000000000..c5ea3d686a --- /dev/null +++ b/apps/sim/tools/mailchimp/add_segment_member.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpAddSegmentMember') + +export interface MailchimpAddSegmentMemberParams { + apiKey: string + listId: string + segmentId: string + emailAddress: string +} + +export interface MailchimpAddSegmentMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'add_segment_member' + segmentId: string + } + success: boolean + } +} + +export const mailchimpAddSegmentMemberTool: ToolConfig< + MailchimpAddSegmentMemberParams, + MailchimpAddSegmentMemberResponse +> = { + id: 'mailchimp_add_segment_member', + name: 'Add Member to Segment in Mailchimp', + description: 'Add a member to a specific segment in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment', + }, + emailAddress: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Email address of the member', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/segments/${params.segmentId}/members` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + email_address: params.emailAddress, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'add_segment_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'add_segment_member' as const, + segmentId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Added member data', + properties: { + member: { type: 'object', description: 'Added member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/add_subscriber_to_automation.ts b/apps/sim/tools/mailchimp/add_subscriber_to_automation.ts new file mode 100644 index 0000000000..81152a8985 --- /dev/null +++ b/apps/sim/tools/mailchimp/add_subscriber_to_automation.ts @@ -0,0 +1,113 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpAddSubscriberToAutomation') + +export interface MailchimpAddSubscriberToAutomationParams { + apiKey: string + workflowId: string + workflowEmailId: string + emailAddress: string +} + +export interface MailchimpAddSubscriberToAutomationResponse { + success: boolean + output: { + subscriber: any + metadata: { + operation: 'add_subscriber_to_automation' + workflowId: string + workflowEmailId: string + } + success: boolean + } +} + +export const mailchimpAddSubscriberToAutomationTool: ToolConfig< + MailchimpAddSubscriberToAutomationParams, + MailchimpAddSubscriberToAutomationResponse +> = { + id: 'mailchimp_add_subscriber_to_automation', + name: 'Add Subscriber to Automation in Mailchimp', + description: 'Manually add a subscriber to a workflow email queue', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + workflowId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the automation workflow', + }, + workflowEmailId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the workflow email', + }, + emailAddress: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Email address of the subscriber', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/automations/${params.workflowId}/emails/${params.workflowEmailId}/queue` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + email_address: params.emailAddress, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'add_subscriber_to_automation') + } + + const data = await response.json() + + return { + success: true, + output: { + subscriber: data, + metadata: { + operation: 'add_subscriber_to_automation' as const, + workflowId: '', + workflowEmailId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Subscriber queue data', + properties: { + subscriber: { type: 'object', description: 'Subscriber object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/archive_member.ts b/apps/sim/tools/mailchimp/archive_member.ts new file mode 100644 index 0000000000..dbe79651e4 --- /dev/null +++ b/apps/sim/tools/mailchimp/archive_member.ts @@ -0,0 +1,96 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpArchiveMember') + +export interface MailchimpArchiveMemberParams { + apiKey: string + listId: string + subscriberEmail: string +} + +export interface MailchimpArchiveMemberResponse { + success: boolean + output: { + metadata: { + operation: 'archive_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpArchiveMemberTool: ToolConfig< + MailchimpArchiveMemberParams, + MailchimpArchiveMemberResponse +> = { + id: 'mailchimp_archive_member', + name: 'Archive Member from Mailchimp Audience', + description: 'Permanently archive (delete) a member from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/members/${params.subscriberEmail}/actions/delete-permanent` + ), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'archive_member') + } + + return { + success: true, + output: { + metadata: { + operation: 'archive_member' as const, + subscriberHash: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Archive confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_audience.ts b/apps/sim/tools/mailchimp/create_audience.ts new file mode 100644 index 0000000000..17310ec932 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_audience.ts @@ -0,0 +1,143 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateAudience') + +export interface MailchimpCreateAudienceParams { + apiKey: string + audienceName: string + contact: string + permissionReminder: string + campaignDefaults: string + emailTypeOption: string +} + +export interface MailchimpCreateAudienceResponse { + success: boolean + output: { + list: any + metadata: { + operation: 'create_audience' + listId: string + } + success: boolean + } +} + +export const mailchimpCreateAudienceTool: ToolConfig< + MailchimpCreateAudienceParams, + MailchimpCreateAudienceResponse +> = { + id: 'mailchimp_create_audience', + name: 'Create Audience in Mailchimp', + description: 'Create a new audience (list) in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + audienceName: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the list', + }, + contact: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON object of contact information', + }, + permissionReminder: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Permission reminder text', + }, + campaignDefaults: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON object of default campaign settings', + }, + emailTypeOption: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Support multiple email formats', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, '/lists'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + name: params.audienceName, + permission_reminder: params.permissionReminder, + email_type_option: params.emailTypeOption === 'true', + } + + if (params.contact) { + try { + body.contact = JSON.parse(params.contact) + } catch (error) { + logger.warn('Failed to parse contact', { error }) + } + } + + if (params.campaignDefaults) { + try { + body.campaign_defaults = JSON.parse(params.campaignDefaults) + } catch (error) { + logger.warn('Failed to parse campaign defaults', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_audience') + } + + const data = await response.json() + + return { + success: true, + output: { + list: data, + metadata: { + operation: 'create_audience' as const, + listId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created audience data', + properties: { + list: { type: 'object', description: 'Created audience/list object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_batch_operation.ts b/apps/sim/tools/mailchimp/create_batch_operation.ts new file mode 100644 index 0000000000..5e49f871b1 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_batch_operation.ts @@ -0,0 +1,100 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateBatchOperation') + +export interface MailchimpCreateBatchOperationParams { + apiKey: string + operations: string +} + +export interface MailchimpCreateBatchOperationResponse { + success: boolean + output: { + batch: any + metadata: { + operation: 'create_batch_operation' + batchId: string + } + success: boolean + } +} + +export const mailchimpCreateBatchOperationTool: ToolConfig< + MailchimpCreateBatchOperationParams, + MailchimpCreateBatchOperationResponse +> = { + id: 'mailchimp_create_batch_operation', + name: 'Create Batch Operation in Mailchimp', + description: 'Create a new batch operation in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + operations: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of operations', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, '/batches'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + let operations = [] + try { + operations = JSON.parse(params.operations) + } catch (error) { + logger.warn('Failed to parse operations', { error }) + } + + return { operations } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_batch_operation') + } + + const data = await response.json() + + return { + success: true, + output: { + batch: data, + metadata: { + operation: 'create_batch_operation' as const, + batchId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created batch operation data', + properties: { + batch: { type: 'object', description: 'Created batch operation object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_campaign.ts b/apps/sim/tools/mailchimp/create_campaign.ts new file mode 100644 index 0000000000..add7e5dce8 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_campaign.ts @@ -0,0 +1,127 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateCampaign') + +export interface MailchimpCreateCampaignParams { + apiKey: string + campaignType: string + campaignSettings: string + recipients?: string +} + +export interface MailchimpCreateCampaignResponse { + success: boolean + output: { + campaign: any + metadata: { + operation: 'create_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpCreateCampaignTool: ToolConfig< + MailchimpCreateCampaignParams, + MailchimpCreateCampaignResponse +> = { + id: 'mailchimp_create_campaign', + name: 'Create Campaign in Mailchimp', + description: 'Create a new campaign in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignType: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Campaign type', + }, + campaignSettings: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON object of campaign settings', + }, + recipients: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of recipients', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, '/campaigns'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + type: params.campaignType, + } + + if (params.campaignSettings) { + try { + body.settings = JSON.parse(params.campaignSettings) + } catch (error) { + logger.warn('Failed to parse campaign settings', { error }) + } + } + + if (params.recipients) { + try { + body.recipients = JSON.parse(params.recipients) + } catch (error) { + logger.warn('Failed to parse recipients', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_campaign') + } + + const data = await response.json() + + return { + success: true, + output: { + campaign: data, + metadata: { + operation: 'create_campaign' as const, + campaignId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created campaign data', + properties: { + campaign: { type: 'object', description: 'Created campaign object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_interest.ts b/apps/sim/tools/mailchimp/create_interest.ts new file mode 100644 index 0000000000..2845a347c4 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_interest.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateInterest') + +export interface MailchimpCreateInterestParams { + apiKey: string + listId: string + interestCategoryId: string + interestName: string +} + +export interface MailchimpCreateInterestResponse { + success: boolean + output: { + interest: any + metadata: { + operation: 'create_interest' + interestId: string + } + success: boolean + } +} + +export const mailchimpCreateInterestTool: ToolConfig< + MailchimpCreateInterestParams, + MailchimpCreateInterestResponse +> = { + id: 'mailchimp_create_interest', + name: 'Create Interest in Mailchimp Interest Category', + description: 'Create a new interest in an interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + interestName: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the interest', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}/interests` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + name: params.interestName, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_interest') + } + + const data = await response.json() + + return { + success: true, + output: { + interest: data, + metadata: { + operation: 'create_interest' as const, + interestId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created interest data', + properties: { + interest: { type: 'object', description: 'Created interest object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_interest_category.ts b/apps/sim/tools/mailchimp/create_interest_category.ts new file mode 100644 index 0000000000..2fb3b147b4 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_interest_category.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateInterestCategory') + +export interface MailchimpCreateInterestCategoryParams { + apiKey: string + listId: string + interestCategoryTitle: string + interestCategoryType: string +} + +export interface MailchimpCreateInterestCategoryResponse { + success: boolean + output: { + category: any + metadata: { + operation: 'create_interest_category' + interestCategoryId: string + } + success: boolean + } +} + +export const mailchimpCreateInterestCategoryTool: ToolConfig< + MailchimpCreateInterestCategoryParams, + MailchimpCreateInterestCategoryResponse +> = { + id: 'mailchimp_create_interest_category', + name: 'Create Interest Category in Mailchimp Audience', + description: 'Create a new interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryTitle: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The title of the interest category', + }, + interestCategoryType: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The type of interest category (checkboxes, dropdown, radio, hidden)', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/interest-categories`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + title: params.interestCategoryTitle, + type: params.interestCategoryType, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_interest_category') + } + + const data = await response.json() + + return { + success: true, + output: { + category: data, + metadata: { + operation: 'create_interest_category' as const, + interestCategoryId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created interest category data', + properties: { + category: { type: 'object', description: 'Created interest category object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_landing_page.ts b/apps/sim/tools/mailchimp/create_landing_page.ts new file mode 100644 index 0000000000..5ed9ca3f21 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_landing_page.ts @@ -0,0 +1,106 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateLandingPage') + +export interface MailchimpCreateLandingPageParams { + apiKey: string + landingPageType: string + landingPageTitle?: string +} + +export interface MailchimpCreateLandingPageResponse { + success: boolean + output: { + landingPage: any + metadata: { + operation: 'create_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpCreateLandingPageTool: ToolConfig< + MailchimpCreateLandingPageParams, + MailchimpCreateLandingPageResponse +> = { + id: 'mailchimp_create_landing_page', + name: 'Create Landing Page in Mailchimp', + description: 'Create a new landing page in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + landingPageType: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The type of landing page (signup)', + }, + landingPageTitle: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The title of the landing page', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, '/landing-pages'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + type: params.landingPageType, + } + + if (params.landingPageTitle) body.title = params.landingPageTitle + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_landing_page') + } + + const data = await response.json() + + return { + success: true, + output: { + landingPage: data, + metadata: { + operation: 'create_landing_page' as const, + pageId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created landing page data', + properties: { + landingPage: { type: 'object', description: 'Created landing page object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_merge_field.ts b/apps/sim/tools/mailchimp/create_merge_field.ts new file mode 100644 index 0000000000..d069404ab2 --- /dev/null +++ b/apps/sim/tools/mailchimp/create_merge_field.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateMergeField') + +export interface MailchimpCreateMergeFieldParams { + apiKey: string + listId: string + mergeName: string + mergeType: string +} + +export interface MailchimpCreateMergeFieldResponse { + success: boolean + output: { + mergeField: any + metadata: { + operation: 'create_merge_field' + mergeId: string + } + success: boolean + } +} + +export const mailchimpCreateMergeFieldTool: ToolConfig< + MailchimpCreateMergeFieldParams, + MailchimpCreateMergeFieldResponse +> = { + id: 'mailchimp_create_merge_field', + name: 'Create Merge Field in Mailchimp Audience', + description: 'Create a new merge field in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + mergeName: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the merge field', + }, + mergeType: { + type: 'string', + required: true, + visibility: 'user-only', + description: + 'The type of the merge field (text, number, address, phone, date, url, imageurl, radio, dropdown, birthday, zip)', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/merge-fields`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + name: params.mergeName, + type: params.mergeType, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_merge_field') + } + + const data = await response.json() + + return { + success: true, + output: { + mergeField: data, + metadata: { + operation: 'create_merge_field' as const, + mergeId: data.merge_id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created merge field data', + properties: { + mergeField: { type: 'object', description: 'Created merge field object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_segment.ts b/apps/sim/tools/mailchimp/create_segment.ts new file mode 100644 index 0000000000..4c3afb767a --- /dev/null +++ b/apps/sim/tools/mailchimp/create_segment.ts @@ -0,0 +1,120 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateSegment') + +export interface MailchimpCreateSegmentParams { + apiKey: string + listId: string + segmentName: string + segmentOptions?: string +} + +export interface MailchimpCreateSegmentResponse { + success: boolean + output: { + segment: any + metadata: { + operation: 'create_segment' + segmentId: string + } + success: boolean + } +} + +export const mailchimpCreateSegmentTool: ToolConfig< + MailchimpCreateSegmentParams, + MailchimpCreateSegmentResponse +> = { + id: 'mailchimp_create_segment', + name: 'Create Segment in Mailchimp Audience', + description: 'Create a new segment in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentName: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the segment', + }, + segmentOptions: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of segment options', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/segments`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + name: params.segmentName, + } + + if (params.segmentOptions) { + try { + const options = JSON.parse(params.segmentOptions) + body.options = options + } catch (error) { + logger.warn('Failed to parse segment options', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_segment') + } + + const data = await response.json() + + return { + success: true, + output: { + segment: data, + metadata: { + operation: 'create_segment' as const, + segmentId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created segment data', + properties: { + segment: { type: 'object', description: 'Created segment object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/create_template.ts b/apps/sim/tools/mailchimp/create_template.ts new file mode 100644 index 0000000000..51de9edf9c --- /dev/null +++ b/apps/sim/tools/mailchimp/create_template.ts @@ -0,0 +1,101 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpCreateTemplate') + +export interface MailchimpCreateTemplateParams { + apiKey: string + templateName: string + templateHtml: string +} + +export interface MailchimpCreateTemplateResponse { + success: boolean + output: { + template: any + metadata: { + operation: 'create_template' + templateId: string + } + success: boolean + } +} + +export const mailchimpCreateTemplateTool: ToolConfig< + MailchimpCreateTemplateParams, + MailchimpCreateTemplateResponse +> = { + id: 'mailchimp_create_template', + name: 'Create Template in Mailchimp', + description: 'Create a new template in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + templateName: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the template', + }, + templateHtml: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The HTML content for the template', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, '/templates'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + name: params.templateName, + html: params.templateHtml, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'create_template') + } + + const data = await response.json() + + return { + success: true, + output: { + template: data, + metadata: { + operation: 'create_template' as const, + templateId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created template data', + properties: { + template: { type: 'object', description: 'Created template object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_audience.ts b/apps/sim/tools/mailchimp/delete_audience.ts new file mode 100644 index 0000000000..29519a1f0e --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_audience.ts @@ -0,0 +1,85 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteAudience') + +export interface MailchimpDeleteAudienceParams { + apiKey: string + listId: string +} + +export interface MailchimpDeleteAudienceResponse { + success: boolean + output: { + metadata: { + operation: 'delete_audience' + listId: string + } + success: boolean + } +} + +export const mailchimpDeleteAudienceTool: ToolConfig< + MailchimpDeleteAudienceParams, + MailchimpDeleteAudienceResponse +> = { + id: 'mailchimp_delete_audience', + name: 'Delete Audience from Mailchimp', + description: 'Delete an audience (list) from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list to delete', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_audience') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_audience' as const, + listId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_batch_operation.ts b/apps/sim/tools/mailchimp/delete_batch_operation.ts new file mode 100644 index 0000000000..a808bd2970 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_batch_operation.ts @@ -0,0 +1,85 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteBatchOperation') + +export interface MailchimpDeleteBatchOperationParams { + apiKey: string + batchId: string +} + +export interface MailchimpDeleteBatchOperationResponse { + success: boolean + output: { + metadata: { + operation: 'delete_batch_operation' + batchId: string + } + success: boolean + } +} + +export const mailchimpDeleteBatchOperationTool: ToolConfig< + MailchimpDeleteBatchOperationParams, + MailchimpDeleteBatchOperationResponse +> = { + id: 'mailchimp_delete_batch_operation', + name: 'Delete Batch Operation from Mailchimp', + description: 'Delete a batch operation from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + batchId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the batch operation to delete', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/batches/${params.batchId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_batch_operation') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_batch_operation' as const, + batchId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_campaign.ts b/apps/sim/tools/mailchimp/delete_campaign.ts new file mode 100644 index 0000000000..bde51c42ec --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_campaign.ts @@ -0,0 +1,85 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteCampaign') + +export interface MailchimpDeleteCampaignParams { + apiKey: string + campaignId: string +} + +export interface MailchimpDeleteCampaignResponse { + success: boolean + output: { + metadata: { + operation: 'delete_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpDeleteCampaignTool: ToolConfig< + MailchimpDeleteCampaignParams, + MailchimpDeleteCampaignResponse +> = { + id: 'mailchimp_delete_campaign', + name: 'Delete Campaign from Mailchimp', + description: 'Delete a campaign from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign to delete', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_campaign') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_campaign' as const, + campaignId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_interest.ts b/apps/sim/tools/mailchimp/delete_interest.ts new file mode 100644 index 0000000000..4eac735552 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_interest.ts @@ -0,0 +1,103 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteInterest') + +export interface MailchimpDeleteInterestParams { + apiKey: string + listId: string + interestCategoryId: string + interestId: string +} + +export interface MailchimpDeleteInterestResponse { + success: boolean + output: { + metadata: { + operation: 'delete_interest' + interestId: string + } + success: boolean + } +} + +export const mailchimpDeleteInterestTool: ToolConfig< + MailchimpDeleteInterestParams, + MailchimpDeleteInterestResponse +> = { + id: 'mailchimp_delete_interest', + name: 'Delete Interest from Mailchimp Interest Category', + description: 'Delete an interest from an interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + interestId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest to delete', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}/interests/${params.interestId}` + ), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_interest') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_interest' as const, + interestId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_interest_category.ts b/apps/sim/tools/mailchimp/delete_interest_category.ts new file mode 100644 index 0000000000..db4031b430 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_interest_category.ts @@ -0,0 +1,96 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteInterestCategory') + +export interface MailchimpDeleteInterestCategoryParams { + apiKey: string + listId: string + interestCategoryId: string +} + +export interface MailchimpDeleteInterestCategoryResponse { + success: boolean + output: { + metadata: { + operation: 'delete_interest_category' + interestCategoryId: string + } + success: boolean + } +} + +export const mailchimpDeleteInterestCategoryTool: ToolConfig< + MailchimpDeleteInterestCategoryParams, + MailchimpDeleteInterestCategoryResponse +> = { + id: 'mailchimp_delete_interest_category', + name: 'Delete Interest Category from Mailchimp Audience', + description: 'Delete an interest category from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category to delete', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}` + ), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_interest_category') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_interest_category' as const, + interestCategoryId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_landing_page.ts b/apps/sim/tools/mailchimp/delete_landing_page.ts new file mode 100644 index 0000000000..a590f5ee43 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_landing_page.ts @@ -0,0 +1,85 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteLandingPage') + +export interface MailchimpDeleteLandingPageParams { + apiKey: string + pageId: string +} + +export interface MailchimpDeleteLandingPageResponse { + success: boolean + output: { + metadata: { + operation: 'delete_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpDeleteLandingPageTool: ToolConfig< + MailchimpDeleteLandingPageParams, + MailchimpDeleteLandingPageResponse +> = { + id: 'mailchimp_delete_landing_page', + name: 'Delete Landing Page from Mailchimp', + description: 'Delete a landing page from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the landing page to delete', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/landing-pages/${params.pageId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_landing_page') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_landing_page' as const, + pageId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_member.ts b/apps/sim/tools/mailchimp/delete_member.ts new file mode 100644 index 0000000000..1b0e02b535 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_member.ts @@ -0,0 +1,93 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteMember') + +export interface MailchimpDeleteMemberParams { + apiKey: string + listId: string + subscriberEmail: string +} + +export interface MailchimpDeleteMemberResponse { + success: boolean + output: { + metadata: { + operation: 'delete_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpDeleteMemberTool: ToolConfig< + MailchimpDeleteMemberParams, + MailchimpDeleteMemberResponse +> = { + id: 'mailchimp_delete_member', + name: 'Delete Member from Mailchimp Audience', + description: 'Delete a member from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members/${params.subscriberEmail}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_member') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_member' as const, + subscriberHash: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_merge_field.ts b/apps/sim/tools/mailchimp/delete_merge_field.ts new file mode 100644 index 0000000000..bb9f4a1c40 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_merge_field.ts @@ -0,0 +1,93 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteMergeField') + +export interface MailchimpDeleteMergeFieldParams { + apiKey: string + listId: string + mergeId: string +} + +export interface MailchimpDeleteMergeFieldResponse { + success: boolean + output: { + metadata: { + operation: 'delete_merge_field' + mergeId: string + } + success: boolean + } +} + +export const mailchimpDeleteMergeFieldTool: ToolConfig< + MailchimpDeleteMergeFieldParams, + MailchimpDeleteMergeFieldResponse +> = { + id: 'mailchimp_delete_merge_field', + name: 'Delete Merge Field from Mailchimp Audience', + description: 'Delete a merge field from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + mergeId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the merge field to delete', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/merge-fields/${params.mergeId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_merge_field') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_merge_field' as const, + mergeId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_segment.ts b/apps/sim/tools/mailchimp/delete_segment.ts new file mode 100644 index 0000000000..5c5ee2640b --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_segment.ts @@ -0,0 +1,93 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteSegment') + +export interface MailchimpDeleteSegmentParams { + apiKey: string + listId: string + segmentId: string +} + +export interface MailchimpDeleteSegmentResponse { + success: boolean + output: { + metadata: { + operation: 'delete_segment' + segmentId: string + } + success: boolean + } +} + +export const mailchimpDeleteSegmentTool: ToolConfig< + MailchimpDeleteSegmentParams, + MailchimpDeleteSegmentResponse +> = { + id: 'mailchimp_delete_segment', + name: 'Delete Segment from Mailchimp Audience', + description: 'Delete a segment from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment to delete', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/segments/${params.segmentId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_segment') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_segment' as const, + segmentId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/delete_template.ts b/apps/sim/tools/mailchimp/delete_template.ts new file mode 100644 index 0000000000..2756b9eef4 --- /dev/null +++ b/apps/sim/tools/mailchimp/delete_template.ts @@ -0,0 +1,85 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpDeleteTemplate') + +export interface MailchimpDeleteTemplateParams { + apiKey: string + templateId: string +} + +export interface MailchimpDeleteTemplateResponse { + success: boolean + output: { + metadata: { + operation: 'delete_template' + templateId: string + } + success: boolean + } +} + +export const mailchimpDeleteTemplateTool: ToolConfig< + MailchimpDeleteTemplateParams, + MailchimpDeleteTemplateResponse +> = { + id: 'mailchimp_delete_template', + name: 'Delete Template from Mailchimp', + description: 'Delete a template from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + templateId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the template to delete', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/templates/${params.templateId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'delete_template') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_template' as const, + templateId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_audience.ts b/apps/sim/tools/mailchimp/get_audience.ts new file mode 100644 index 0000000000..daf71dd4b2 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_audience.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetAudience') + +export interface MailchimpGetAudienceParams { + apiKey: string + listId: string +} + +export interface MailchimpGetAudienceResponse { + success: boolean + output: { + list: any + metadata: { + operation: 'get_audience' + listId: string + } + success: boolean + } +} + +export const mailchimpGetAudienceTool: ToolConfig< + MailchimpGetAudienceParams, + MailchimpGetAudienceResponse +> = { + id: 'mailchimp_get_audience', + name: 'Get Audience from Mailchimp', + description: 'Retrieve details of a specific audience (list) from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_audience') + } + + const data = await response.json() + + return { + success: true, + output: { + list: data, + metadata: { + operation: 'get_audience' as const, + listId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Audience data and metadata', + properties: { + list: { type: 'object', description: 'Audience/list object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_audiences.ts b/apps/sim/tools/mailchimp/get_audiences.ts new file mode 100644 index 0000000000..971d7a528d --- /dev/null +++ b/apps/sim/tools/mailchimp/get_audiences.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetAudiences') + +export interface MailchimpGetAudiencesParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetAudiencesResponse { + success: boolean + output: { + lists: any[] + totalItems: number + metadata: { + operation: 'get_audiences' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetAudiencesTool: ToolConfig< + MailchimpGetAudiencesParams, + MailchimpGetAudiencesResponse +> = { + id: 'mailchimp_get_audiences', + name: 'Get Audiences from Mailchimp', + description: 'Retrieve a list of audiences (lists) from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/lists') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_audiences') + } + + const data = await response.json() + const lists = data.lists || [] + + return { + success: true, + output: { + lists, + totalItems: data.total_items || lists.length, + metadata: { + operation: 'get_audiences' as const, + totalReturned: lists.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Audiences data and metadata', + properties: { + lists: { type: 'array', description: 'Array of audience/list objects' }, + totalItems: { type: 'number', description: 'Total number of lists' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_automation.ts b/apps/sim/tools/mailchimp/get_automation.ts new file mode 100644 index 0000000000..9b05f9f5ea --- /dev/null +++ b/apps/sim/tools/mailchimp/get_automation.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetAutomation') + +export interface MailchimpGetAutomationParams { + apiKey: string + workflowId: string +} + +export interface MailchimpGetAutomationResponse { + success: boolean + output: { + automation: any + metadata: { + operation: 'get_automation' + workflowId: string + } + success: boolean + } +} + +export const mailchimpGetAutomationTool: ToolConfig< + MailchimpGetAutomationParams, + MailchimpGetAutomationResponse +> = { + id: 'mailchimp_get_automation', + name: 'Get Automation from Mailchimp', + description: 'Retrieve details of a specific automation from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + workflowId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the automation workflow', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/automations/${params.workflowId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_automation') + } + + const data = await response.json() + + return { + success: true, + output: { + automation: data, + metadata: { + operation: 'get_automation' as const, + workflowId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Automation data and metadata', + properties: { + automation: { type: 'object', description: 'Automation object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_automations.ts b/apps/sim/tools/mailchimp/get_automations.ts new file mode 100644 index 0000000000..ea47a87133 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_automations.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetAutomations') + +export interface MailchimpGetAutomationsParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetAutomationsResponse { + success: boolean + output: { + automations: any[] + totalItems: number + metadata: { + operation: 'get_automations' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetAutomationsTool: ToolConfig< + MailchimpGetAutomationsParams, + MailchimpGetAutomationsResponse +> = { + id: 'mailchimp_get_automations', + name: 'Get Automations from Mailchimp', + description: 'Retrieve a list of automations from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/automations') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_automations') + } + + const data = await response.json() + const automations = data.automations || [] + + return { + success: true, + output: { + automations, + totalItems: data.total_items || automations.length, + metadata: { + operation: 'get_automations' as const, + totalReturned: automations.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Automations data and metadata', + properties: { + automations: { type: 'array', description: 'Array of automation objects' }, + totalItems: { type: 'number', description: 'Total number of automations' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_batch_operation.ts b/apps/sim/tools/mailchimp/get_batch_operation.ts new file mode 100644 index 0000000000..4e1ccdd089 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_batch_operation.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetBatchOperation') + +export interface MailchimpGetBatchOperationParams { + apiKey: string + batchId: string +} + +export interface MailchimpGetBatchOperationResponse { + success: boolean + output: { + batch: any + metadata: { + operation: 'get_batch_operation' + batchId: string + } + success: boolean + } +} + +export const mailchimpGetBatchOperationTool: ToolConfig< + MailchimpGetBatchOperationParams, + MailchimpGetBatchOperationResponse +> = { + id: 'mailchimp_get_batch_operation', + name: 'Get Batch Operation from Mailchimp', + description: 'Retrieve details of a specific batch operation from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + batchId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the batch operation', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/batches/${params.batchId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_batch_operation') + } + + const data = await response.json() + + return { + success: true, + output: { + batch: data, + metadata: { + operation: 'get_batch_operation' as const, + batchId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Batch operation data and metadata', + properties: { + batch: { type: 'object', description: 'Batch operation object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_batch_operations.ts b/apps/sim/tools/mailchimp/get_batch_operations.ts new file mode 100644 index 0000000000..0e98715d86 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_batch_operations.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetBatchOperations') + +export interface MailchimpGetBatchOperationsParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetBatchOperationsResponse { + success: boolean + output: { + batches: any[] + totalItems: number + metadata: { + operation: 'get_batch_operations' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetBatchOperationsTool: ToolConfig< + MailchimpGetBatchOperationsParams, + MailchimpGetBatchOperationsResponse +> = { + id: 'mailchimp_get_batch_operations', + name: 'Get Batch Operations from Mailchimp', + description: 'Retrieve a list of batch operations from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/batches') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_batch_operations') + } + + const data = await response.json() + const batches = data.batches || [] + + return { + success: true, + output: { + batches, + totalItems: data.total_items || batches.length, + metadata: { + operation: 'get_batch_operations' as const, + totalReturned: batches.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Batch operations data and metadata', + properties: { + batches: { type: 'array', description: 'Array of batch operation objects' }, + totalItems: { type: 'number', description: 'Total number of batch operations' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_campaign.ts b/apps/sim/tools/mailchimp/get_campaign.ts new file mode 100644 index 0000000000..ec40e0272f --- /dev/null +++ b/apps/sim/tools/mailchimp/get_campaign.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetCampaign') + +export interface MailchimpGetCampaignParams { + apiKey: string + campaignId: string +} + +export interface MailchimpGetCampaignResponse { + success: boolean + output: { + campaign: any + metadata: { + operation: 'get_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpGetCampaignTool: ToolConfig< + MailchimpGetCampaignParams, + MailchimpGetCampaignResponse +> = { + id: 'mailchimp_get_campaign', + name: 'Get Campaign from Mailchimp', + description: 'Retrieve details of a specific campaign from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_campaign') + } + + const data = await response.json() + + return { + success: true, + output: { + campaign: data, + metadata: { + operation: 'get_campaign' as const, + campaignId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaign data and metadata', + properties: { + campaign: { type: 'object', description: 'Campaign object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_campaign_content.ts b/apps/sim/tools/mailchimp/get_campaign_content.ts new file mode 100644 index 0000000000..706b8b5ae7 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_campaign_content.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetCampaignContent') + +export interface MailchimpGetCampaignContentParams { + apiKey: string + campaignId: string +} + +export interface MailchimpGetCampaignContentResponse { + success: boolean + output: { + content: any + metadata: { + operation: 'get_campaign_content' + campaignId: string + } + success: boolean + } +} + +export const mailchimpGetCampaignContentTool: ToolConfig< + MailchimpGetCampaignContentParams, + MailchimpGetCampaignContentResponse +> = { + id: 'mailchimp_get_campaign_content', + name: 'Get Campaign Content from Mailchimp', + description: 'Retrieve the HTML and plain-text content for a Mailchimp campaign', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/content`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_campaign_content') + } + + const data = await response.json() + + return { + success: true, + output: { + content: data, + metadata: { + operation: 'get_campaign_content' as const, + campaignId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaign content data', + properties: { + content: { type: 'object', description: 'Campaign content object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_campaign_report.ts b/apps/sim/tools/mailchimp/get_campaign_report.ts new file mode 100644 index 0000000000..852a9c912f --- /dev/null +++ b/apps/sim/tools/mailchimp/get_campaign_report.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetCampaignReport') + +export interface MailchimpGetCampaignReportParams { + apiKey: string + campaignId: string +} + +export interface MailchimpGetCampaignReportResponse { + success: boolean + output: { + report: any + metadata: { + operation: 'get_campaign_report' + campaignId: string + } + success: boolean + } +} + +export const mailchimpGetCampaignReportTool: ToolConfig< + MailchimpGetCampaignReportParams, + MailchimpGetCampaignReportResponse +> = { + id: 'mailchimp_get_campaign_report', + name: 'Get Campaign Report from Mailchimp', + description: 'Retrieve the report for a specific campaign from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/reports/${params.campaignId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_campaign_report') + } + + const data = await response.json() + + return { + success: true, + output: { + report: data, + metadata: { + operation: 'get_campaign_report' as const, + campaignId: data.campaign_id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaign report data and metadata', + properties: { + report: { type: 'object', description: 'Campaign report object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_campaign_reports.ts b/apps/sim/tools/mailchimp/get_campaign_reports.ts new file mode 100644 index 0000000000..d83a2acd2c --- /dev/null +++ b/apps/sim/tools/mailchimp/get_campaign_reports.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetCampaignReports') + +export interface MailchimpGetCampaignReportsParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetCampaignReportsResponse { + success: boolean + output: { + reports: any[] + totalItems: number + metadata: { + operation: 'get_campaign_reports' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetCampaignReportsTool: ToolConfig< + MailchimpGetCampaignReportsParams, + MailchimpGetCampaignReportsResponse +> = { + id: 'mailchimp_get_campaign_reports', + name: 'Get Campaign Reports from Mailchimp', + description: 'Retrieve a list of campaign reports from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/reports') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_campaign_reports') + } + + const data = await response.json() + const reports = data.reports || [] + + return { + success: true, + output: { + reports, + totalItems: data.total_items || reports.length, + metadata: { + operation: 'get_campaign_reports' as const, + totalReturned: reports.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaign reports data and metadata', + properties: { + reports: { type: 'array', description: 'Array of campaign report objects' }, + totalItems: { type: 'number', description: 'Total number of reports' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_campaigns.ts b/apps/sim/tools/mailchimp/get_campaigns.ts new file mode 100644 index 0000000000..bc725ab7bc --- /dev/null +++ b/apps/sim/tools/mailchimp/get_campaigns.ts @@ -0,0 +1,125 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetCampaigns') + +export interface MailchimpGetCampaignsParams { + apiKey: string + campaignType?: string + status?: string + count?: string + offset?: string +} + +export interface MailchimpGetCampaignsResponse { + success: boolean + output: { + campaigns: any[] + totalItems: number + metadata: { + operation: 'get_campaigns' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetCampaignsTool: ToolConfig< + MailchimpGetCampaignsParams, + MailchimpGetCampaignsResponse +> = { + id: 'mailchimp_get_campaigns', + name: 'Get Campaigns from Mailchimp', + description: 'Retrieve a list of campaigns from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignType: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by campaign type (regular, plaintext, absplit, rss, variate)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by status (save, paused, schedule, sending, sent)', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.campaignType) queryParams.append('type', params.campaignType) + if (params.status) queryParams.append('status', params.status) + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/campaigns') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_campaigns') + } + + const data = await response.json() + const campaigns = data.campaigns || [] + + return { + success: true, + output: { + campaigns, + totalItems: data.total_items || campaigns.length, + metadata: { + operation: 'get_campaigns' as const, + totalReturned: campaigns.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaigns data and metadata', + properties: { + campaigns: { type: 'array', description: 'Array of campaign objects' }, + totalItems: { type: 'number', description: 'Total number of campaigns' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_interest.ts b/apps/sim/tools/mailchimp/get_interest.ts new file mode 100644 index 0000000000..bdde022c76 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_interest.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetInterest') + +export interface MailchimpGetInterestParams { + apiKey: string + listId: string + interestCategoryId: string + interestId: string +} + +export interface MailchimpGetInterestResponse { + success: boolean + output: { + interest: any + metadata: { + operation: 'get_interest' + interestId: string + } + success: boolean + } +} + +export const mailchimpGetInterestTool: ToolConfig< + MailchimpGetInterestParams, + MailchimpGetInterestResponse +> = { + id: 'mailchimp_get_interest', + name: 'Get Interest from Mailchimp Interest Category', + description: + 'Retrieve details of a specific interest from an interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + interestId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}/interests/${params.interestId}` + ), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_interest') + } + + const data = await response.json() + + return { + success: true, + output: { + interest: data, + metadata: { + operation: 'get_interest' as const, + interestId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Interest data and metadata', + properties: { + interest: { type: 'object', description: 'Interest object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_interest_categories.ts b/apps/sim/tools/mailchimp/get_interest_categories.ts new file mode 100644 index 0000000000..80305e7d45 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_interest_categories.ts @@ -0,0 +1,116 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetInterestCategories') + +export interface MailchimpGetInterestCategoriesParams { + apiKey: string + listId: string + count?: string + offset?: string +} + +export interface MailchimpGetInterestCategoriesResponse { + success: boolean + output: { + categories: any[] + totalItems: number + metadata: { + operation: 'get_interest_categories' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetInterestCategoriesTool: ToolConfig< + MailchimpGetInterestCategoriesParams, + MailchimpGetInterestCategoriesResponse +> = { + id: 'mailchimp_get_interest_categories', + name: 'Get Interest Categories from Mailchimp Audience', + description: 'Retrieve a list of interest categories from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/interest-categories`) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_interest_categories') + } + + const data = await response.json() + const categories = data.categories || [] + + return { + success: true, + output: { + categories, + totalItems: data.total_items || categories.length, + metadata: { + operation: 'get_interest_categories' as const, + totalReturned: categories.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Interest categories data and metadata', + properties: { + categories: { type: 'array', description: 'Array of interest category objects' }, + totalItems: { type: 'number', description: 'Total number of categories' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_interest_category.ts b/apps/sim/tools/mailchimp/get_interest_category.ts new file mode 100644 index 0000000000..6d51d0457f --- /dev/null +++ b/apps/sim/tools/mailchimp/get_interest_category.ts @@ -0,0 +1,101 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetInterestCategory') + +export interface MailchimpGetInterestCategoryParams { + apiKey: string + listId: string + interestCategoryId: string +} + +export interface MailchimpGetInterestCategoryResponse { + success: boolean + output: { + category: any + metadata: { + operation: 'get_interest_category' + interestCategoryId: string + } + success: boolean + } +} + +export const mailchimpGetInterestCategoryTool: ToolConfig< + MailchimpGetInterestCategoryParams, + MailchimpGetInterestCategoryResponse +> = { + id: 'mailchimp_get_interest_category', + name: 'Get Interest Category from Mailchimp Audience', + description: 'Retrieve details of a specific interest category from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}` + ), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_interest_category') + } + + const data = await response.json() + + return { + success: true, + output: { + category: data, + metadata: { + operation: 'get_interest_category' as const, + interestCategoryId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Interest category data and metadata', + properties: { + category: { type: 'object', description: 'Interest category object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_interests.ts b/apps/sim/tools/mailchimp/get_interests.ts new file mode 100644 index 0000000000..93ca63e1f5 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_interests.ts @@ -0,0 +1,126 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetInterests') + +export interface MailchimpGetInterestsParams { + apiKey: string + listId: string + interestCategoryId: string + count?: string + offset?: string +} + +export interface MailchimpGetInterestsResponse { + success: boolean + output: { + interests: any[] + totalItems: number + metadata: { + operation: 'get_interests' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetInterestsTool: ToolConfig< + MailchimpGetInterestsParams, + MailchimpGetInterestsResponse +> = { + id: 'mailchimp_get_interests', + name: 'Get Interests from Mailchimp Interest Category', + description: 'Retrieve a list of interests from an interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}/interests` + ) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_interests') + } + + const data = await response.json() + const interests = data.interests || [] + + return { + success: true, + output: { + interests, + totalItems: data.total_items || interests.length, + metadata: { + operation: 'get_interests' as const, + totalReturned: interests.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Interests data and metadata', + properties: { + interests: { type: 'array', description: 'Array of interest objects' }, + totalItems: { type: 'number', description: 'Total number of interests' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_landing_page.ts b/apps/sim/tools/mailchimp/get_landing_page.ts new file mode 100644 index 0000000000..f3ec0cf37b --- /dev/null +++ b/apps/sim/tools/mailchimp/get_landing_page.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetLandingPage') + +export interface MailchimpGetLandingPageParams { + apiKey: string + pageId: string +} + +export interface MailchimpGetLandingPageResponse { + success: boolean + output: { + landingPage: any + metadata: { + operation: 'get_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpGetLandingPageTool: ToolConfig< + MailchimpGetLandingPageParams, + MailchimpGetLandingPageResponse +> = { + id: 'mailchimp_get_landing_page', + name: 'Get Landing Page from Mailchimp', + description: 'Retrieve details of a specific landing page from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the landing page', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/landing-pages/${params.pageId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_landing_page') + } + + const data = await response.json() + + return { + success: true, + output: { + landingPage: data, + metadata: { + operation: 'get_landing_page' as const, + pageId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Landing page data and metadata', + properties: { + landingPage: { type: 'object', description: 'Landing page object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_landing_pages.ts b/apps/sim/tools/mailchimp/get_landing_pages.ts new file mode 100644 index 0000000000..b8095cdd9c --- /dev/null +++ b/apps/sim/tools/mailchimp/get_landing_pages.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetLandingPages') + +export interface MailchimpGetLandingPagesParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetLandingPagesResponse { + success: boolean + output: { + landingPages: any[] + totalItems: number + metadata: { + operation: 'get_landing_pages' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetLandingPagesTool: ToolConfig< + MailchimpGetLandingPagesParams, + MailchimpGetLandingPagesResponse +> = { + id: 'mailchimp_get_landing_pages', + name: 'Get Landing Pages from Mailchimp', + description: 'Retrieve a list of landing pages from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/landing-pages') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_landing_pages') + } + + const data = await response.json() + const landingPages = data.landing_pages || [] + + return { + success: true, + output: { + landingPages, + totalItems: data.total_items || landingPages.length, + metadata: { + operation: 'get_landing_pages' as const, + totalReturned: landingPages.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Landing pages data and metadata', + properties: { + landingPages: { type: 'array', description: 'Array of landing page objects' }, + totalItems: { type: 'number', description: 'Total number of landing pages' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_member.ts b/apps/sim/tools/mailchimp/get_member.ts new file mode 100644 index 0000000000..42a24a1eaa --- /dev/null +++ b/apps/sim/tools/mailchimp/get_member.ts @@ -0,0 +1,98 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetMember') + +export interface MailchimpGetMemberParams { + apiKey: string + listId: string + subscriberEmail: string +} + +export interface MailchimpGetMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'get_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpGetMemberTool: ToolConfig< + MailchimpGetMemberParams, + MailchimpGetMemberResponse +> = { + id: 'mailchimp_get_member', + name: 'Get Member from Mailchimp Audience', + description: 'Retrieve details of a specific member from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members/${params.subscriberEmail}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'get_member' as const, + subscriberHash: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Member data and metadata', + properties: { + member: { type: 'object', description: 'Member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_member_tags.ts b/apps/sim/tools/mailchimp/get_member_tags.ts new file mode 100644 index 0000000000..850f7e23b9 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_member_tags.ts @@ -0,0 +1,105 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetMemberTags') + +export interface MailchimpGetMemberTagsParams { + apiKey: string + listId: string + subscriberEmail: string +} + +export interface MailchimpGetMemberTagsResponse { + success: boolean + output: { + tags: any[] + totalItems: number + metadata: { + operation: 'get_member_tags' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetMemberTagsTool: ToolConfig< + MailchimpGetMemberTagsParams, + MailchimpGetMemberTagsResponse +> = { + id: 'mailchimp_get_member_tags', + name: 'Get Member Tags from Mailchimp', + description: 'Retrieve tags associated with a member in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/members/${params.subscriberEmail}/tags` + ), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_member_tags') + } + + const data = await response.json() + const tags = data.tags || [] + + return { + success: true, + output: { + tags, + totalItems: data.total_items || tags.length, + metadata: { + operation: 'get_member_tags' as const, + totalReturned: tags.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Member tags data and metadata', + properties: { + tags: { type: 'array', description: 'Array of tag objects' }, + totalItems: { type: 'number', description: 'Total number of tags' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_members.ts b/apps/sim/tools/mailchimp/get_members.ts new file mode 100644 index 0000000000..64fbaa7b3a --- /dev/null +++ b/apps/sim/tools/mailchimp/get_members.ts @@ -0,0 +1,124 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetMembers') + +export interface MailchimpGetMembersParams { + apiKey: string + listId: string + status?: string + count?: string + offset?: string +} + +export interface MailchimpGetMembersResponse { + success: boolean + output: { + members: any[] + totalItems: number + metadata: { + operation: 'get_members' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetMembersTool: ToolConfig< + MailchimpGetMembersParams, + MailchimpGetMembersResponse +> = { + id: 'mailchimp_get_members', + name: 'Get Members from Mailchimp Audience', + description: 'Retrieve a list of members from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by status (subscribed, unsubscribed, cleaned, pending)', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.status) queryParams.append('status', params.status) + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members`) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_members') + } + + const data = await response.json() + const members = data.members || [] + + return { + success: true, + output: { + members, + totalItems: data.total_items || members.length, + metadata: { + operation: 'get_members' as const, + totalReturned: members.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Members data and metadata', + properties: { + members: { type: 'array', description: 'Array of member objects' }, + totalItems: { type: 'number', description: 'Total number of members' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_merge_field.ts b/apps/sim/tools/mailchimp/get_merge_field.ts new file mode 100644 index 0000000000..912be2a5fc --- /dev/null +++ b/apps/sim/tools/mailchimp/get_merge_field.ts @@ -0,0 +1,98 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetMergeField') + +export interface MailchimpGetMergeFieldParams { + apiKey: string + listId: string + mergeId: string +} + +export interface MailchimpGetMergeFieldResponse { + success: boolean + output: { + mergeField: any + metadata: { + operation: 'get_merge_field' + mergeId: string + } + success: boolean + } +} + +export const mailchimpGetMergeFieldTool: ToolConfig< + MailchimpGetMergeFieldParams, + MailchimpGetMergeFieldResponse +> = { + id: 'mailchimp_get_merge_field', + name: 'Get Merge Field from Mailchimp Audience', + description: 'Retrieve details of a specific merge field from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + mergeId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the merge field', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/merge-fields/${params.mergeId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_merge_field') + } + + const data = await response.json() + + return { + success: true, + output: { + mergeField: data, + metadata: { + operation: 'get_merge_field' as const, + mergeId: data.merge_id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Merge field data and metadata', + properties: { + mergeField: { type: 'object', description: 'Merge field object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_merge_fields.ts b/apps/sim/tools/mailchimp/get_merge_fields.ts new file mode 100644 index 0000000000..8e1dbca19a --- /dev/null +++ b/apps/sim/tools/mailchimp/get_merge_fields.ts @@ -0,0 +1,116 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetMergeFields') + +export interface MailchimpGetMergeFieldsParams { + apiKey: string + listId: string + count?: string + offset?: string +} + +export interface MailchimpGetMergeFieldsResponse { + success: boolean + output: { + mergeFields: any[] + totalItems: number + metadata: { + operation: 'get_merge_fields' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetMergeFieldsTool: ToolConfig< + MailchimpGetMergeFieldsParams, + MailchimpGetMergeFieldsResponse +> = { + id: 'mailchimp_get_merge_fields', + name: 'Get Merge Fields from Mailchimp Audience', + description: 'Retrieve a list of merge fields from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/merge-fields`) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_merge_fields') + } + + const data = await response.json() + const mergeFields = data.merge_fields || [] + + return { + success: true, + output: { + mergeFields, + totalItems: data.total_items || mergeFields.length, + metadata: { + operation: 'get_merge_fields' as const, + totalReturned: mergeFields.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Merge fields data and metadata', + properties: { + mergeFields: { type: 'array', description: 'Array of merge field objects' }, + totalItems: { type: 'number', description: 'Total number of merge fields' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_segment.ts b/apps/sim/tools/mailchimp/get_segment.ts new file mode 100644 index 0000000000..ed2ba417ad --- /dev/null +++ b/apps/sim/tools/mailchimp/get_segment.ts @@ -0,0 +1,98 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetSegment') + +export interface MailchimpGetSegmentParams { + apiKey: string + listId: string + segmentId: string +} + +export interface MailchimpGetSegmentResponse { + success: boolean + output: { + segment: any + metadata: { + operation: 'get_segment' + segmentId: string + } + success: boolean + } +} + +export const mailchimpGetSegmentTool: ToolConfig< + MailchimpGetSegmentParams, + MailchimpGetSegmentResponse +> = { + id: 'mailchimp_get_segment', + name: 'Get Segment from Mailchimp Audience', + description: 'Retrieve details of a specific segment from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/segments/${params.segmentId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_segment') + } + + const data = await response.json() + + return { + success: true, + output: { + segment: data, + metadata: { + operation: 'get_segment' as const, + segmentId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Segment data and metadata', + properties: { + segment: { type: 'object', description: 'Segment object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_segment_members.ts b/apps/sim/tools/mailchimp/get_segment_members.ts new file mode 100644 index 0000000000..8a594c8fb2 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_segment_members.ts @@ -0,0 +1,126 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetSegmentMembers') + +export interface MailchimpGetSegmentMembersParams { + apiKey: string + listId: string + segmentId: string + count?: string + offset?: string +} + +export interface MailchimpGetSegmentMembersResponse { + success: boolean + output: { + members: any[] + totalItems: number + metadata: { + operation: 'get_segment_members' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetSegmentMembersTool: ToolConfig< + MailchimpGetSegmentMembersParams, + MailchimpGetSegmentMembersResponse +> = { + id: 'mailchimp_get_segment_members', + name: 'Get Segment Members from Mailchimp', + description: 'Retrieve members of a specific segment from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/segments/${params.segmentId}/members` + ) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_segment_members') + } + + const data = await response.json() + const members = data.members || [] + + return { + success: true, + output: { + members, + totalItems: data.total_items || members.length, + metadata: { + operation: 'get_segment_members' as const, + totalReturned: members.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Segment members data and metadata', + properties: { + members: { type: 'array', description: 'Array of member objects' }, + totalItems: { type: 'number', description: 'Total number of members' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_segments.ts b/apps/sim/tools/mailchimp/get_segments.ts new file mode 100644 index 0000000000..480eb0cf73 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_segments.ts @@ -0,0 +1,116 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetSegments') + +export interface MailchimpGetSegmentsParams { + apiKey: string + listId: string + count?: string + offset?: string +} + +export interface MailchimpGetSegmentsResponse { + success: boolean + output: { + segments: any[] + totalItems: number + metadata: { + operation: 'get_segments' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetSegmentsTool: ToolConfig< + MailchimpGetSegmentsParams, + MailchimpGetSegmentsResponse +> = { + id: 'mailchimp_get_segments', + name: 'Get Segments from Mailchimp Audience', + description: 'Retrieve a list of segments from a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/segments`) + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_segments') + } + + const data = await response.json() + const segments = data.segments || [] + + return { + success: true, + output: { + segments, + totalItems: data.total_items || segments.length, + metadata: { + operation: 'get_segments' as const, + totalReturned: segments.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Segments data and metadata', + properties: { + segments: { type: 'array', description: 'Array of segment objects' }, + totalItems: { type: 'number', description: 'Total number of segments' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_template.ts b/apps/sim/tools/mailchimp/get_template.ts new file mode 100644 index 0000000000..31a4f79389 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_template.ts @@ -0,0 +1,90 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetTemplate') + +export interface MailchimpGetTemplateParams { + apiKey: string + templateId: string +} + +export interface MailchimpGetTemplateResponse { + success: boolean + output: { + template: any + metadata: { + operation: 'get_template' + templateId: string + } + success: boolean + } +} + +export const mailchimpGetTemplateTool: ToolConfig< + MailchimpGetTemplateParams, + MailchimpGetTemplateResponse +> = { + id: 'mailchimp_get_template', + name: 'Get Template from Mailchimp', + description: 'Retrieve details of a specific template from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + templateId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the template', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/templates/${params.templateId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_template') + } + + const data = await response.json() + + return { + success: true, + output: { + template: data, + metadata: { + operation: 'get_template' as const, + templateId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Template data and metadata', + properties: { + template: { type: 'object', description: 'Template object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/get_templates.ts b/apps/sim/tools/mailchimp/get_templates.ts new file mode 100644 index 0000000000..db181474c6 --- /dev/null +++ b/apps/sim/tools/mailchimp/get_templates.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpGetTemplates') + +export interface MailchimpGetTemplatesParams { + apiKey: string + count?: string + offset?: string +} + +export interface MailchimpGetTemplatesResponse { + success: boolean + output: { + templates: any[] + totalItems: number + metadata: { + operation: 'get_templates' + totalReturned: number + } + success: boolean + } +} + +export const mailchimpGetTemplatesTool: ToolConfig< + MailchimpGetTemplatesParams, + MailchimpGetTemplatesResponse +> = { + id: 'mailchimp_get_templates', + name: 'Get Templates from Mailchimp', + description: 'Retrieve a list of templates from Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + count: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results (default: 10, max: 1000)', + }, + offset: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of results to skip', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.count) queryParams.append('count', params.count) + if (params.offset) queryParams.append('offset', params.offset) + + const query = queryParams.toString() + const url = buildMailchimpUrl(params.apiKey, '/templates') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'get_templates') + } + + const data = await response.json() + const templates = data.templates || [] + + return { + success: true, + output: { + templates, + totalItems: data.total_items || templates.length, + metadata: { + operation: 'get_templates' as const, + totalReturned: templates.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Templates data and metadata', + properties: { + templates: { type: 'array', description: 'Array of template objects' }, + totalItems: { type: 'number', description: 'Total number of templates' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/index.ts b/apps/sim/tools/mailchimp/index.ts new file mode 100644 index 0000000000..8b1f23a692 --- /dev/null +++ b/apps/sim/tools/mailchimp/index.ts @@ -0,0 +1,88 @@ +// Audience/List tools + +export { mailchimpAddMemberTool } from './add_member' +export { mailchimpAddMemberTagsTool } from './add_member_tags' +export { mailchimpAddOrUpdateMemberTool } from './add_or_update_member' +export { mailchimpAddSegmentMemberTool } from './add_segment_member' +export { mailchimpAddSubscriberToAutomationTool } from './add_subscriber_to_automation' +export { mailchimpArchiveMemberTool } from './archive_member' +export { mailchimpCreateAudienceTool } from './create_audience' +export { mailchimpCreateBatchOperationTool } from './create_batch_operation' +export { mailchimpCreateCampaignTool } from './create_campaign' +export { mailchimpCreateInterestTool } from './create_interest' +export { mailchimpCreateInterestCategoryTool } from './create_interest_category' +export { mailchimpCreateLandingPageTool } from './create_landing_page' +export { mailchimpCreateMergeFieldTool } from './create_merge_field' +export { mailchimpCreateSegmentTool } from './create_segment' +export { mailchimpCreateTemplateTool } from './create_template' +export { mailchimpDeleteAudienceTool } from './delete_audience' +export { mailchimpDeleteBatchOperationTool } from './delete_batch_operation' +export { mailchimpDeleteCampaignTool } from './delete_campaign' +export { mailchimpDeleteInterestTool } from './delete_interest' +export { mailchimpDeleteInterestCategoryTool } from './delete_interest_category' +export { mailchimpDeleteLandingPageTool } from './delete_landing_page' +export { mailchimpDeleteMemberTool } from './delete_member' +export { mailchimpDeleteMergeFieldTool } from './delete_merge_field' +export { mailchimpDeleteSegmentTool } from './delete_segment' +export { mailchimpDeleteTemplateTool } from './delete_template' +export { mailchimpGetAudienceTool } from './get_audience' +export { mailchimpGetAudiencesTool } from './get_audiences' +export { mailchimpGetAutomationTool } from './get_automation' +// Automation tools +export { mailchimpGetAutomationsTool } from './get_automations' +export { mailchimpGetBatchOperationTool } from './get_batch_operation' +// Batch operation tools +export { mailchimpGetBatchOperationsTool } from './get_batch_operations' +export { mailchimpGetCampaignTool } from './get_campaign' +// Campaign content tools +export { mailchimpGetCampaignContentTool } from './get_campaign_content' +export { mailchimpGetCampaignReportTool } from './get_campaign_report' +// Report tools +export { mailchimpGetCampaignReportsTool } from './get_campaign_reports' +// Campaign tools +export { mailchimpGetCampaignsTool } from './get_campaigns' +export { mailchimpGetInterestTool } from './get_interest' +// Interest category tools +export { mailchimpGetInterestCategoriesTool } from './get_interest_categories' +export { mailchimpGetInterestCategoryTool } from './get_interest_category' +// Interest tools +export { mailchimpGetInterestsTool } from './get_interests' +export { mailchimpGetLandingPageTool } from './get_landing_page' +// Landing page tools +export { mailchimpGetLandingPagesTool } from './get_landing_pages' +export { mailchimpGetMemberTool } from './get_member' +// Tag tools +export { mailchimpGetMemberTagsTool } from './get_member_tags' +// Member tools +export { mailchimpGetMembersTool } from './get_members' +export { mailchimpGetMergeFieldTool } from './get_merge_field' +// Merge field tools +export { mailchimpGetMergeFieldsTool } from './get_merge_fields' +export { mailchimpGetSegmentTool } from './get_segment' +export { mailchimpGetSegmentMembersTool } from './get_segment_members' +// Segment tools +export { mailchimpGetSegmentsTool } from './get_segments' +export { mailchimpGetTemplateTool } from './get_template' +// Template tools +export { mailchimpGetTemplatesTool } from './get_templates' +export { mailchimpPauseAutomationTool } from './pause_automation' +export { mailchimpPublishLandingPageTool } from './publish_landing_page' +export { mailchimpRemoveMemberTagsTool } from './remove_member_tags' +export { mailchimpRemoveSegmentMemberTool } from './remove_segment_member' +export { mailchimpReplicateCampaignTool } from './replicate_campaign' +export { mailchimpScheduleCampaignTool } from './schedule_campaign' +export { mailchimpSendCampaignTool } from './send_campaign' +export { mailchimpSetCampaignContentTool } from './set_campaign_content' +export { mailchimpStartAutomationTool } from './start_automation' +export { mailchimpUnarchiveMemberTool } from './unarchive_member' +export { mailchimpUnpublishLandingPageTool } from './unpublish_landing_page' +export { mailchimpUnscheduleCampaignTool } from './unschedule_campaign' +export { mailchimpUpdateAudienceTool } from './update_audience' +export { mailchimpUpdateCampaignTool } from './update_campaign' +export { mailchimpUpdateInterestTool } from './update_interest' +export { mailchimpUpdateInterestCategoryTool } from './update_interest_category' +export { mailchimpUpdateLandingPageTool } from './update_landing_page' +export { mailchimpUpdateMemberTool } from './update_member' +export { mailchimpUpdateMergeFieldTool } from './update_merge_field' +export { mailchimpUpdateSegmentTool } from './update_segment' +export { mailchimpUpdateTemplateTool } from './update_template' diff --git a/apps/sim/tools/mailchimp/pause_automation.ts b/apps/sim/tools/mailchimp/pause_automation.ts new file mode 100644 index 0000000000..0f26072019 --- /dev/null +++ b/apps/sim/tools/mailchimp/pause_automation.ts @@ -0,0 +1,89 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpPauseAutomation') + +export interface MailchimpPauseAutomationParams { + apiKey: string + workflowId: string +} + +export interface MailchimpPauseAutomationResponse { + success: boolean + output: { + metadata: { + operation: 'pause_automation' + workflowId: string + } + success: boolean + } +} + +export const mailchimpPauseAutomationTool: ToolConfig< + MailchimpPauseAutomationParams, + MailchimpPauseAutomationResponse +> = { + id: 'mailchimp_pause_automation', + name: 'Pause Automation in Mailchimp', + description: 'Pause all emails in a Mailchimp automation workflow', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + workflowId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the automation workflow', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/automations/${params.workflowId}/actions/pause-all-emails` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'pause_automation') + } + + return { + success: true, + output: { + metadata: { + operation: 'pause_automation' as const, + workflowId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Pause confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/publish_landing_page.ts b/apps/sim/tools/mailchimp/publish_landing_page.ts new file mode 100644 index 0000000000..bfcfa700d8 --- /dev/null +++ b/apps/sim/tools/mailchimp/publish_landing_page.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpPublishLandingPage') + +export interface MailchimpPublishLandingPageParams { + apiKey: string + pageId: string +} + +export interface MailchimpPublishLandingPageResponse { + success: boolean + output: { + metadata: { + operation: 'publish_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpPublishLandingPageTool: ToolConfig< + MailchimpPublishLandingPageParams, + MailchimpPublishLandingPageResponse +> = { + id: 'mailchimp_publish_landing_page', + name: 'Publish Landing Page in Mailchimp', + description: 'Publish a landing page in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the landing page', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/landing-pages/${params.pageId}/actions/publish`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'publish_landing_page') + } + + return { + success: true, + output: { + metadata: { + operation: 'publish_landing_page' as const, + pageId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Publish confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/remove_member_tags.ts b/apps/sim/tools/mailchimp/remove_member_tags.ts new file mode 100644 index 0000000000..578f84d057 --- /dev/null +++ b/apps/sim/tools/mailchimp/remove_member_tags.ts @@ -0,0 +1,113 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpRemoveMemberTags') + +export interface MailchimpRemoveMemberTagsParams { + apiKey: string + listId: string + subscriberEmail: string + tags: string +} + +export interface MailchimpRemoveMemberTagsResponse { + success: boolean + output: { + metadata: { + operation: 'remove_member_tags' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpRemoveMemberTagsTool: ToolConfig< + MailchimpRemoveMemberTagsParams, + MailchimpRemoveMemberTagsResponse +> = { + id: 'mailchimp_remove_member_tags', + name: 'Remove Tags from Member in Mailchimp', + description: 'Remove tags from a member in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + tags: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of tags with inactive status', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/members/${params.subscriberEmail}/tags` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + let tags = [] + try { + tags = JSON.parse(params.tags) + } catch (error) { + logger.warn('Failed to parse tags', { error }) + } + + return { tags } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'remove_member_tags') + } + + return { + success: true, + output: { + metadata: { + operation: 'remove_member_tags' as const, + subscriberHash: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Tag removal confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/remove_segment_member.ts b/apps/sim/tools/mailchimp/remove_segment_member.ts new file mode 100644 index 0000000000..7166f86881 --- /dev/null +++ b/apps/sim/tools/mailchimp/remove_segment_member.ts @@ -0,0 +1,103 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpRemoveSegmentMember') + +export interface MailchimpRemoveSegmentMemberParams { + apiKey: string + listId: string + segmentId: string + subscriberEmail: string +} + +export interface MailchimpRemoveSegmentMemberResponse { + success: boolean + output: { + metadata: { + operation: 'remove_segment_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpRemoveSegmentMemberTool: ToolConfig< + MailchimpRemoveSegmentMemberParams, + MailchimpRemoveSegmentMemberResponse +> = { + id: 'mailchimp_remove_segment_member', + name: 'Remove Member from Segment in Mailchimp', + description: 'Remove a member from a specific segment in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/segments/${params.segmentId}/members/${params.subscriberEmail}` + ), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'remove_segment_member') + } + + return { + success: true, + output: { + metadata: { + operation: 'remove_segment_member' as const, + subscriberHash: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Removal confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/replicate_campaign.ts b/apps/sim/tools/mailchimp/replicate_campaign.ts new file mode 100644 index 0000000000..36f856045c --- /dev/null +++ b/apps/sim/tools/mailchimp/replicate_campaign.ts @@ -0,0 +1,91 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpReplicateCampaign') + +export interface MailchimpReplicateCampaignParams { + apiKey: string + campaignId: string +} + +export interface MailchimpReplicateCampaignResponse { + success: boolean + output: { + campaign: any + metadata: { + operation: 'replicate_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpReplicateCampaignTool: ToolConfig< + MailchimpReplicateCampaignParams, + MailchimpReplicateCampaignResponse +> = { + id: 'mailchimp_replicate_campaign', + name: 'Replicate Campaign in Mailchimp', + description: 'Create a copy of an existing Mailchimp campaign', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign to replicate', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/actions/replicate`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'replicate_campaign') + } + + const data = await response.json() + + return { + success: true, + output: { + campaign: data, + metadata: { + operation: 'replicate_campaign' as const, + campaignId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Replicated campaign data', + properties: { + campaign: { type: 'object', description: 'Replicated campaign object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/schedule_campaign.ts b/apps/sim/tools/mailchimp/schedule_campaign.ts new file mode 100644 index 0000000000..377ac05a39 --- /dev/null +++ b/apps/sim/tools/mailchimp/schedule_campaign.ts @@ -0,0 +1,98 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpScheduleCampaign') + +export interface MailchimpScheduleCampaignParams { + apiKey: string + campaignId: string + scheduleTime: string +} + +export interface MailchimpScheduleCampaignResponse { + success: boolean + output: { + metadata: { + operation: 'schedule_campaign' + campaignId: string + scheduleTime: string + } + success: boolean + } +} + +export const mailchimpScheduleCampaignTool: ToolConfig< + MailchimpScheduleCampaignParams, + MailchimpScheduleCampaignResponse +> = { + id: 'mailchimp_schedule_campaign', + name: 'Schedule Campaign in Mailchimp', + description: 'Schedule a Mailchimp campaign to be sent at a specific time', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign to schedule', + }, + scheduleTime: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'ISO 8601 format date and time', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/actions/schedule`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + schedule_time: params.scheduleTime, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'schedule_campaign') + } + + return { + success: true, + output: { + metadata: { + operation: 'schedule_campaign' as const, + campaignId: '', + scheduleTime: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Schedule confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/send_campaign.ts b/apps/sim/tools/mailchimp/send_campaign.ts new file mode 100644 index 0000000000..f30269e80f --- /dev/null +++ b/apps/sim/tools/mailchimp/send_campaign.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpSendCampaign') + +export interface MailchimpSendCampaignParams { + apiKey: string + campaignId: string +} + +export interface MailchimpSendCampaignResponse { + success: boolean + output: { + metadata: { + operation: 'send_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpSendCampaignTool: ToolConfig< + MailchimpSendCampaignParams, + MailchimpSendCampaignResponse +> = { + id: 'mailchimp_send_campaign', + name: 'Send Campaign in Mailchimp', + description: 'Send a Mailchimp campaign', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign to send', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/actions/send`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'send_campaign') + } + + return { + success: true, + output: { + metadata: { + operation: 'send_campaign' as const, + campaignId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Send confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/set_campaign_content.ts b/apps/sim/tools/mailchimp/set_campaign_content.ts new file mode 100644 index 0000000000..737756c548 --- /dev/null +++ b/apps/sim/tools/mailchimp/set_campaign_content.ts @@ -0,0 +1,120 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpSetCampaignContent') + +export interface MailchimpSetCampaignContentParams { + apiKey: string + campaignId: string + html?: string + plainText?: string + templateId?: string +} + +export interface MailchimpSetCampaignContentResponse { + success: boolean + output: { + content: any + metadata: { + operation: 'set_campaign_content' + campaignId: string + } + success: boolean + } +} + +export const mailchimpSetCampaignContentTool: ToolConfig< + MailchimpSetCampaignContentParams, + MailchimpSetCampaignContentResponse +> = { + id: 'mailchimp_set_campaign_content', + name: 'Set Campaign Content in Mailchimp', + description: 'Set the content for a Mailchimp campaign', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign', + }, + html: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The HTML content for the campaign', + }, + plainText: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The plain-text content for the campaign', + }, + templateId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The ID of the template to use', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/content`), + method: 'PUT', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.html) body.html = params.html + if (params.plainText) body.plain_text = params.plainText + if (params.templateId) body.template = { id: params.templateId } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'set_campaign_content') + } + + const data = await response.json() + + return { + success: true, + output: { + content: data, + metadata: { + operation: 'set_campaign_content' as const, + campaignId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Campaign content data', + properties: { + content: { type: 'object', description: 'Campaign content object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/start_automation.ts b/apps/sim/tools/mailchimp/start_automation.ts new file mode 100644 index 0000000000..4f7fa3133c --- /dev/null +++ b/apps/sim/tools/mailchimp/start_automation.ts @@ -0,0 +1,89 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpStartAutomation') + +export interface MailchimpStartAutomationParams { + apiKey: string + workflowId: string +} + +export interface MailchimpStartAutomationResponse { + success: boolean + output: { + metadata: { + operation: 'start_automation' + workflowId: string + } + success: boolean + } +} + +export const mailchimpStartAutomationTool: ToolConfig< + MailchimpStartAutomationParams, + MailchimpStartAutomationResponse +> = { + id: 'mailchimp_start_automation', + name: 'Start Automation in Mailchimp', + description: 'Start all emails in a Mailchimp automation workflow', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + workflowId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the automation workflow', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/automations/${params.workflowId}/actions/start-all-emails` + ), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'start_automation') + } + + return { + success: true, + output: { + metadata: { + operation: 'start_automation' as const, + workflowId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Start confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/types.ts b/apps/sim/tools/mailchimp/types.ts new file mode 100644 index 0000000000..2348e68639 --- /dev/null +++ b/apps/sim/tools/mailchimp/types.ts @@ -0,0 +1,53 @@ +import { createLogger } from '@/lib/logs/console/logger' + +const logger = createLogger('Mailchimp') + +// Base params +export interface MailchimpBaseParams { + apiKey: string // API key with server prefix (e.g., "key-us19") +} + +export interface MailchimpPaginationParams { + count?: string + offset?: string +} + +export interface MailchimpPagingInfo { + totalItems: number +} + +export interface MailchimpResponse { + success: boolean + output: { + data?: T + paging?: MailchimpPagingInfo + metadata: { + operation: string + [key: string]: any + } + success: boolean + } +} + +// Helper function to extract server prefix from API key +export function extractServerPrefix(apiKey: string): string { + const parts = apiKey.split('-') + if (parts.length < 2) { + throw new Error('Invalid Mailchimp API key format. Expected format: key-dc (e.g., abc123-us19)') + } + return parts[parts.length - 1] +} + +// Helper function to build Mailchimp API URLs +export function buildMailchimpUrl(apiKey: string, path: string): string { + const serverPrefix = extractServerPrefix(apiKey) + return `https://${serverPrefix}.api.mailchimp.com/3.0${path}` +} + +// Helper function for consistent error handling +export function handleMailchimpError(data: any, status: number, operation: string): never { + logger.error(`Mailchimp API request failed for ${operation}`, { data, status }) + + const errorMessage = data.detail || data.title || data.error || data.message || 'Unknown error' + throw new Error(`Mailchimp ${operation} failed: ${errorMessage}`) +} diff --git a/apps/sim/tools/mailchimp/unarchive_member.ts b/apps/sim/tools/mailchimp/unarchive_member.ts new file mode 100644 index 0000000000..da92094208 --- /dev/null +++ b/apps/sim/tools/mailchimp/unarchive_member.ts @@ -0,0 +1,116 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUnarchiveMember') + +export interface MailchimpUnarchiveMemberParams { + apiKey: string + listId: string + subscriberEmail: string + emailAddress: string + status: string +} + +export interface MailchimpUnarchiveMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'unarchive_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpUnarchiveMemberTool: ToolConfig< + MailchimpUnarchiveMemberParams, + MailchimpUnarchiveMemberResponse +> = { + id: 'mailchimp_unarchive_member', + name: 'Unarchive Member in Mailchimp Audience', + description: 'Restore an archived member to a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + emailAddress: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address', + }, + status: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Subscriber status', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members/${params.subscriberEmail}`), + method: 'PUT', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + email_address: params.emailAddress, + status: params.status, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'unarchive_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'unarchive_member' as const, + subscriberHash: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Unarchived member data', + properties: { + member: { type: 'object', description: 'Unarchived member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/unpublish_landing_page.ts b/apps/sim/tools/mailchimp/unpublish_landing_page.ts new file mode 100644 index 0000000000..defa2bdb3f --- /dev/null +++ b/apps/sim/tools/mailchimp/unpublish_landing_page.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUnpublishLandingPage') + +export interface MailchimpUnpublishLandingPageParams { + apiKey: string + pageId: string +} + +export interface MailchimpUnpublishLandingPageResponse { + success: boolean + output: { + metadata: { + operation: 'unpublish_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpUnpublishLandingPageTool: ToolConfig< + MailchimpUnpublishLandingPageParams, + MailchimpUnpublishLandingPageResponse +> = { + id: 'mailchimp_unpublish_landing_page', + name: 'Unpublish Landing Page in Mailchimp', + description: 'Unpublish a landing page in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the landing page', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/landing-pages/${params.pageId}/actions/unpublish`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'unpublish_landing_page') + } + + return { + success: true, + output: { + metadata: { + operation: 'unpublish_landing_page' as const, + pageId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Unpublish confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/unschedule_campaign.ts b/apps/sim/tools/mailchimp/unschedule_campaign.ts new file mode 100644 index 0000000000..20f383a8c5 --- /dev/null +++ b/apps/sim/tools/mailchimp/unschedule_campaign.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUnscheduleCampaign') + +export interface MailchimpUnscheduleCampaignParams { + apiKey: string + campaignId: string +} + +export interface MailchimpUnscheduleCampaignResponse { + success: boolean + output: { + metadata: { + operation: 'unschedule_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpUnscheduleCampaignTool: ToolConfig< + MailchimpUnscheduleCampaignParams, + MailchimpUnscheduleCampaignResponse +> = { + id: 'mailchimp_unschedule_campaign', + name: 'Unschedule Campaign in Mailchimp', + description: 'Unschedule a previously scheduled Mailchimp campaign', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign to unschedule', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}/actions/unschedule`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'unschedule_campaign') + } + + return { + success: true, + output: { + metadata: { + operation: 'unschedule_campaign' as const, + campaignId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Unschedule confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_audience.ts b/apps/sim/tools/mailchimp/update_audience.ts new file mode 100644 index 0000000000..fe7b7a3828 --- /dev/null +++ b/apps/sim/tools/mailchimp/update_audience.ts @@ -0,0 +1,136 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateAudience') + +export interface MailchimpUpdateAudienceParams { + apiKey: string + listId: string + audienceName?: string + permissionReminder?: string + campaignDefaults?: string + emailTypeOption?: string +} + +export interface MailchimpUpdateAudienceResponse { + success: boolean + output: { + list: any + metadata: { + operation: 'update_audience' + listId: string + } + success: boolean + } +} + +export const mailchimpUpdateAudienceTool: ToolConfig< + MailchimpUpdateAudienceParams, + MailchimpUpdateAudienceResponse +> = { + id: 'mailchimp_update_audience', + name: 'Update Audience in Mailchimp', + description: 'Update an existing audience (list) in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + audienceName: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the list', + }, + permissionReminder: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Permission reminder text', + }, + campaignDefaults: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of default campaign settings', + }, + emailTypeOption: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Support multiple email formats', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/lists/${params.listId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.audienceName) body.name = params.audienceName + if (params.permissionReminder) body.permission_reminder = params.permissionReminder + if (params.emailTypeOption !== undefined) + body.email_type_option = params.emailTypeOption === 'true' + + if (params.campaignDefaults) { + try { + body.campaign_defaults = JSON.parse(params.campaignDefaults) + } catch (error) { + logger.warn('Failed to parse campaign defaults', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_audience') + } + + const data = await response.json() + + return { + success: true, + output: { + list: data, + metadata: { + operation: 'update_audience' as const, + listId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated audience data', + properties: { + list: { type: 'object', description: 'Updated audience/list object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_campaign.ts b/apps/sim/tools/mailchimp/update_campaign.ts new file mode 100644 index 0000000000..246c9d91fe --- /dev/null +++ b/apps/sim/tools/mailchimp/update_campaign.ts @@ -0,0 +1,125 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateCampaign') + +export interface MailchimpUpdateCampaignParams { + apiKey: string + campaignId: string + campaignSettings?: string + recipients?: string +} + +export interface MailchimpUpdateCampaignResponse { + success: boolean + output: { + campaign: any + metadata: { + operation: 'update_campaign' + campaignId: string + } + success: boolean + } +} + +export const mailchimpUpdateCampaignTool: ToolConfig< + MailchimpUpdateCampaignParams, + MailchimpUpdateCampaignResponse +> = { + id: 'mailchimp_update_campaign', + name: 'Update Campaign in Mailchimp', + description: 'Update an existing campaign in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + campaignId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the campaign', + }, + campaignSettings: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of campaign settings', + }, + recipients: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of recipients', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/campaigns/${params.campaignId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.campaignSettings) { + try { + body.settings = JSON.parse(params.campaignSettings) + } catch (error) { + logger.warn('Failed to parse campaign settings', { error }) + } + } + + if (params.recipients) { + try { + body.recipients = JSON.parse(params.recipients) + } catch (error) { + logger.warn('Failed to parse recipients', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_campaign') + } + + const data = await response.json() + + return { + success: true, + output: { + campaign: data, + metadata: { + operation: 'update_campaign' as const, + campaignId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated campaign data', + properties: { + campaign: { type: 'object', description: 'Updated campaign object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_interest.ts b/apps/sim/tools/mailchimp/update_interest.ts new file mode 100644 index 0000000000..ba9d03016c --- /dev/null +++ b/apps/sim/tools/mailchimp/update_interest.ts @@ -0,0 +1,122 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateInterest') + +export interface MailchimpUpdateInterestParams { + apiKey: string + listId: string + interestCategoryId: string + interestId: string + interestName?: string +} + +export interface MailchimpUpdateInterestResponse { + success: boolean + output: { + interest: any + metadata: { + operation: 'update_interest' + interestId: string + } + success: boolean + } +} + +export const mailchimpUpdateInterestTool: ToolConfig< + MailchimpUpdateInterestParams, + MailchimpUpdateInterestResponse +> = { + id: 'mailchimp_update_interest', + name: 'Update Interest in Mailchimp Interest Category', + description: 'Update an existing interest in an interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + interestId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest', + }, + interestName: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the interest', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}/interests/${params.interestId}` + ), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.interestName) body.name = params.interestName + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_interest') + } + + const data = await response.json() + + return { + success: true, + output: { + interest: data, + metadata: { + operation: 'update_interest' as const, + interestId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated interest data', + properties: { + interest: { type: 'object', description: 'Updated interest object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_interest_category.ts b/apps/sim/tools/mailchimp/update_interest_category.ts new file mode 100644 index 0000000000..b21fa61226 --- /dev/null +++ b/apps/sim/tools/mailchimp/update_interest_category.ts @@ -0,0 +1,115 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateInterestCategory') + +export interface MailchimpUpdateInterestCategoryParams { + apiKey: string + listId: string + interestCategoryId: string + interestCategoryTitle?: string +} + +export interface MailchimpUpdateInterestCategoryResponse { + success: boolean + output: { + category: any + metadata: { + operation: 'update_interest_category' + interestCategoryId: string + } + success: boolean + } +} + +export const mailchimpUpdateInterestCategoryTool: ToolConfig< + MailchimpUpdateInterestCategoryParams, + MailchimpUpdateInterestCategoryResponse +> = { + id: 'mailchimp_update_interest_category', + name: 'Update Interest Category in Mailchimp Audience', + description: 'Update an existing interest category in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + interestCategoryId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the interest category', + }, + interestCategoryTitle: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The title of the interest category', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl( + params.apiKey, + `/lists/${params.listId}/interest-categories/${params.interestCategoryId}` + ), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.interestCategoryTitle) body.title = params.interestCategoryTitle + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_interest_category') + } + + const data = await response.json() + + return { + success: true, + output: { + category: data, + metadata: { + operation: 'update_interest_category' as const, + interestCategoryId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated interest category data', + properties: { + category: { type: 'object', description: 'Updated interest category object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_landing_page.ts b/apps/sim/tools/mailchimp/update_landing_page.ts new file mode 100644 index 0000000000..8d002653e7 --- /dev/null +++ b/apps/sim/tools/mailchimp/update_landing_page.ts @@ -0,0 +1,104 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateLandingPage') + +export interface MailchimpUpdateLandingPageParams { + apiKey: string + pageId: string + landingPageTitle?: string +} + +export interface MailchimpUpdateLandingPageResponse { + success: boolean + output: { + landingPage: any + metadata: { + operation: 'update_landing_page' + pageId: string + } + success: boolean + } +} + +export const mailchimpUpdateLandingPageTool: ToolConfig< + MailchimpUpdateLandingPageParams, + MailchimpUpdateLandingPageResponse +> = { + id: 'mailchimp_update_landing_page', + name: 'Update Landing Page in Mailchimp', + description: 'Update an existing landing page in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the landing page', + }, + landingPageTitle: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The title of the landing page', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/landing-pages/${params.pageId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.landingPageTitle) body.title = params.landingPageTitle + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_landing_page') + } + + const data = await response.json() + + return { + success: true, + output: { + landingPage: data, + metadata: { + operation: 'update_landing_page' as const, + pageId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated landing page data', + properties: { + landingPage: { type: 'object', description: 'Updated landing page object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_member.ts b/apps/sim/tools/mailchimp/update_member.ts new file mode 100644 index 0000000000..0483f8bf92 --- /dev/null +++ b/apps/sim/tools/mailchimp/update_member.ts @@ -0,0 +1,150 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateMember') + +export interface MailchimpUpdateMemberParams { + apiKey: string + listId: string + subscriberEmail: string + emailAddress?: string + status?: string + mergeFields?: string + interests?: string +} + +export interface MailchimpUpdateMemberResponse { + success: boolean + output: { + member: any + metadata: { + operation: 'update_member' + subscriberHash: string + } + success: boolean + } +} + +export const mailchimpUpdateMemberTool: ToolConfig< + MailchimpUpdateMemberParams, + MailchimpUpdateMemberResponse +> = { + id: 'mailchimp_update_member', + name: 'Update Member in Mailchimp Audience', + description: 'Update an existing member in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + subscriberEmail: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Member email address or MD5 hash', + }, + emailAddress: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Member email address', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Subscriber status', + }, + mergeFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of merge fields', + }, + interests: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of interests', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/members/${params.subscriberEmail}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.emailAddress) body.email_address = params.emailAddress + if (params.status) body.status = params.status + + if (params.mergeFields) { + try { + body.merge_fields = JSON.parse(params.mergeFields) + } catch (error) { + logger.warn('Failed to parse merge fields', { error }) + } + } + + if (params.interests) { + try { + body.interests = JSON.parse(params.interests) + } catch (error) { + logger.warn('Failed to parse interests', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_member') + } + + const data = await response.json() + + return { + success: true, + output: { + member: data, + metadata: { + operation: 'update_member' as const, + subscriberHash: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated member data', + properties: { + member: { type: 'object', description: 'Updated member object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_merge_field.ts b/apps/sim/tools/mailchimp/update_merge_field.ts new file mode 100644 index 0000000000..1b25460769 --- /dev/null +++ b/apps/sim/tools/mailchimp/update_merge_field.ts @@ -0,0 +1,112 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateMergeField') + +export interface MailchimpUpdateMergeFieldParams { + apiKey: string + listId: string + mergeId: string + mergeName?: string +} + +export interface MailchimpUpdateMergeFieldResponse { + success: boolean + output: { + mergeField: any + metadata: { + operation: 'update_merge_field' + mergeId: string + } + success: boolean + } +} + +export const mailchimpUpdateMergeFieldTool: ToolConfig< + MailchimpUpdateMergeFieldParams, + MailchimpUpdateMergeFieldResponse +> = { + id: 'mailchimp_update_merge_field', + name: 'Update Merge Field in Mailchimp Audience', + description: 'Update an existing merge field in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + mergeId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the merge field', + }, + mergeName: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the merge field', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/merge-fields/${params.mergeId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.mergeName) body.name = params.mergeName + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_merge_field') + } + + const data = await response.json() + + return { + success: true, + output: { + mergeField: data, + metadata: { + operation: 'update_merge_field' as const, + mergeId: data.merge_id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated merge field data', + properties: { + mergeField: { type: 'object', description: 'Updated merge field object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_segment.ts b/apps/sim/tools/mailchimp/update_segment.ts new file mode 100644 index 0000000000..90d237514c --- /dev/null +++ b/apps/sim/tools/mailchimp/update_segment.ts @@ -0,0 +1,128 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateSegment') + +export interface MailchimpUpdateSegmentParams { + apiKey: string + listId: string + segmentId: string + segmentName?: string + segmentOptions?: string +} + +export interface MailchimpUpdateSegmentResponse { + success: boolean + output: { + segment: any + metadata: { + operation: 'update_segment' + segmentId: string + } + success: boolean + } +} + +export const mailchimpUpdateSegmentTool: ToolConfig< + MailchimpUpdateSegmentParams, + MailchimpUpdateSegmentResponse +> = { + id: 'mailchimp_update_segment', + name: 'Update Segment in Mailchimp Audience', + description: 'Update an existing segment in a Mailchimp audience', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the list', + }, + segmentId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the segment', + }, + segmentName: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the segment', + }, + segmentOptions: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'JSON object of segment options', + }, + }, + + request: { + url: (params) => + buildMailchimpUrl(params.apiKey, `/lists/${params.listId}/segments/${params.segmentId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.segmentName) body.name = params.segmentName + + if (params.segmentOptions) { + try { + const options = JSON.parse(params.segmentOptions) + body.options = options + } catch (error) { + logger.warn('Failed to parse segment options', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_segment') + } + + const data = await response.json() + + return { + success: true, + output: { + segment: data, + metadata: { + operation: 'update_segment' as const, + segmentId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated segment data', + properties: { + segment: { type: 'object', description: 'Updated segment object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/mailchimp/update_template.ts b/apps/sim/tools/mailchimp/update_template.ts new file mode 100644 index 0000000000..3e7ea3fbba --- /dev/null +++ b/apps/sim/tools/mailchimp/update_template.ts @@ -0,0 +1,112 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildMailchimpUrl, handleMailchimpError } from './types' + +const logger = createLogger('MailchimpUpdateTemplate') + +export interface MailchimpUpdateTemplateParams { + apiKey: string + templateId: string + templateName?: string + templateHtml?: string +} + +export interface MailchimpUpdateTemplateResponse { + success: boolean + output: { + template: any + metadata: { + operation: 'update_template' + templateId: string + } + success: boolean + } +} + +export const mailchimpUpdateTemplateTool: ToolConfig< + MailchimpUpdateTemplateParams, + MailchimpUpdateTemplateResponse +> = { + id: 'mailchimp_update_template', + name: 'Update Template in Mailchimp', + description: 'Update an existing template in Mailchimp', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Mailchimp API key with server prefix', + }, + templateId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The unique ID for the template', + }, + templateName: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The name of the template', + }, + templateHtml: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'The HTML content for the template', + }, + }, + + request: { + url: (params) => buildMailchimpUrl(params.apiKey, `/templates/${params.templateId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.templateName) body.name = params.templateName + if (params.templateHtml) body.html = params.templateHtml + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleMailchimpError(data, response.status, 'update_template') + } + + const data = await response.json() + + return { + success: true, + output: { + template: data, + metadata: { + operation: 'update_template' as const, + templateId: data.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated template data', + properties: { + template: { type: 'object', description: 'Updated template object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/bulk_update_accounts.ts b/apps/sim/tools/pylon/bulk_update_accounts.ts new file mode 100644 index 0000000000..817b1658f4 --- /dev/null +++ b/apps/sim/tools/pylon/bulk_update_accounts.ts @@ -0,0 +1,140 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonBulkUpdateAccounts') + +export interface PylonBulkUpdateAccountsParams { + apiToken: string + accountIds: string + customFields?: string + tags?: string + ownerId?: string + tagsApplyMode?: string +} + +export interface PylonBulkUpdateAccountsResponse { + success: boolean + output: { + accounts: any[] + metadata: { + operation: 'bulk_update_accounts' + totalUpdated: number + } + success: boolean + } +} + +export const pylonBulkUpdateAccountsTool: ToolConfig< + PylonBulkUpdateAccountsParams, + PylonBulkUpdateAccountsResponse +> = { + id: 'pylon_bulk_update_accounts', + name: 'Bulk Update Accounts in Pylon', + description: 'Update multiple accounts at once', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + accountIds: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Comma-separated account IDs to update', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tag IDs', + }, + ownerId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Owner user ID', + }, + tagsApplyMode: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Tag application mode: append_only, remove_only, or replace', + }, + }, + + request: { + url: () => buildPylonUrl('/accounts'), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + account_ids: params.accountIds.split(',').map((id) => id.trim()), + } + + if (params.ownerId) body.owner_id = params.ownerId + if (params.tagsApplyMode) body.tags_apply_mode = params.tagsApplyMode + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + if (params.tags) { + body.tags = params.tags.split(',').map((t) => t.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'bulk_update_accounts') + } + + const data = await response.json() + + return { + success: true, + output: { + accounts: data.data || [], + metadata: { + operation: 'bulk_update_accounts' as const, + totalUpdated: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk updated accounts data', + properties: { + accounts: { type: 'array', description: 'Array of updated account objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/create_account.ts b/apps/sim/tools/pylon/create_account.ts new file mode 100644 index 0000000000..a87c9fa419 --- /dev/null +++ b/apps/sim/tools/pylon/create_account.ts @@ -0,0 +1,191 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonCreateAccount') + +export interface PylonCreateAccountParams { + apiToken: string + name: string + domains?: string + primaryDomain?: string + customFields?: string + tags?: string + channels?: string + externalIds?: string + ownerId?: string + logoUrl?: string + subaccountIds?: string +} + +export interface PylonCreateAccountResponse { + success: boolean + output: { + account: any + metadata: { + operation: 'create_account' + accountId: string + } + success: boolean + } +} + +export const pylonCreateAccountTool: ToolConfig< + PylonCreateAccountParams, + PylonCreateAccountResponse +> = { + id: 'pylon_create_account', + name: 'Create Account in Pylon', + description: 'Create a new account with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + name: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Account name', + }, + domains: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated list of domains', + }, + primaryDomain: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Primary domain for the account', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tag IDs', + }, + channels: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated channel IDs', + }, + externalIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated external IDs', + }, + ownerId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Owner user ID', + }, + logoUrl: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'URL to account logo', + }, + subaccountIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated subaccount IDs', + }, + }, + + request: { + url: () => buildPylonUrl('/accounts'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + name: params.name, + } + + if (params.domains) { + body.domains = params.domains.split(',').map((d) => d.trim()) + } + if (params.primaryDomain) body.primary_domain = params.primaryDomain + if (params.ownerId) body.owner_id = params.ownerId + if (params.logoUrl) body.logo_url = params.logoUrl + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + if (params.tags) { + body.tags = params.tags.split(',').map((t) => t.trim()) + } + + if (params.channels) { + body.channels = params.channels.split(',').map((c) => c.trim()) + } + + if (params.externalIds) { + body.external_ids = params.externalIds.split(',').map((e) => e.trim()) + } + + if (params.subaccountIds) { + body.subaccount_ids = params.subaccountIds.split(',').map((s) => s.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'create_account') + } + + const data = await response.json() + + return { + success: true, + output: { + account: data.data, + metadata: { + operation: 'create_account' as const, + accountId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created account data', + properties: { + account: { type: 'object', description: 'Created account object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/create_contact.ts b/apps/sim/tools/pylon/create_contact.ts new file mode 100644 index 0000000000..ebe0bff34a --- /dev/null +++ b/apps/sim/tools/pylon/create_contact.ts @@ -0,0 +1,153 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonCreateContact') + +export interface PylonCreateContactParams { + apiToken: string + name: string + email?: string + accountId?: string + accountExternalId?: string + avatarUrl?: string + customFields?: string + portalRole?: string +} + +export interface PylonCreateContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'create_contact' + contactId: string + } + success: boolean + } +} + +export const pylonCreateContactTool: ToolConfig< + PylonCreateContactParams, + PylonCreateContactResponse +> = { + id: 'pylon_create_contact', + name: 'Create Contact in Pylon', + description: 'Create a new contact with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + name: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact name', + }, + email: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Contact email address', + }, + accountId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Account ID to associate with contact', + }, + accountExternalId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'External account ID to associate with contact', + }, + avatarUrl: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'URL for contact avatar image', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + portalRole: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Portal role for the contact', + }, + }, + + request: { + url: () => buildPylonUrl('/contacts'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + name: params.name, + } + + if (params.email) body.email = params.email + if (params.accountId) body.account_id = params.accountId + if (params.accountExternalId) body.account_external_id = params.accountExternalId + if (params.avatarUrl) body.avatar_url = params.avatarUrl + if (params.portalRole) body.portal_role = params.portalRole + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'create_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data.data, + metadata: { + operation: 'create_contact' as const, + contactId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created contact data', + properties: { + contact: { type: 'object', description: 'Created contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/create_issue.ts b/apps/sim/tools/pylon/create_issue.ts new file mode 100644 index 0000000000..b1da5ef815 --- /dev/null +++ b/apps/sim/tools/pylon/create_issue.ts @@ -0,0 +1,188 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonCreateIssue') + +export interface PylonCreateIssueParams { + apiToken: string + title: string + bodyHtml: string + accountId?: string + assigneeId?: string + teamId?: string + requesterId?: string + requesterEmail?: string + priority?: string + tags?: string + customFields?: string + attachmentUrls?: string +} + +export interface PylonCreateIssueResponse { + success: boolean + output: { + issue: any + metadata: { + operation: 'create_issue' + issueId: string + } + success: boolean + } +} + +export const pylonCreateIssueTool: ToolConfig = { + id: 'pylon_create_issue', + name: 'Create Issue in Pylon', + description: 'Create a new issue with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + title: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Issue title', + }, + bodyHtml: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Issue body in HTML format', + }, + accountId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Account ID to associate with issue', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User ID to assign issue to', + }, + teamId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Team ID to assign issue to', + }, + requesterId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Requester user ID (alternative to requester_email)', + }, + requesterEmail: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Requester email address (alternative to requester_id)', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Issue priority', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tag IDs', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + attachmentUrls: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated attachment URLs', + }, + }, + + request: { + url: () => buildPylonUrl('/issues'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + title: params.title, + body_html: params.bodyHtml, + } + + if (params.accountId) body.account_id = params.accountId + if (params.assigneeId) body.assignee_id = params.assigneeId + if (params.teamId) body.team_id = params.teamId + if (params.requesterId) body.requester_id = params.requesterId + if (params.requesterEmail) body.requester_email = params.requesterEmail + if (params.priority) body.priority = params.priority + + if (params.tags) { + body.tags = params.tags.split(',').map((t) => t.trim()) + } + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + if (params.attachmentUrls) { + body.attachment_urls = params.attachmentUrls.split(',').map((url) => url.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'create_issue') + } + + const data = await response.json() + + return { + success: true, + output: { + issue: data.data, + metadata: { + operation: 'create_issue' as const, + issueId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created issue data', + properties: { + issue: { type: 'object', description: 'Created issue object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/create_tag.ts b/apps/sim/tools/pylon/create_tag.ts new file mode 100644 index 0000000000..059e4c5d9c --- /dev/null +++ b/apps/sim/tools/pylon/create_tag.ts @@ -0,0 +1,111 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonCreateTag') + +export interface PylonCreateTagParams { + apiToken: string + objectType: string + value: string + hexColor?: string +} + +export interface PylonCreateTagResponse { + success: boolean + output: { + tag: any + metadata: { + operation: 'create_tag' + tagId: string + } + success: boolean + } +} + +export const pylonCreateTagTool: ToolConfig = { + id: 'pylon_create_tag', + name: 'Create Tag in Pylon', + description: 'Create a new tag with specified properties (objectType: account/issue/contact)', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + objectType: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Object type for tag (account, issue, or contact)', + }, + value: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tag value/name', + }, + hexColor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Hex color code for tag (e.g., #FF5733)', + }, + }, + + request: { + url: () => buildPylonUrl('/tags'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = { + object_type: params.objectType, + value: params.value, + } + + if (params.hexColor) body.hex_color = params.hexColor + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'create_tag') + } + + const data = await response.json() + + return { + success: true, + output: { + tag: data.data, + metadata: { + operation: 'create_tag' as const, + tagId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created tag data', + properties: { + tag: { type: 'object', description: 'Created tag object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/create_team.ts b/apps/sim/tools/pylon/create_team.ts new file mode 100644 index 0000000000..258386d304 --- /dev/null +++ b/apps/sim/tools/pylon/create_team.ts @@ -0,0 +1,105 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonCreateTeam') + +export interface PylonCreateTeamParams { + apiToken: string + name?: string + userIds?: string +} + +export interface PylonCreateTeamResponse { + success: boolean + output: { + team: any + metadata: { + operation: 'create_team' + teamId: string + } + success: boolean + } +} + +export const pylonCreateTeamTool: ToolConfig = { + id: 'pylon_create_team', + name: 'Create Team in Pylon', + description: 'Create a new team with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Team name', + }, + userIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated user IDs to add as team members', + }, + }, + + request: { + url: () => buildPylonUrl('/teams'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.name) body.name = params.name + + if (params.userIds) { + body.user_ids = params.userIds.split(',').map((id) => id.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'create_team') + } + + const data = await response.json() + + return { + success: true, + output: { + team: data.data, + metadata: { + operation: 'create_team' as const, + teamId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created team data', + properties: { + team: { type: 'object', description: 'Created team object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/delete_account.ts b/apps/sim/tools/pylon/delete_account.ts new file mode 100644 index 0000000000..62fd0f890d --- /dev/null +++ b/apps/sim/tools/pylon/delete_account.ts @@ -0,0 +1,84 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonDeleteAccount') + +export interface PylonDeleteAccountParams { + apiToken: string + accountId: string +} + +export interface PylonDeleteAccountResponse { + success: boolean + output: { + metadata: { + operation: 'delete_account' + accountId: string + } + success: boolean + } +} + +export const pylonDeleteAccountTool: ToolConfig< + PylonDeleteAccountParams, + PylonDeleteAccountResponse +> = { + id: 'pylon_delete_account', + name: 'Delete Account from Pylon', + description: 'Remove an account by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + accountId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Account ID to delete', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/accounts/${params.accountId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'delete_account') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_account' as const, + accountId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion confirmation', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/delete_contact.ts b/apps/sim/tools/pylon/delete_contact.ts new file mode 100644 index 0000000000..7b97021808 --- /dev/null +++ b/apps/sim/tools/pylon/delete_contact.ts @@ -0,0 +1,84 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonDeleteContact') + +export interface PylonDeleteContactParams { + apiToken: string + contactId: string +} + +export interface PylonDeleteContactResponse { + success: boolean + output: { + metadata: { + operation: 'delete_contact' + contactId: string + } + success: boolean + } +} + +export const pylonDeleteContactTool: ToolConfig< + PylonDeleteContactParams, + PylonDeleteContactResponse +> = { + id: 'pylon_delete_contact', + name: 'Delete Contact in Pylon', + description: 'Delete a specific contact by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to delete', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/contacts/${params.contactId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'delete_contact') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_contact' as const, + contactId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Delete operation result', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/delete_issue.ts b/apps/sim/tools/pylon/delete_issue.ts new file mode 100644 index 0000000000..caff3f6b0c --- /dev/null +++ b/apps/sim/tools/pylon/delete_issue.ts @@ -0,0 +1,81 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonDeleteIssue') + +export interface PylonDeleteIssueParams { + apiToken: string + issueId: string +} + +export interface PylonDeleteIssueResponse { + success: boolean + output: { + metadata: { + operation: 'delete_issue' + issueId: string + } + success: boolean + } +} + +export const pylonDeleteIssueTool: ToolConfig = { + id: 'pylon_delete_issue', + name: 'Delete Issue from Pylon', + description: 'Remove an issue by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue to delete', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params.issueId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response, params?: PylonDeleteIssueParams) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'delete_issue') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_issue' as const, + issueId: params?.issueId || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deletion result', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/delete_tag.ts b/apps/sim/tools/pylon/delete_tag.ts new file mode 100644 index 0000000000..4ee8eac25b --- /dev/null +++ b/apps/sim/tools/pylon/delete_tag.ts @@ -0,0 +1,81 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonDeleteTag') + +export interface PylonDeleteTagParams { + apiToken: string + tagId: string +} + +export interface PylonDeleteTagResponse { + success: boolean + output: { + metadata: { + operation: 'delete_tag' + tagId: string + } + success: boolean + } +} + +export const pylonDeleteTagTool: ToolConfig = { + id: 'pylon_delete_tag', + name: 'Delete Tag in Pylon', + description: 'Delete a specific tag by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tag ID to delete', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/tags/${params.tagId}`), + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'delete_tag') + } + + return { + success: true, + output: { + metadata: { + operation: 'delete_tag' as const, + tagId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Delete operation result', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_account.ts b/apps/sim/tools/pylon/get_account.ts new file mode 100644 index 0000000000..34cfcf1666 --- /dev/null +++ b/apps/sim/tools/pylon/get_account.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetAccount') + +export interface PylonGetAccountParams { + apiToken: string + accountId: string +} + +export interface PylonGetAccountResponse { + success: boolean + output: { + account: any + metadata: { + operation: 'get_account' + accountId: string + } + success: boolean + } +} + +export const pylonGetAccountTool: ToolConfig = { + id: 'pylon_get_account', + name: 'Get Account from Pylon', + description: 'Retrieve a single account by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + accountId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Account ID to retrieve', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/accounts/${params.accountId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_account') + } + + const data = await response.json() + + return { + success: true, + output: { + account: data.data, + metadata: { + operation: 'get_account' as const, + accountId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Account data', + properties: { + account: { type: 'object', description: 'Account object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_contact.ts b/apps/sim/tools/pylon/get_contact.ts new file mode 100644 index 0000000000..a3119ac2d3 --- /dev/null +++ b/apps/sim/tools/pylon/get_contact.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetContact') + +export interface PylonGetContactParams { + apiToken: string + contactId: string + cursor?: string + limit?: string +} + +export interface PylonGetContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'get_contact' + contactId: string + } + success: boolean + } +} + +export const pylonGetContactTool: ToolConfig = { + id: 'pylon_get_contact', + name: 'Get Contact in Pylon', + description: 'Retrieve a specific contact by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to retrieve', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Maximum number of items to return', + }, + }, + + request: { + url: (params) => { + const url = new URL(buildPylonUrl(`/contacts/${params.contactId}`)) + if (params.cursor) { + url.searchParams.append('cursor', params.cursor) + } + if (params.limit) { + url.searchParams.append('limit', params.limit) + } + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data.data, + metadata: { + operation: 'get_contact' as const, + contactId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Contact data', + properties: { + contact: { type: 'object', description: 'Contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_issue.ts b/apps/sim/tools/pylon/get_issue.ts new file mode 100644 index 0000000000..9316f15fcd --- /dev/null +++ b/apps/sim/tools/pylon/get_issue.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetIssue') + +export interface PylonGetIssueParams { + apiToken: string + issueId: string +} + +export interface PylonGetIssueResponse { + success: boolean + output: { + issue: any + metadata: { + operation: 'get_issue' + issueId: string + } + success: boolean + } +} + +export const pylonGetIssueTool: ToolConfig = { + id: 'pylon_get_issue', + name: 'Get Issue from Pylon', + description: 'Fetch a specific issue by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue to retrieve', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params.issueId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_issue') + } + + const data = await response.json() + + return { + success: true, + output: { + issue: data.data, + metadata: { + operation: 'get_issue' as const, + issueId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Issue data', + properties: { + issue: { type: 'object', description: 'Issue object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_tag.ts b/apps/sim/tools/pylon/get_tag.ts new file mode 100644 index 0000000000..093b8e5c2b --- /dev/null +++ b/apps/sim/tools/pylon/get_tag.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetTag') + +export interface PylonGetTagParams { + apiToken: string + tagId: string +} + +export interface PylonGetTagResponse { + success: boolean + output: { + tag: any + metadata: { + operation: 'get_tag' + tagId: string + } + success: boolean + } +} + +export const pylonGetTagTool: ToolConfig = { + id: 'pylon_get_tag', + name: 'Get Tag in Pylon', + description: 'Retrieve a specific tag by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tag ID to retrieve', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/tags/${params.tagId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_tag') + } + + const data = await response.json() + + return { + success: true, + output: { + tag: data.data, + metadata: { + operation: 'get_tag' as const, + tagId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Tag data', + properties: { + tag: { type: 'object', description: 'Tag object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_team.ts b/apps/sim/tools/pylon/get_team.ts new file mode 100644 index 0000000000..24bc1af392 --- /dev/null +++ b/apps/sim/tools/pylon/get_team.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetTeam') + +export interface PylonGetTeamParams { + apiToken: string + teamId: string +} + +export interface PylonGetTeamResponse { + success: boolean + output: { + team: any + metadata: { + operation: 'get_team' + teamId: string + } + success: boolean + } +} + +export const pylonGetTeamTool: ToolConfig = { + id: 'pylon_get_team', + name: 'Get Team in Pylon', + description: 'Retrieve a specific team by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + teamId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Team ID to retrieve', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/teams/${params.teamId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_team') + } + + const data = await response.json() + + return { + success: true, + output: { + team: data.data, + metadata: { + operation: 'get_team' as const, + teamId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Team data', + properties: { + team: { type: 'object', description: 'Team object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/get_user.ts b/apps/sim/tools/pylon/get_user.ts new file mode 100644 index 0000000000..579b953a67 --- /dev/null +++ b/apps/sim/tools/pylon/get_user.ts @@ -0,0 +1,86 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonGetUser') + +export interface PylonGetUserParams { + apiToken: string + userId: string +} + +export interface PylonGetUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'get_user' + userId: string + } + success: boolean + } +} + +export const pylonGetUserTool: ToolConfig = { + id: 'pylon_get_user', + name: 'Get User in Pylon', + description: 'Retrieve a specific user by ID', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User ID to retrieve', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/users/${params.userId}`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'get_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.data, + metadata: { + operation: 'get_user' as const, + userId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'User data', + properties: { + user: { type: 'object', description: 'User object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/index.ts b/apps/sim/tools/pylon/index.ts new file mode 100644 index 0000000000..e58ca95e95 --- /dev/null +++ b/apps/sim/tools/pylon/index.ts @@ -0,0 +1,47 @@ +// Pylon API integration tools +// Export all tools for registration + +export { pylonBulkUpdateAccountsTool } from './bulk_update_accounts' +export { pylonCreateAccountTool } from './create_account' +export { pylonCreateContactTool } from './create_contact' +export { pylonCreateIssueTool } from './create_issue' +export { pylonCreateTagTool } from './create_tag' +export { pylonCreateTeamTool } from './create_team' +export { pylonDeleteAccountTool } from './delete_account' +export { pylonDeleteContactTool } from './delete_contact' +export { pylonDeleteIssueTool } from './delete_issue' +export { pylonDeleteTagTool } from './delete_tag' +export { pylonGetAccountTool } from './get_account' +export { pylonGetContactTool } from './get_contact' +export { pylonGetIssueTool } from './get_issue' +export { pylonGetTagTool } from './get_tag' +export { pylonGetTeamTool } from './get_team' +export { pylonGetUserTool } from './get_user' +export { pylonLinkExternalIssueTool } from './link_external_issue' +// Account tools +export { pylonListAccountsTool } from './list_accounts' +// Contact tools +export { pylonListContactsTool } from './list_contacts' +export { pylonListIssueFollowersTool } from './list_issue_followers' +// Issue tools +export { pylonListIssuesTool } from './list_issues' +// Tag tools +export { pylonListTagsTool } from './list_tags' +// Team tools +export { pylonListTeamsTool } from './list_teams' +// User tools +export { pylonListUsersTool } from './list_users' +export { pylonManageIssueFollowersTool } from './manage_issue_followers' +// Message tools +export { pylonRedactMessageTool } from './redact_message' +export { pylonSearchAccountsTool } from './search_accounts' +export { pylonSearchContactsTool } from './search_contacts' +export { pylonSearchIssuesTool } from './search_issues' +export { pylonSearchUsersTool } from './search_users' +export { pylonSnoozeIssueTool } from './snooze_issue' +export { pylonUpdateAccountTool } from './update_account' +export { pylonUpdateContactTool } from './update_contact' +export { pylonUpdateIssueTool } from './update_issue' +export { pylonUpdateTagTool } from './update_tag' +export { pylonUpdateTeamTool } from './update_team' +export { pylonUpdateUserTool } from './update_user' diff --git a/apps/sim/tools/pylon/link_external_issue.ts b/apps/sim/tools/pylon/link_external_issue.ts new file mode 100644 index 0000000000..b69f1ed4ca --- /dev/null +++ b/apps/sim/tools/pylon/link_external_issue.ts @@ -0,0 +1,112 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonLinkExternalIssue') + +export interface PylonLinkExternalIssueParams { + apiToken: string + issueId: string + externalIssueId: string + source: string +} + +export interface PylonLinkExternalIssueResponse { + success: boolean + output: { + externalIssue: any + metadata: { + operation: 'link_external_issue' + issueId: string + externalIssueId: string + source: string + } + success: boolean + } +} + +export const pylonLinkExternalIssueTool: ToolConfig< + PylonLinkExternalIssueParams, + PylonLinkExternalIssueResponse +> = { + id: 'pylon_link_external_issue', + name: 'Link External Issue in Pylon', + description: 'Link an issue to an external system issue', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the Pylon issue', + }, + externalIssueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the external issue', + }, + source: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The source system (e.g., "jira", "linear", "github")', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params?.issueId || ''}/external-issues`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + external_issue_id: params?.externalIssueId || '', + source: params?.source || '', + }), + }, + + transformResponse: async (response: Response, params?: PylonLinkExternalIssueParams) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'link_external_issue') + } + + const data = await response.json() + + return { + success: true, + output: { + externalIssue: data.data, + metadata: { + operation: 'link_external_issue' as const, + issueId: params?.issueId || '', + externalIssueId: params?.externalIssueId || '', + source: params?.source || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Linked external issue data', + properties: { + externalIssue: { type: 'object', description: 'External issue link object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/list_accounts.ts b/apps/sim/tools/pylon/list_accounts.ts new file mode 100644 index 0000000000..06af15feb4 --- /dev/null +++ b/apps/sim/tools/pylon/list_accounts.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListAccounts') + +export interface PylonListAccountsParams { + apiToken: string + limit?: string + cursor?: string +} + +export interface PylonListAccountsResponse { + success: boolean + output: { + accounts: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'list_accounts' + totalReturned: number + } + success: boolean + } +} + +export const pylonListAccountsTool: ToolConfig = + { + id: 'pylon_list_accounts', + name: 'List Accounts in Pylon', + description: 'Retrieve a paginated list of accounts', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of accounts to return (1-1000, default 100)', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + }, + + request: { + url: (params) => { + const url = new URL(buildPylonUrl('/accounts')) + if (params.limit) { + url.searchParams.append('limit', params.limit) + } + if (params.cursor) { + url.searchParams.append('cursor', params.cursor) + } + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_accounts') + } + + const data = await response.json() + + return { + success: true, + output: { + accounts: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'list_accounts' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of accounts', + properties: { + accounts: { type: 'array', description: 'Array of account objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/pylon/list_contacts.ts b/apps/sim/tools/pylon/list_contacts.ts new file mode 100644 index 0000000000..f09a2152ac --- /dev/null +++ b/apps/sim/tools/pylon/list_contacts.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListContacts') + +export interface PylonListContactsParams { + apiToken: string + cursor?: string + limit?: string +} + +export interface PylonListContactsResponse { + success: boolean + output: { + contacts: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'list_contacts' + totalReturned: number + } + success: boolean + } +} + +export const pylonListContactsTool: ToolConfig = + { + id: 'pylon_list_contacts', + name: 'List Contacts in Pylon', + description: 'Retrieve a list of contacts', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Maximum number of contacts to return', + }, + }, + + request: { + url: (params) => { + const url = new URL(buildPylonUrl('/contacts')) + if (params.cursor) { + url.searchParams.append('cursor', params.cursor) + } + if (params.limit) { + url.searchParams.append('limit', params.limit) + } + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_contacts') + } + + const data = await response.json() + + return { + success: true, + output: { + contacts: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'list_contacts' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of contacts', + properties: { + contacts: { type: 'array', description: 'Array of contact objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/pylon/list_issue_followers.ts b/apps/sim/tools/pylon/list_issue_followers.ts new file mode 100644 index 0000000000..5fbf42d6b3 --- /dev/null +++ b/apps/sim/tools/pylon/list_issue_followers.ts @@ -0,0 +1,91 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListIssueFollowers') + +export interface PylonListIssueFollowersParams { + apiToken: string + issueId: string +} + +export interface PylonListIssueFollowersResponse { + success: boolean + output: { + followers: any[] + metadata: { + operation: 'list_issue_followers' + issueId: string + totalFollowers: number + } + success: boolean + } +} + +export const pylonListIssueFollowersTool: ToolConfig< + PylonListIssueFollowersParams, + PylonListIssueFollowersResponse +> = { + id: 'pylon_list_issue_followers', + name: 'List Issue Followers in Pylon', + description: 'Get list of followers for a specific issue', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params?.issueId || ''}/followers`), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response, params?: PylonListIssueFollowersParams) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_issue_followers') + } + + const data = await response.json() + + return { + success: true, + output: { + followers: data.data || [], + metadata: { + operation: 'list_issue_followers' as const, + issueId: params?.issueId || '', + totalFollowers: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Followers list', + properties: { + followers: { type: 'array', description: 'Array of follower objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/list_issues.ts b/apps/sim/tools/pylon/list_issues.ts new file mode 100644 index 0000000000..53593360b5 --- /dev/null +++ b/apps/sim/tools/pylon/list_issues.ts @@ -0,0 +1,114 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListIssues') + +export interface PylonListIssuesParams { + apiToken: string + startTime: string + endTime: string + cursor?: string +} + +export interface PylonListIssuesResponse { + success: boolean + output: { + issues: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'list_issues' + totalReturned: number + } + success: boolean + } +} + +export const pylonListIssuesTool: ToolConfig = { + id: 'pylon_list_issues', + name: 'List Issues in Pylon', + description: 'Retrieve a list of issues within a specified time range', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + startTime: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Start time in RFC3339 format (e.g., 2024-01-01T00:00:00Z)', + }, + endTime: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'End time in RFC3339 format (e.g., 2024-01-31T23:59:59Z)', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + }, + + request: { + url: (params) => { + const url = new URL(buildPylonUrl('/issues')) + url.searchParams.append('start_time', params.startTime) + url.searchParams.append('end_time', params.endTime) + if (params.cursor) { + url.searchParams.append('cursor', params.cursor) + } + return url.toString() + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_issues') + } + + const data = await response.json() + + return { + success: true, + output: { + issues: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'list_issues' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of issues', + properties: { + issues: { type: 'array', description: 'Array of issue objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/list_tags.ts b/apps/sim/tools/pylon/list_tags.ts new file mode 100644 index 0000000000..ce89b2ac67 --- /dev/null +++ b/apps/sim/tools/pylon/list_tags.ts @@ -0,0 +1,79 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListTags') + +export interface PylonListTagsParams { + apiToken: string +} + +export interface PylonListTagsResponse { + success: boolean + output: { + tags: any[] + metadata: { + operation: 'list_tags' + totalReturned: number + } + success: boolean + } +} + +export const pylonListTagsTool: ToolConfig = { + id: 'pylon_list_tags', + name: 'List Tags in Pylon', + description: 'Retrieve a list of tags', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + }, + + request: { + url: () => buildPylonUrl('/tags'), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_tags') + } + + const data = await response.json() + + return { + success: true, + output: { + tags: data.data || [], + metadata: { + operation: 'list_tags' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of tags', + properties: { + tags: { type: 'array', description: 'Array of tag objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/list_teams.ts b/apps/sim/tools/pylon/list_teams.ts new file mode 100644 index 0000000000..e1194ce54c --- /dev/null +++ b/apps/sim/tools/pylon/list_teams.ts @@ -0,0 +1,79 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListTeams') + +export interface PylonListTeamsParams { + apiToken: string +} + +export interface PylonListTeamsResponse { + success: boolean + output: { + teams: any[] + metadata: { + operation: 'list_teams' + totalReturned: number + } + success: boolean + } +} + +export const pylonListTeamsTool: ToolConfig = { + id: 'pylon_list_teams', + name: 'List Teams in Pylon', + description: 'Retrieve a list of teams', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + }, + + request: { + url: () => buildPylonUrl('/teams'), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_teams') + } + + const data = await response.json() + + return { + success: true, + output: { + teams: data.data || [], + metadata: { + operation: 'list_teams' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of teams', + properties: { + teams: { type: 'array', description: 'Array of team objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/list_users.ts b/apps/sim/tools/pylon/list_users.ts new file mode 100644 index 0000000000..825be35bd5 --- /dev/null +++ b/apps/sim/tools/pylon/list_users.ts @@ -0,0 +1,79 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonListUsers') + +export interface PylonListUsersParams { + apiToken: string +} + +export interface PylonListUsersResponse { + success: boolean + output: { + users: any[] + metadata: { + operation: 'list_users' + totalReturned: number + } + success: boolean + } +} + +export const pylonListUsersTool: ToolConfig = { + id: 'pylon_list_users', + name: 'List Users in Pylon', + description: 'Retrieve a list of users', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + }, + + request: { + url: () => buildPylonUrl('/users'), + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'list_users') + } + + const data = await response.json() + + return { + success: true, + output: { + users: data.data || [], + metadata: { + operation: 'list_users' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'List of users', + properties: { + users: { type: 'array', description: 'Array of user objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/manage_issue_followers.ts b/apps/sim/tools/pylon/manage_issue_followers.ts new file mode 100644 index 0000000000..f102e1cacc --- /dev/null +++ b/apps/sim/tools/pylon/manage_issue_followers.ts @@ -0,0 +1,128 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonManageIssueFollowers') + +export interface PylonManageIssueFollowersParams { + apiToken: string + issueId: string + userIds?: string + contactIds?: string + operation?: string +} + +export interface PylonManageIssueFollowersResponse { + success: boolean + output: { + followers: any[] + metadata: { + operation: 'manage_issue_followers' + issueId: string + action: string + } + success: boolean + } +} + +export const pylonManageIssueFollowersTool: ToolConfig< + PylonManageIssueFollowersParams, + PylonManageIssueFollowersResponse +> = { + id: 'pylon_manage_issue_followers', + name: 'Manage Issue Followers in Pylon', + description: 'Add or remove followers from an issue', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue', + }, + userIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated user IDs to add/remove', + }, + contactIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated contact IDs to add/remove', + }, + operation: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Operation to perform: "add" or "remove" (default: "add")', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params?.issueId || ''}/followers`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.userIds) { + body.user_ids = params.userIds.split(',').map((id) => id.trim()) + } + + if (params.contactIds) { + body.contact_ids = params.contactIds.split(',').map((id) => id.trim()) + } + + body.operation = params?.operation || 'add' + + return body + }, + }, + + transformResponse: async (response: Response, params?: PylonManageIssueFollowersParams) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'manage_issue_followers') + } + + const data = await response.json() + + return { + success: true, + output: { + followers: data.data || [], + metadata: { + operation: 'manage_issue_followers' as const, + issueId: params?.issueId || '', + action: params?.operation || 'add' || 'add', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated followers list', + properties: { + followers: { type: 'array', description: 'Array of follower objects' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/redact_message.ts b/apps/sim/tools/pylon/redact_message.ts new file mode 100644 index 0000000000..39b9fedf1d --- /dev/null +++ b/apps/sim/tools/pylon/redact_message.ts @@ -0,0 +1,94 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonRedactMessage') + +export interface PylonRedactMessageParams { + apiToken: string + issueId: string + messageId: string +} + +export interface PylonRedactMessageResponse { + success: boolean + output: { + metadata: { + operation: 'redact_message' + issueId: string + messageId: string + } + success: boolean + } +} + +export const pylonRedactMessageTool: ToolConfig< + PylonRedactMessageParams, + PylonRedactMessageResponse +> = { + id: 'pylon_redact_message', + name: 'Redact Message in Pylon', + description: 'Redact a specific message within an issue', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Issue ID containing the message', + }, + messageId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Message ID to redact', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params.issueId}/messages/${params.messageId}/redact`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'redact_message') + } + + return { + success: true, + output: { + metadata: { + operation: 'redact_message' as const, + issueId: '', + messageId: '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Redact operation result', + properties: { + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/search_accounts.ts b/apps/sim/tools/pylon/search_accounts.ts new file mode 100644 index 0000000000..78b403eac3 --- /dev/null +++ b/apps/sim/tools/pylon/search_accounts.ts @@ -0,0 +1,130 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonSearchAccounts') + +export interface PylonSearchAccountsParams { + apiToken: string + filter: string + limit?: string + cursor?: string +} + +export interface PylonSearchAccountsResponse { + success: boolean + output: { + accounts: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'search_accounts' + totalReturned: number + } + success: boolean + } +} + +export const pylonSearchAccountsTool: ToolConfig< + PylonSearchAccountsParams, + PylonSearchAccountsResponse +> = { + id: 'pylon_search_accounts', + name: 'Search Accounts in Pylon', + description: 'Search accounts with custom filters', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Filter as JSON string with field/operator/value structure', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Number of accounts to return (1-1000, default 100)', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + }, + + request: { + url: () => buildPylonUrl('/accounts/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + try { + body.filter = JSON.parse(params.filter) + } catch (error) { + logger.warn('Failed to parse filter', { error }) + throw new Error('Invalid filter JSON format') + } + + if (params.limit) { + body.limit = Number.parseInt(params.limit, 10) + } + + if (params.cursor) { + body.cursor = params.cursor + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'search_accounts') + } + + const data = await response.json() + + return { + success: true, + output: { + accounts: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'search_accounts' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + accounts: { type: 'array', description: 'Array of matching account objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/search_contacts.ts b/apps/sim/tools/pylon/search_contacts.ts new file mode 100644 index 0000000000..baaaea6c87 --- /dev/null +++ b/apps/sim/tools/pylon/search_contacts.ts @@ -0,0 +1,125 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonSearchContacts') + +export interface PylonSearchContactsParams { + apiToken: string + filter: string + limit?: string + cursor?: string +} + +export interface PylonSearchContactsResponse { + success: boolean + output: { + contacts: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'search_contacts' + totalReturned: number + } + success: boolean + } +} + +export const pylonSearchContactsTool: ToolConfig< + PylonSearchContactsParams, + PylonSearchContactsResponse +> = { + id: 'pylon_search_contacts', + name: 'Search Contacts in Pylon', + description: 'Search for contacts using a filter', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Filter as JSON object', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Maximum number of contacts to return', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + }, + + request: { + url: () => buildPylonUrl('/contacts/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + try { + body.filter = JSON.parse(params.filter) + } catch (error) { + logger.warn('Failed to parse filter', { error }) + body.filter = {} + } + + if (params.limit) body.limit = Number.parseInt(params.limit, 10) + if (params.cursor) body.cursor = params.cursor + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'search_contacts') + } + + const data = await response.json() + + return { + success: true, + output: { + contacts: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'search_contacts' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + contacts: { type: 'array', description: 'Array of contact objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/search_issues.ts b/apps/sim/tools/pylon/search_issues.ts new file mode 100644 index 0000000000..b11f2242fb --- /dev/null +++ b/apps/sim/tools/pylon/search_issues.ts @@ -0,0 +1,124 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonSearchIssues') + +export interface PylonSearchIssuesParams { + apiToken: string + filter: string + cursor?: string + limit?: number +} + +export interface PylonSearchIssuesResponse { + success: boolean + output: { + issues: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'search_issues' + totalReturned: number + } + success: boolean + } +} + +export const pylonSearchIssuesTool: ToolConfig = + { + id: 'pylon_search_issues', + name: 'Search Issues in Pylon', + description: 'Query issues using filters', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Filter criteria as JSON string', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Maximum number of results to return', + }, + }, + + request: { + url: () => buildPylonUrl('/issues/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.filter) { + try { + body.filter = JSON.parse(params.filter) + } catch (error) { + logger.warn('Failed to parse filter', { error }) + } + } + + if (params.cursor) body.cursor = params.cursor + if (params.limit !== undefined) body.limit = params.limit + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'search_issues') + } + + const data = await response.json() + + return { + success: true, + output: { + issues: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'search_issues' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + issues: { type: 'array', description: 'Array of issue objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/pylon/search_users.ts b/apps/sim/tools/pylon/search_users.ts new file mode 100644 index 0000000000..a68fec6cda --- /dev/null +++ b/apps/sim/tools/pylon/search_users.ts @@ -0,0 +1,122 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonSearchUsers') + +export interface PylonSearchUsersParams { + apiToken: string + filter: string + cursor?: string + limit?: string +} + +export interface PylonSearchUsersResponse { + success: boolean + output: { + users: any[] + pagination?: { + cursor?: string + has_next_page?: boolean + } + metadata: { + operation: 'search_users' + totalReturned: number + } + success: boolean + } +} + +export const pylonSearchUsersTool: ToolConfig = { + id: 'pylon_search_users', + name: 'Search Users in Pylon', + description: 'Search for users using a filter with email field', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Filter as JSON object with email field', + }, + cursor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Pagination cursor for next page of results', + }, + limit: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Maximum number of users to return', + }, + }, + + request: { + url: () => buildPylonUrl('/users/search'), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + try { + body.filter = JSON.parse(params.filter) + } catch (error) { + logger.warn('Failed to parse filter', { error }) + body.filter = {} + } + + if (params.cursor) body.cursor = params.cursor + if (params.limit) body.limit = Number.parseInt(params.limit, 10) + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'search_users') + } + + const data = await response.json() + + return { + success: true, + output: { + users: data.data || [], + pagination: data.pagination, + metadata: { + operation: 'search_users' as const, + totalReturned: data.data?.length || 0, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + users: { type: 'array', description: 'Array of user objects' }, + pagination: { type: 'object', description: 'Pagination metadata' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/snooze_issue.ts b/apps/sim/tools/pylon/snooze_issue.ts new file mode 100644 index 0000000000..112b5f4bf7 --- /dev/null +++ b/apps/sim/tools/pylon/snooze_issue.ts @@ -0,0 +1,99 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonSnoozeIssue') + +export interface PylonSnoozeIssueParams { + apiToken: string + issueId: string + snoozeUntil: string +} + +export interface PylonSnoozeIssueResponse { + success: boolean + output: { + issue: any + metadata: { + operation: 'snooze_issue' + issueId: string + snoozeUntil: string + } + success: boolean + } +} + +export const pylonSnoozeIssueTool: ToolConfig = { + id: 'pylon_snooze_issue', + name: 'Snooze Issue in Pylon', + description: 'Postpone issue visibility until specified time', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue to snooze', + }, + snoozeUntil: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'RFC3339 timestamp when issue should reappear (e.g., 2024-01-01T00:00:00Z)', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params?.issueId || ''}/snooze`), + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + snooze_until: params?.snoozeUntil || '', + }), + }, + + transformResponse: async (response: Response, params?: PylonSnoozeIssueParams) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'snooze_issue') + } + + const data = await response.json() + + return { + success: true, + output: { + issue: data.data, + metadata: { + operation: 'snooze_issue' as const, + issueId: params?.issueId || '', + snoozeUntil: params?.snoozeUntil || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Snoozed issue data', + properties: { + issue: { type: 'object', description: 'Snoozed issue object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/types.ts b/apps/sim/tools/pylon/types.ts new file mode 100644 index 0000000000..059766b3d7 --- /dev/null +++ b/apps/sim/tools/pylon/types.ts @@ -0,0 +1,27 @@ +// Shared types and utilities for Pylon tools + +export const PYLON_API_BASE_URL = 'https://api.usepylon.com' + +export function buildPylonUrl(path: string): string { + return `${PYLON_API_BASE_URL}${path}` +} + +export interface PylonErrorResponse { + error?: { + code?: string + message?: string + details?: any + } + request_id?: string +} + +export function handlePylonError( + data: PylonErrorResponse, + status: number, + operation: string +): never { + const errorMessage = data.error?.message || `Pylon API error during ${operation}` + const errorCode = data.error?.code || `HTTP_${status}` + + throw new Error(`${errorCode}: ${errorMessage}`) +} diff --git a/apps/sim/tools/pylon/update_account.ts b/apps/sim/tools/pylon/update_account.ts new file mode 100644 index 0000000000..30151347b0 --- /dev/null +++ b/apps/sim/tools/pylon/update_account.ts @@ -0,0 +1,198 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateAccount') + +export interface PylonUpdateAccountParams { + apiToken: string + accountId: string + name?: string + domains?: string + primaryDomain?: string + customFields?: string + tags?: string + channels?: string + externalIds?: string + ownerId?: string + logoUrl?: string + subaccountIds?: string +} + +export interface PylonUpdateAccountResponse { + success: boolean + output: { + account: any + metadata: { + operation: 'update_account' + accountId: string + } + success: boolean + } +} + +export const pylonUpdateAccountTool: ToolConfig< + PylonUpdateAccountParams, + PylonUpdateAccountResponse +> = { + id: 'pylon_update_account', + name: 'Update Account in Pylon', + description: 'Update an existing account with new properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + accountId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Account ID to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Account name', + }, + domains: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated list of domains', + }, + primaryDomain: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Primary domain for the account', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tag IDs', + }, + channels: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated channel IDs', + }, + externalIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated external IDs', + }, + ownerId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Owner user ID', + }, + logoUrl: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'URL to account logo', + }, + subaccountIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated subaccount IDs', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/accounts/${params.accountId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.name) body.name = params.name + if (params.primaryDomain) body.primary_domain = params.primaryDomain + if (params.ownerId) body.owner_id = params.ownerId + if (params.logoUrl) body.logo_url = params.logoUrl + + if (params.domains) { + body.domains = params.domains.split(',').map((d) => d.trim()) + } + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + if (params.tags) { + body.tags = params.tags.split(',').map((t) => t.trim()) + } + + if (params.channels) { + body.channels = params.channels.split(',').map((c) => c.trim()) + } + + if (params.externalIds) { + body.external_ids = params.externalIds.split(',').map((e) => e.trim()) + } + + if (params.subaccountIds) { + body.subaccount_ids = params.subaccountIds.split(',').map((s) => s.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_account') + } + + const data = await response.json() + + return { + success: true, + output: { + account: data.data, + metadata: { + operation: 'update_account' as const, + accountId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated account data', + properties: { + account: { type: 'object', description: 'Updated account object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/update_contact.ts b/apps/sim/tools/pylon/update_contact.ts new file mode 100644 index 0000000000..8480364ef7 --- /dev/null +++ b/apps/sim/tools/pylon/update_contact.ts @@ -0,0 +1,159 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateContact') + +export interface PylonUpdateContactParams { + apiToken: string + contactId: string + name?: string + email?: string + accountId?: string + accountExternalId?: string + avatarUrl?: string + customFields?: string + portalRole?: string +} + +export interface PylonUpdateContactResponse { + success: boolean + output: { + contact: any + metadata: { + operation: 'update_contact' + contactId: string + } + success: boolean + } +} + +export const pylonUpdateContactTool: ToolConfig< + PylonUpdateContactParams, + PylonUpdateContactResponse +> = { + id: 'pylon_update_contact', + name: 'Update Contact in Pylon', + description: 'Update an existing contact with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Contact ID to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Contact name', + }, + email: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Contact email address', + }, + accountId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Account ID to associate with contact', + }, + accountExternalId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'External account ID to associate with contact', + }, + avatarUrl: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'URL for contact avatar image', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + portalRole: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Portal role for the contact', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/contacts/${params.contactId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.name) body.name = params.name + if (params.email) body.email = params.email + if (params.accountId) body.account_id = params.accountId + if (params.accountExternalId) body.account_external_id = params.accountExternalId + if (params.avatarUrl) body.avatar_url = params.avatarUrl + if (params.portalRole) body.portal_role = params.portalRole + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + contact: data.data, + metadata: { + operation: 'update_contact' as const, + contactId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated contact data', + properties: { + contact: { type: 'object', description: 'Updated contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/update_issue.ts b/apps/sim/tools/pylon/update_issue.ts new file mode 100644 index 0000000000..1b5e47525d --- /dev/null +++ b/apps/sim/tools/pylon/update_issue.ts @@ -0,0 +1,168 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateIssue') + +export interface PylonUpdateIssueParams { + apiToken: string + issueId: string + state?: string + assigneeId?: string + teamId?: string + tags?: string + customFields?: string + customerPortalVisible?: boolean + requesterId?: string + accountId?: string +} + +export interface PylonUpdateIssueResponse { + success: boolean + output: { + issue: any + metadata: { + operation: 'update_issue' + issueId: string + } + success: boolean + } +} + +export const pylonUpdateIssueTool: ToolConfig = { + id: 'pylon_update_issue', + name: 'Update Issue in Pylon', + description: 'Update an existing issue', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + issueId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the issue to update', + }, + state: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Issue state', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User ID to assign issue to', + }, + teamId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Team ID to assign issue to', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tag IDs', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + customerPortalVisible: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: 'Whether issue is visible in customer portal', + }, + requesterId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Requester user ID', + }, + accountId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Account ID to associate with issue', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/issues/${params.issueId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.state !== undefined) body.state = params.state + if (params.assigneeId !== undefined) body.assignee_id = params.assigneeId + if (params.teamId !== undefined) body.team_id = params.teamId + if (params.requesterId !== undefined) body.requester_id = params.requesterId + if (params.accountId !== undefined) body.account_id = params.accountId + if (params.customerPortalVisible !== undefined) + body.customer_portal_visible = params.customerPortalVisible + + if (params.tags) { + body.tags = params.tags.split(',').map((t) => t.trim()) + } + + if (params.customFields) { + try { + body.custom_fields = JSON.parse(params.customFields) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_issue') + } + + const data = await response.json() + + return { + success: true, + output: { + issue: data.data, + metadata: { + operation: 'update_issue' as const, + issueId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated issue data', + properties: { + issue: { type: 'object', description: 'Updated issue object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/update_tag.ts b/apps/sim/tools/pylon/update_tag.ts new file mode 100644 index 0000000000..19a7bfe112 --- /dev/null +++ b/apps/sim/tools/pylon/update_tag.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateTag') + +export interface PylonUpdateTagParams { + apiToken: string + tagId: string + hexColor?: string + value?: string +} + +export interface PylonUpdateTagResponse { + success: boolean + output: { + tag: any + metadata: { + operation: 'update_tag' + tagId: string + } + success: boolean + } +} + +export const pylonUpdateTagTool: ToolConfig = { + id: 'pylon_update_tag', + name: 'Update Tag in Pylon', + description: 'Update an existing tag with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Tag ID to update', + }, + hexColor: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Hex color code for tag (e.g., #FF5733)', + }, + value: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Tag value/name', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/tags/${params.tagId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.hexColor) body.hex_color = params.hexColor + if (params.value) body.value = params.value + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_tag') + } + + const data = await response.json() + + return { + success: true, + output: { + tag: data.data, + metadata: { + operation: 'update_tag' as const, + tagId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated tag data', + properties: { + tag: { type: 'object', description: 'Updated tag object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/update_team.ts b/apps/sim/tools/pylon/update_team.ts new file mode 100644 index 0000000000..1e758f6076 --- /dev/null +++ b/apps/sim/tools/pylon/update_team.ts @@ -0,0 +1,113 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateTeam') + +export interface PylonUpdateTeamParams { + apiToken: string + teamId: string + name?: string + userIds?: string +} + +export interface PylonUpdateTeamResponse { + success: boolean + output: { + team: any + metadata: { + operation: 'update_team' + teamId: string + } + success: boolean + } +} + +export const pylonUpdateTeamTool: ToolConfig = { + id: 'pylon_update_team', + name: 'Update Team in Pylon', + description: + 'Update an existing team with specified properties (userIds replaces entire membership)', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + teamId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Team ID to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Team name', + }, + userIds: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated user IDs (replaces entire team membership)', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/teams/${params.teamId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.name) body.name = params.name + + if (params.userIds) { + body.user_ids = params.userIds.split(',').map((id) => id.trim()) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_team') + } + + const data = await response.json() + + return { + success: true, + output: { + team: data.data, + metadata: { + operation: 'update_team' as const, + teamId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated team data', + properties: { + team: { type: 'object', description: 'Updated team object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/pylon/update_user.ts b/apps/sim/tools/pylon/update_user.ts new file mode 100644 index 0000000000..b90e30993b --- /dev/null +++ b/apps/sim/tools/pylon/update_user.ts @@ -0,0 +1,109 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildPylonUrl, handlePylonError } from './types' + +const logger = createLogger('PylonUpdateUser') + +export interface PylonUpdateUserParams { + apiToken: string + userId: string + roleId?: string + status?: string +} + +export interface PylonUpdateUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'update_user' + userId: string + } + success: boolean + } +} + +export const pylonUpdateUserTool: ToolConfig = { + id: 'pylon_update_user', + name: 'Update User in Pylon', + description: 'Update an existing user with specified properties', + version: '1.0.0', + + params: { + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Pylon API token', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User ID to update', + }, + roleId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Role ID to assign to user', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User status', + }, + }, + + request: { + url: (params) => buildPylonUrl(`/users/${params.userId}`), + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.apiToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: any = {} + + if (params.roleId) body.role_id = params.roleId + if (params.status) body.status = params.status + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handlePylonError(data, response.status, 'update_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.data, + metadata: { + operation: 'update_user' as const, + userId: data.data?.id || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated user data', + properties: { + user: { type: 'object', description: 'Updated user object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 5a73522963..f22d04cd20 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -290,6 +290,24 @@ import { incidentioWorkflowsShowTool, incidentioWorkflowsUpdateTool, } from '@/tools/incidentio' +import { + intercomCreateCompanyTool, + intercomCreateContactTool, + intercomCreateMessageTool, + intercomCreateTicketTool, + intercomDeleteContactTool, + intercomGetCompanyTool, + intercomGetContactTool, + intercomGetConversationTool, + intercomGetTicketTool, + intercomListCompaniesTool, + intercomListContactsTool, + intercomListConversationsTool, + intercomReplyConversationTool, + intercomSearchContactsTool, + intercomSearchConversationsTool, + intercomUpdateContactTool, +} from '@/tools/intercom' import { searchTool as jinaSearchTool, readUrlTool } from '@/tools/jina' import { jiraAddCommentTool, @@ -402,6 +420,81 @@ import { linearUpdateWorkflowStateTool, } from '@/tools/linear' import { linkupSearchTool } from '@/tools/linkup' +import { + mailchimpAddMemberTagsTool, + mailchimpAddMemberTool, + mailchimpAddOrUpdateMemberTool, + mailchimpAddSegmentMemberTool, + mailchimpAddSubscriberToAutomationTool, + mailchimpArchiveMemberTool, + mailchimpCreateAudienceTool, + mailchimpCreateBatchOperationTool, + mailchimpCreateCampaignTool, + mailchimpCreateInterestCategoryTool, + mailchimpCreateInterestTool, + mailchimpCreateLandingPageTool, + mailchimpCreateMergeFieldTool, + mailchimpCreateSegmentTool, + mailchimpCreateTemplateTool, + mailchimpDeleteAudienceTool, + mailchimpDeleteBatchOperationTool, + mailchimpDeleteCampaignTool, + mailchimpDeleteInterestCategoryTool, + mailchimpDeleteInterestTool, + mailchimpDeleteLandingPageTool, + mailchimpDeleteMemberTool, + mailchimpDeleteMergeFieldTool, + mailchimpDeleteSegmentTool, + mailchimpDeleteTemplateTool, + mailchimpGetAudiencesTool, + mailchimpGetAudienceTool, + mailchimpGetAutomationsTool, + mailchimpGetAutomationTool, + mailchimpGetBatchOperationsTool, + mailchimpGetBatchOperationTool, + mailchimpGetCampaignContentTool, + mailchimpGetCampaignReportsTool, + mailchimpGetCampaignReportTool, + mailchimpGetCampaignsTool, + mailchimpGetCampaignTool, + mailchimpGetInterestCategoriesTool, + mailchimpGetInterestCategoryTool, + mailchimpGetInterestsTool, + mailchimpGetInterestTool, + mailchimpGetLandingPagesTool, + mailchimpGetLandingPageTool, + mailchimpGetMembersTool, + mailchimpGetMemberTagsTool, + mailchimpGetMemberTool, + mailchimpGetMergeFieldsTool, + mailchimpGetMergeFieldTool, + mailchimpGetSegmentMembersTool, + mailchimpGetSegmentsTool, + mailchimpGetSegmentTool, + mailchimpGetTemplatesTool, + mailchimpGetTemplateTool, + mailchimpPauseAutomationTool, + mailchimpPublishLandingPageTool, + mailchimpRemoveMemberTagsTool, + mailchimpRemoveSegmentMemberTool, + mailchimpReplicateCampaignTool, + mailchimpScheduleCampaignTool, + mailchimpSendCampaignTool, + mailchimpSetCampaignContentTool, + mailchimpStartAutomationTool, + mailchimpUnarchiveMemberTool, + mailchimpUnpublishLandingPageTool, + mailchimpUnscheduleCampaignTool, + mailchimpUpdateAudienceTool, + mailchimpUpdateCampaignTool, + mailchimpUpdateInterestCategoryTool, + mailchimpUpdateInterestTool, + mailchimpUpdateLandingPageTool, + mailchimpUpdateMemberTool, + mailchimpUpdateMergeFieldTool, + mailchimpUpdateSegmentTool, + mailchimpUpdateTemplateTool, +} from '@/tools/mailchimp' import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0' import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory' import { @@ -574,6 +667,45 @@ import { posthogUpdatePropertyDefinitionTool, posthogUpdateSurveyTool, } from '@/tools/posthog' +import { + pylonBulkUpdateAccountsTool, + pylonCreateAccountTool, + pylonCreateContactTool, + pylonCreateIssueTool, + pylonCreateTagTool, + pylonCreateTeamTool, + pylonDeleteAccountTool, + pylonDeleteContactTool, + pylonDeleteIssueTool, + pylonDeleteTagTool, + pylonGetAccountTool, + pylonGetContactTool, + pylonGetIssueTool, + pylonGetTagTool, + pylonGetTeamTool, + pylonGetUserTool, + pylonLinkExternalIssueTool, + pylonListAccountsTool, + pylonListContactsTool, + pylonListIssueFollowersTool, + pylonListIssuesTool, + pylonListTagsTool, + pylonListTeamsTool, + pylonListUsersTool, + pylonManageIssueFollowersTool, + pylonRedactMessageTool, + pylonSearchAccountsTool, + pylonSearchContactsTool, + pylonSearchIssuesTool, + pylonSearchUsersTool, + pylonSnoozeIssueTool, + pylonUpdateAccountTool, + pylonUpdateContactTool, + pylonUpdateIssueTool, + pylonUpdateTagTool, + pylonUpdateTeamTool, + pylonUpdateUserTool, +} from '@/tools/pylon' import { qdrantFetchTool, qdrantSearchTool, qdrantUpsertTool } from '@/tools/qdrant' import { redditDeleteTool, @@ -825,6 +957,34 @@ import { youtubeSearchTool, youtubeVideoDetailsTool, } from '@/tools/youtube' +import { + zendeskAutocompleteOrganizationsTool, + zendeskCreateOrganizationsBulkTool, + zendeskCreateOrganizationTool, + zendeskCreateTicketsBulkTool, + zendeskCreateTicketTool, + zendeskCreateUsersBulkTool, + zendeskCreateUserTool, + zendeskDeleteOrganizationTool, + zendeskDeleteTicketTool, + zendeskDeleteUserTool, + zendeskGetCurrentUserTool, + zendeskGetOrganizationsTool, + zendeskGetOrganizationTool, + zendeskGetTicketsTool, + zendeskGetTicketTool, + zendeskGetUsersTool, + zendeskGetUserTool, + zendeskMergeTicketsTool, + zendeskSearchCountTool, + zendeskSearchTool, + zendeskSearchUsersTool, + zendeskUpdateOrganizationTool, + zendeskUpdateTicketsBulkTool, + zendeskUpdateTicketTool, + zendeskUpdateUsersBulkTool, + zendeskUpdateUserTool, +} from '@/tools/zendesk' import { zepAddMessagesTool, zepAddUserTool, @@ -1591,6 +1751,158 @@ export const tools: Record = { salesforce_create_task: salesforceCreateTaskTool, salesforce_update_task: salesforceUpdateTaskTool, salesforce_delete_task: salesforceDeleteTaskTool, + pylon_list_issues: pylonListIssuesTool, + pylon_create_issue: pylonCreateIssueTool, + pylon_get_issue: pylonGetIssueTool, + pylon_update_issue: pylonUpdateIssueTool, + pylon_delete_issue: pylonDeleteIssueTool, + pylon_search_issues: pylonSearchIssuesTool, + pylon_snooze_issue: pylonSnoozeIssueTool, + pylon_list_issue_followers: pylonListIssueFollowersTool, + pylon_manage_issue_followers: pylonManageIssueFollowersTool, + pylon_link_external_issue: pylonLinkExternalIssueTool, + pylon_list_accounts: pylonListAccountsTool, + pylon_create_account: pylonCreateAccountTool, + pylon_get_account: pylonGetAccountTool, + pylon_update_account: pylonUpdateAccountTool, + pylon_delete_account: pylonDeleteAccountTool, + pylon_bulk_update_accounts: pylonBulkUpdateAccountsTool, + pylon_search_accounts: pylonSearchAccountsTool, + pylon_list_contacts: pylonListContactsTool, + pylon_create_contact: pylonCreateContactTool, + pylon_get_contact: pylonGetContactTool, + pylon_update_contact: pylonUpdateContactTool, + pylon_delete_contact: pylonDeleteContactTool, + pylon_search_contacts: pylonSearchContactsTool, + pylon_list_users: pylonListUsersTool, + pylon_get_user: pylonGetUserTool, + pylon_update_user: pylonUpdateUserTool, + pylon_search_users: pylonSearchUsersTool, + pylon_list_teams: pylonListTeamsTool, + pylon_get_team: pylonGetTeamTool, + pylon_create_team: pylonCreateTeamTool, + pylon_update_team: pylonUpdateTeamTool, + pylon_list_tags: pylonListTagsTool, + pylon_get_tag: pylonGetTagTool, + pylon_create_tag: pylonCreateTagTool, + pylon_update_tag: pylonUpdateTagTool, + pylon_delete_tag: pylonDeleteTagTool, + pylon_redact_message: pylonRedactMessageTool, + mailchimp_get_audiences: mailchimpGetAudiencesTool, + mailchimp_get_audience: mailchimpGetAudienceTool, + mailchimp_create_audience: mailchimpCreateAudienceTool, + mailchimp_update_audience: mailchimpUpdateAudienceTool, + mailchimp_delete_audience: mailchimpDeleteAudienceTool, + mailchimp_get_members: mailchimpGetMembersTool, + mailchimp_get_member: mailchimpGetMemberTool, + mailchimp_add_member: mailchimpAddMemberTool, + mailchimp_add_or_update_member: mailchimpAddOrUpdateMemberTool, + mailchimp_update_member: mailchimpUpdateMemberTool, + mailchimp_delete_member: mailchimpDeleteMemberTool, + mailchimp_archive_member: mailchimpArchiveMemberTool, + mailchimp_unarchive_member: mailchimpUnarchiveMemberTool, + mailchimp_get_campaigns: mailchimpGetCampaignsTool, + mailchimp_get_campaign: mailchimpGetCampaignTool, + mailchimp_create_campaign: mailchimpCreateCampaignTool, + mailchimp_update_campaign: mailchimpUpdateCampaignTool, + mailchimp_delete_campaign: mailchimpDeleteCampaignTool, + mailchimp_send_campaign: mailchimpSendCampaignTool, + mailchimp_schedule_campaign: mailchimpScheduleCampaignTool, + mailchimp_unschedule_campaign: mailchimpUnscheduleCampaignTool, + mailchimp_replicate_campaign: mailchimpReplicateCampaignTool, + mailchimp_get_campaign_content: mailchimpGetCampaignContentTool, + mailchimp_set_campaign_content: mailchimpSetCampaignContentTool, + mailchimp_get_automations: mailchimpGetAutomationsTool, + mailchimp_get_automation: mailchimpGetAutomationTool, + mailchimp_start_automation: mailchimpStartAutomationTool, + mailchimp_pause_automation: mailchimpPauseAutomationTool, + mailchimp_add_subscriber_to_automation: mailchimpAddSubscriberToAutomationTool, + mailchimp_get_templates: mailchimpGetTemplatesTool, + mailchimp_get_template: mailchimpGetTemplateTool, + mailchimp_create_template: mailchimpCreateTemplateTool, + mailchimp_update_template: mailchimpUpdateTemplateTool, + mailchimp_delete_template: mailchimpDeleteTemplateTool, + mailchimp_get_campaign_reports: mailchimpGetCampaignReportsTool, + mailchimp_get_campaign_report: mailchimpGetCampaignReportTool, + mailchimp_get_segments: mailchimpGetSegmentsTool, + mailchimp_get_segment: mailchimpGetSegmentTool, + mailchimp_create_segment: mailchimpCreateSegmentTool, + mailchimp_update_segment: mailchimpUpdateSegmentTool, + mailchimp_delete_segment: mailchimpDeleteSegmentTool, + mailchimp_get_segment_members: mailchimpGetSegmentMembersTool, + mailchimp_add_segment_member: mailchimpAddSegmentMemberTool, + mailchimp_remove_segment_member: mailchimpRemoveSegmentMemberTool, + mailchimp_get_member_tags: mailchimpGetMemberTagsTool, + mailchimp_add_member_tags: mailchimpAddMemberTagsTool, + mailchimp_remove_member_tags: mailchimpRemoveMemberTagsTool, + mailchimp_get_merge_fields: mailchimpGetMergeFieldsTool, + mailchimp_get_merge_field: mailchimpGetMergeFieldTool, + mailchimp_create_merge_field: mailchimpCreateMergeFieldTool, + mailchimp_update_merge_field: mailchimpUpdateMergeFieldTool, + mailchimp_delete_merge_field: mailchimpDeleteMergeFieldTool, + mailchimp_get_interest_categories: mailchimpGetInterestCategoriesTool, + mailchimp_get_interest_category: mailchimpGetInterestCategoryTool, + mailchimp_create_interest_category: mailchimpCreateInterestCategoryTool, + mailchimp_update_interest_category: mailchimpUpdateInterestCategoryTool, + mailchimp_delete_interest_category: mailchimpDeleteInterestCategoryTool, + mailchimp_get_interests: mailchimpGetInterestsTool, + mailchimp_get_interest: mailchimpGetInterestTool, + mailchimp_create_interest: mailchimpCreateInterestTool, + mailchimp_update_interest: mailchimpUpdateInterestTool, + mailchimp_delete_interest: mailchimpDeleteInterestTool, + mailchimp_get_landing_pages: mailchimpGetLandingPagesTool, + mailchimp_get_landing_page: mailchimpGetLandingPageTool, + mailchimp_create_landing_page: mailchimpCreateLandingPageTool, + mailchimp_update_landing_page: mailchimpUpdateLandingPageTool, + mailchimp_delete_landing_page: mailchimpDeleteLandingPageTool, + mailchimp_publish_landing_page: mailchimpPublishLandingPageTool, + mailchimp_unpublish_landing_page: mailchimpUnpublishLandingPageTool, + mailchimp_get_batch_operations: mailchimpGetBatchOperationsTool, + mailchimp_get_batch_operation: mailchimpGetBatchOperationTool, + mailchimp_create_batch_operation: mailchimpCreateBatchOperationTool, + mailchimp_delete_batch_operation: mailchimpDeleteBatchOperationTool, + zendesk_get_tickets: zendeskGetTicketsTool, + zendesk_get_ticket: zendeskGetTicketTool, + zendesk_create_ticket: zendeskCreateTicketTool, + zendesk_create_tickets_bulk: zendeskCreateTicketsBulkTool, + zendesk_update_ticket: zendeskUpdateTicketTool, + zendesk_update_tickets_bulk: zendeskUpdateTicketsBulkTool, + zendesk_delete_ticket: zendeskDeleteTicketTool, + zendesk_merge_tickets: zendeskMergeTicketsTool, + zendesk_get_users: zendeskGetUsersTool, + zendesk_get_user: zendeskGetUserTool, + zendesk_search_users: zendeskSearchUsersTool, + zendesk_create_user: zendeskCreateUserTool, + zendesk_create_users_bulk: zendeskCreateUsersBulkTool, + zendesk_update_user: zendeskUpdateUserTool, + zendesk_update_users_bulk: zendeskUpdateUsersBulkTool, + zendesk_delete_user: zendeskDeleteUserTool, + zendesk_get_current_user: zendeskGetCurrentUserTool, + zendesk_get_organizations: zendeskGetOrganizationsTool, + zendesk_get_organization: zendeskGetOrganizationTool, + zendesk_autocomplete_organizations: zendeskAutocompleteOrganizationsTool, + zendesk_create_organization: zendeskCreateOrganizationTool, + zendesk_create_organizations_bulk: zendeskCreateOrganizationsBulkTool, + zendesk_update_organization: zendeskUpdateOrganizationTool, + zendesk_delete_organization: zendeskDeleteOrganizationTool, + zendesk_search: zendeskSearchTool, + zendesk_search_count: zendeskSearchCountTool, + intercom_create_contact: intercomCreateContactTool, + intercom_get_contact: intercomGetContactTool, + intercom_update_contact: intercomUpdateContactTool, + intercom_list_contacts: intercomListContactsTool, + intercom_search_contacts: intercomSearchContactsTool, + intercom_delete_contact: intercomDeleteContactTool, + intercom_create_company: intercomCreateCompanyTool, + intercom_get_company: intercomGetCompanyTool, + intercom_list_companies: intercomListCompaniesTool, + intercom_get_conversation: intercomGetConversationTool, + intercom_list_conversations: intercomListConversationsTool, + intercom_reply_conversation: intercomReplyConversationTool, + intercom_search_conversations: intercomSearchConversationsTool, + intercom_create_ticket: intercomCreateTicketTool, + intercom_get_ticket: intercomGetTicketTool, + intercom_create_message: intercomCreateMessageTool, sentry_issues_list: listIssuesTool, sentry_issues_get: getIssueTool, sentry_issues_update: updateIssueTool, diff --git a/apps/sim/tools/zendesk/autocomplete_organizations.ts b/apps/sim/tools/zendesk/autocomplete_organizations.ts new file mode 100644 index 0000000000..7b0ef8ccbb --- /dev/null +++ b/apps/sim/tools/zendesk/autocomplete_organizations.ts @@ -0,0 +1,144 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskAutocompleteOrganizations') + +export interface ZendeskAutocompleteOrganizationsParams { + email: string + apiToken: string + subdomain: string + name: string + perPage?: string + page?: string +} + +export interface ZendeskAutocompleteOrganizationsResponse { + success: boolean + output: { + organizations: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'autocomplete_organizations' + totalReturned: number + } + success: boolean + } +} + +export const zendeskAutocompleteOrganizationsTool: ToolConfig< + ZendeskAutocompleteOrganizationsParams, + ZendeskAutocompleteOrganizationsResponse +> = { + id: 'zendesk_autocomplete_organizations', + name: 'Autocomplete Organizations in Zendesk', + description: + 'Autocomplete organizations in Zendesk by name prefix (for name matching/autocomplete)', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + name: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Organization name to search for', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + queryParams.append('name', params.name) + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/organizations/autocomplete') + return `${url}?${query}` + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'autocomplete_organizations') + } + + const data = await response.json() + const organizations = data.organizations || [] + + return { + success: true, + output: { + organizations, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || organizations.length, + }, + metadata: { + operation: 'autocomplete_organizations' as const, + totalReturned: organizations.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Organizations search results', + properties: { + organizations: { type: 'array', description: 'Array of organization objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/create_organization.ts b/apps/sim/tools/zendesk/create_organization.ts new file mode 100644 index 0000000000..c77162ba85 --- /dev/null +++ b/apps/sim/tools/zendesk/create_organization.ts @@ -0,0 +1,165 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateOrganization') + +export interface ZendeskCreateOrganizationParams { + email: string + apiToken: string + subdomain: string + name: string + domainNames?: string + details?: string + notes?: string + tags?: string + customFields?: string +} + +export interface ZendeskCreateOrganizationResponse { + success: boolean + output: { + organization: any + metadata: { + operation: 'create_organization' + organizationId: string + } + success: boolean + } +} + +export const zendeskCreateOrganizationTool: ToolConfig< + ZendeskCreateOrganizationParams, + ZendeskCreateOrganizationResponse +> = { + id: 'zendesk_create_organization', + name: 'Create Organization in Zendesk', + description: 'Create a new organization in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + name: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Organization name', + }, + domainNames: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated domain names', + }, + details: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization details', + }, + notes: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization notes', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object (e.g., {"field_id": "value"})', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/organizations'), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const organization: any = { + name: params.name, + } + + if (params.domainNames) + organization.domain_names = params.domainNames.split(',').map((d) => d.trim()) + if (params.details) organization.details = params.details + if (params.notes) organization.notes = params.notes + if (params.tags) organization.tags = params.tags.split(',').map((t) => t.trim()) + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + organization.organization_fields = customFields + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { organization } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_organization') + } + + const data = await response.json() + + return { + success: true, + output: { + organization: data.organization, + metadata: { + operation: 'create_organization' as const, + organizationId: data.organization?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created organization data', + properties: { + organization: { type: 'object', description: 'Created organization object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/create_organizations_bulk.ts b/apps/sim/tools/zendesk/create_organizations_bulk.ts new file mode 100644 index 0000000000..e46f9d9a8f --- /dev/null +++ b/apps/sim/tools/zendesk/create_organizations_bulk.ts @@ -0,0 +1,117 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateOrganizationsBulk') + +export interface ZendeskCreateOrganizationsBulkParams { + email: string + apiToken: string + subdomain: string + organizations: string +} + +export interface ZendeskCreateOrganizationsBulkResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'create_organizations_bulk' + jobId: string + } + success: boolean + } +} + +export const zendeskCreateOrganizationsBulkTool: ToolConfig< + ZendeskCreateOrganizationsBulkParams, + ZendeskCreateOrganizationsBulkResponse +> = { + id: 'zendesk_create_organizations_bulk', + name: 'Bulk Create Organizations in Zendesk', + description: 'Create multiple organizations in Zendesk using bulk import', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + organizations: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of organization objects to create', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/organizations/create_many'), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + try { + const organizations = JSON.parse(params.organizations) + return { organizations } + } catch (error) { + logger.error('Failed to parse organizations array', { error }) + throw new Error('Invalid organizations JSON format') + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_organizations_bulk') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'create_organizations_bulk' as const, + jobId: data.job_status?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk creation job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/create_ticket.ts b/apps/sim/tools/zendesk/create_ticket.ts new file mode 100644 index 0000000000..d0fd81a994 --- /dev/null +++ b/apps/sim/tools/zendesk/create_ticket.ts @@ -0,0 +1,197 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateTicket') + +export interface ZendeskCreateTicketParams { + email: string + apiToken: string + subdomain: string + subject: string + description: string + priority?: string + status?: string + type?: string + tags?: string + assigneeId?: string + groupId?: string + requesterId?: string + customFields?: string +} + +export interface ZendeskCreateTicketResponse { + success: boolean + output: { + ticket: any + metadata: { + operation: 'create_ticket' + ticketId: string + } + success: boolean + } +} + +export const zendeskCreateTicketTool: ToolConfig< + ZendeskCreateTicketParams, + ZendeskCreateTicketResponse +> = { + id: 'zendesk_create_ticket', + name: 'Create Ticket in Zendesk', + description: 'Create a new ticket in Zendesk with support for custom fields', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + subject: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Ticket subject (optional - will be auto-generated if not provided)', + }, + description: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Ticket description (first comment)', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Priority (low, normal, high, urgent)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Status (new, open, pending, hold, solved, closed)', + }, + type: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Type (problem, incident, question, task)', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Assignee user ID', + }, + groupId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Group ID', + }, + requesterId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Requester user ID', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object (e.g., {"field_id": "value"})', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/tickets'), + method: 'POST', + headers: (params) => { + // Use Basic Authentication with email/token format for Zendesk API tokens + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const ticket: any = { + subject: params.subject, + comment: { body: params.description }, + } + + if (params.priority) ticket.priority = params.priority + if (params.status) ticket.status = params.status + if (params.type) ticket.type = params.type + if (params.assigneeId) ticket.assignee_id = params.assigneeId + if (params.groupId) ticket.group_id = params.groupId + if (params.requesterId) ticket.requester_id = params.requesterId + if (params.tags) ticket.tags = params.tags.split(',').map((t) => t.trim()) + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + ticket.custom_fields = Object.entries(customFields).map(([id, value]) => ({ id, value })) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { ticket } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: data.ticket, + metadata: { + operation: 'create_ticket' as const, + ticketId: data.ticket?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created ticket data', + properties: { + ticket: { type: 'object', description: 'Created ticket object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/create_tickets_bulk.ts b/apps/sim/tools/zendesk/create_tickets_bulk.ts new file mode 100644 index 0000000000..bf7084f7db --- /dev/null +++ b/apps/sim/tools/zendesk/create_tickets_bulk.ts @@ -0,0 +1,118 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateTicketsBulk') + +export interface ZendeskCreateTicketsBulkParams { + email: string + apiToken: string + subdomain: string + tickets: string +} + +export interface ZendeskCreateTicketsBulkResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'create_tickets_bulk' + jobId?: string + } + success: boolean + } +} + +export const zendeskCreateTicketsBulkTool: ToolConfig< + ZendeskCreateTicketsBulkParams, + ZendeskCreateTicketsBulkResponse +> = { + id: 'zendesk_create_tickets_bulk', + name: 'Bulk Create Tickets in Zendesk', + description: 'Create multiple tickets in Zendesk at once (max 100)', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + tickets: { + type: 'string', + required: true, + visibility: 'user-only', + description: + 'JSON array of ticket objects to create (max 100). Each ticket should have subject and comment properties.', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/tickets/create_many'), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + try { + const tickets = JSON.parse(params.tickets) + return { tickets } + } catch (error) { + logger.error('Failed to parse tickets JSON', { error }) + throw new Error('Invalid tickets JSON format') + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_tickets_bulk') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'create_tickets_bulk' as const, + jobId: data.job_status?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk create job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/create_user.ts b/apps/sim/tools/zendesk/create_user.ts new file mode 100644 index 0000000000..c8f138e377 --- /dev/null +++ b/apps/sim/tools/zendesk/create_user.ts @@ -0,0 +1,177 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateUser') + +export interface ZendeskCreateUserParams { + email: string + apiToken: string + subdomain: string + name: string + userEmail?: string + role?: string + phone?: string + organizationId?: string + verified?: string + tags?: string + customFields?: string +} + +export interface ZendeskCreateUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'create_user' + userId: string + } + success: boolean + } +} + +export const zendeskCreateUserTool: ToolConfig = + { + id: 'zendesk_create_user', + name: 'Create User in Zendesk', + description: 'Create a new user in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + name: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User name', + }, + userEmail: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User email', + }, + role: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User role (end-user, agent, admin)', + }, + phone: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User phone number', + }, + organizationId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization ID', + }, + verified: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Set to "true" to skip email verification', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object (e.g., {"field_id": "value"})', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/users'), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const user: any = {} + + if (params.name) user.name = params.name + if (params.userEmail) user.email = params.userEmail + if (params.role) user.role = params.role + if (params.phone) user.phone = params.phone + if (params.organizationId) user.organization_id = params.organizationId + if (params.verified) user.verified = params.verified === 'true' + if (params.tags) user.tags = params.tags.split(',').map((t) => t.trim()) + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + user.user_fields = customFields + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { user } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.user, + metadata: { + operation: 'create_user' as const, + userId: data.user?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Created user data', + properties: { + user: { type: 'object', description: 'Created user object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/zendesk/create_users_bulk.ts b/apps/sim/tools/zendesk/create_users_bulk.ts new file mode 100644 index 0000000000..5549d2d609 --- /dev/null +++ b/apps/sim/tools/zendesk/create_users_bulk.ts @@ -0,0 +1,117 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskCreateUsersBulk') + +export interface ZendeskCreateUsersBulkParams { + email: string + apiToken: string + subdomain: string + users: string +} + +export interface ZendeskCreateUsersBulkResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'create_users_bulk' + jobId: string + } + success: boolean + } +} + +export const zendeskCreateUsersBulkTool: ToolConfig< + ZendeskCreateUsersBulkParams, + ZendeskCreateUsersBulkResponse +> = { + id: 'zendesk_create_users_bulk', + name: 'Bulk Create Users in Zendesk', + description: 'Create multiple users in Zendesk using bulk import', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + users: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of user objects to create', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/users/create_many'), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + try { + const users = JSON.parse(params.users) + return { users } + } catch (error) { + logger.error('Failed to parse users array', { error }) + throw new Error('Invalid users JSON format') + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'create_users_bulk') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'create_users_bulk' as const, + jobId: data.job_status?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk creation job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/delete_organization.ts b/apps/sim/tools/zendesk/delete_organization.ts new file mode 100644 index 0000000000..e733e4f75d --- /dev/null +++ b/apps/sim/tools/zendesk/delete_organization.ts @@ -0,0 +1,107 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskDeleteOrganization') + +export interface ZendeskDeleteOrganizationParams { + email: string + apiToken: string + subdomain: string + organizationId: string +} + +export interface ZendeskDeleteOrganizationResponse { + success: boolean + output: { + organization: any + metadata: { + operation: 'delete_organization' + organizationId: string + } + success: boolean + } +} + +export const zendeskDeleteOrganizationTool: ToolConfig< + ZendeskDeleteOrganizationParams, + ZendeskDeleteOrganizationResponse +> = { + id: 'zendesk_delete_organization', + name: 'Delete Organization from Zendesk', + description: 'Delete an organization from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + organizationId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Organization ID to delete', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/organizations/${params.organizationId}`), + method: 'DELETE', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response, params) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'delete_organization') + } + + // DELETE returns 204 No Content with empty body + return { + success: true, + output: { + organization: null, + metadata: { + operation: 'delete_organization' as const, + organizationId: params?.organizationId || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deleted organization data', + properties: { + organization: { type: 'object', description: 'Deleted organization object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/delete_ticket.ts b/apps/sim/tools/zendesk/delete_ticket.ts new file mode 100644 index 0000000000..20768572b8 --- /dev/null +++ b/apps/sim/tools/zendesk/delete_ticket.ts @@ -0,0 +1,107 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskDeleteTicket') + +export interface ZendeskDeleteTicketParams { + email: string + apiToken: string + subdomain: string + ticketId: string +} + +export interface ZendeskDeleteTicketResponse { + success: boolean + output: { + deleted: boolean + metadata: { + operation: 'delete_ticket' + ticketId: string + } + success: boolean + } +} + +export const zendeskDeleteTicketTool: ToolConfig< + ZendeskDeleteTicketParams, + ZendeskDeleteTicketResponse +> = { + id: 'zendesk_delete_ticket', + name: 'Delete Ticket from Zendesk', + description: 'Delete a ticket from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + ticketId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Ticket ID to delete', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/tickets/${params.ticketId}`), + method: 'DELETE', + headers: (params) => { + // Use Basic Authentication with email/token format for Zendesk API tokens + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response, params) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'delete_ticket') + } + + return { + success: true, + output: { + deleted: true, + metadata: { + operation: 'delete_ticket' as const, + ticketId: params?.ticketId || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Delete confirmation', + properties: { + deleted: { type: 'boolean', description: 'Deletion success' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/delete_user.ts b/apps/sim/tools/zendesk/delete_user.ts new file mode 100644 index 0000000000..9d4f69e98c --- /dev/null +++ b/apps/sim/tools/zendesk/delete_user.ts @@ -0,0 +1,105 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskDeleteUser') + +export interface ZendeskDeleteUserParams { + email: string + apiToken: string + subdomain: string + userId: string +} + +export interface ZendeskDeleteUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'delete_user' + userId: string + } + success: boolean + } +} + +export const zendeskDeleteUserTool: ToolConfig = + { + id: 'zendesk_delete_user', + name: 'Delete User from Zendesk', + description: 'Delete a user from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User ID to delete', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/users/${params.userId}`), + method: 'DELETE', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response, params) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'delete_user') + } + + // DELETE returns 204 No Content with empty body + return { + success: true, + output: { + user: null, + metadata: { + operation: 'delete_user' as const, + userId: params?.userId || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Deleted user data', + properties: { + user: { type: 'object', description: 'Deleted user object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/zendesk/get_current_user.ts b/apps/sim/tools/zendesk/get_current_user.ts new file mode 100644 index 0000000000..6605afa737 --- /dev/null +++ b/apps/sim/tools/zendesk/get_current_user.ts @@ -0,0 +1,99 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetCurrentUser') + +export interface ZendeskGetCurrentUserParams { + email: string + apiToken: string + subdomain: string +} + +export interface ZendeskGetCurrentUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'get_current_user' + } + success: boolean + } +} + +export const zendeskGetCurrentUserTool: ToolConfig< + ZendeskGetCurrentUserParams, + ZendeskGetCurrentUserResponse +> = { + id: 'zendesk_get_current_user', + name: 'Get Current User from Zendesk', + description: 'Get the currently authenticated user from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/users/me'), + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_current_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.user, + metadata: { + operation: 'get_current_user' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Current user data', + properties: { + user: { type: 'object', description: 'Current user object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/get_organization.ts b/apps/sim/tools/zendesk/get_organization.ts new file mode 100644 index 0000000000..a62a2cce5b --- /dev/null +++ b/apps/sim/tools/zendesk/get_organization.ts @@ -0,0 +1,106 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetOrganization') + +export interface ZendeskGetOrganizationParams { + email: string + apiToken: string + subdomain: string + organizationId: string +} + +export interface ZendeskGetOrganizationResponse { + success: boolean + output: { + organization: any + metadata: { + operation: 'get_organization' + } + success: boolean + } +} + +export const zendeskGetOrganizationTool: ToolConfig< + ZendeskGetOrganizationParams, + ZendeskGetOrganizationResponse +> = { + id: 'zendesk_get_organization', + name: 'Get Single Organization from Zendesk', + description: 'Get a single organization by ID from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + organizationId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Organization ID to retrieve', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/organizations/${params.organizationId}`), + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_organization') + } + + const data = await response.json() + + return { + success: true, + output: { + organization: data.organization, + metadata: { + operation: 'get_organization' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Organization data', + properties: { + organization: { type: 'object', description: 'Organization object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/get_organizations.ts b/apps/sim/tools/zendesk/get_organizations.ts new file mode 100644 index 0000000000..fad3c2e0af --- /dev/null +++ b/apps/sim/tools/zendesk/get_organizations.ts @@ -0,0 +1,135 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetOrganizations') + +export interface ZendeskGetOrganizationsParams { + email: string + apiToken: string + subdomain: string + perPage?: string + page?: string +} + +export interface ZendeskGetOrganizationsResponse { + success: boolean + output: { + organizations: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'get_organizations' + totalReturned: number + } + success: boolean + } +} + +export const zendeskGetOrganizationsTool: ToolConfig< + ZendeskGetOrganizationsParams, + ZendeskGetOrganizationsResponse +> = { + id: 'zendesk_get_organizations', + name: 'Get Organizations from Zendesk', + description: 'Retrieve a list of organizations from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain (e.g., "mycompany" for mycompany.zendesk.com)', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/organizations') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_organizations') + } + + const data = await response.json() + const organizations = data.organizations || [] + + return { + success: true, + output: { + organizations, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || organizations.length, + }, + metadata: { + operation: 'get_organizations' as const, + totalReturned: organizations.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Organizations data and metadata', + properties: { + organizations: { type: 'array', description: 'Array of organization objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/get_ticket.ts b/apps/sim/tools/zendesk/get_ticket.ts new file mode 100644 index 0000000000..e4af1dd5ab --- /dev/null +++ b/apps/sim/tools/zendesk/get_ticket.ts @@ -0,0 +1,104 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetTicket') + +export interface ZendeskGetTicketParams { + email: string + apiToken: string + subdomain: string + ticketId: string +} + +export interface ZendeskGetTicketResponse { + success: boolean + output: { + ticket: any + metadata: { + operation: 'get_ticket' + } + success: boolean + } +} + +export const zendeskGetTicketTool: ToolConfig = { + id: 'zendesk_get_ticket', + name: 'Get Single Ticket from Zendesk', + description: 'Get a single ticket by ID from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + ticketId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Ticket ID to retrieve', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/tickets/${params.ticketId}`), + method: 'GET', + headers: (params) => { + // Use Basic Authentication with email/token format for Zendesk API tokens + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: data.ticket, + metadata: { + operation: 'get_ticket' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Ticket data', + properties: { + ticket: { type: 'object', description: 'Ticket object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/get_tickets.ts b/apps/sim/tools/zendesk/get_tickets.ts new file mode 100644 index 0000000000..97846c68b8 --- /dev/null +++ b/apps/sim/tools/zendesk/get_tickets.ts @@ -0,0 +1,190 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetTickets') + +export interface ZendeskGetTicketsParams { + email: string + apiToken: string + subdomain: string + status?: string + priority?: string + type?: string + assigneeId?: string + organizationId?: string + sortBy?: string + sortOrder?: string + perPage?: string + page?: string +} + +export interface ZendeskGetTicketsResponse { + success: boolean + output: { + tickets: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'get_tickets' + totalReturned: number + } + success: boolean + } +} + +export const zendeskGetTicketsTool: ToolConfig = + { + id: 'zendesk_get_tickets', + name: 'Get Tickets from Zendesk', + description: 'Retrieve a list of tickets from Zendesk with optional filtering', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain (e.g., "mycompany" for mycompany.zendesk.com)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by status (new, open, pending, hold, solved, closed)', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by priority (low, normal, high, urgent)', + }, + type: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by type (problem, incident, question, task)', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by assignee user ID', + }, + organizationId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by organization ID', + }, + sortBy: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Sort field (created_at, updated_at, priority, status)', + }, + sortOrder: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Sort order (asc or desc)', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.status) queryParams.append('status', params.status) + if (params.priority) queryParams.append('priority', params.priority) + if (params.type) queryParams.append('type', params.type) + if (params.assigneeId) queryParams.append('assignee_id', params.assigneeId) + if (params.organizationId) queryParams.append('organization_id', params.organizationId) + if (params.sortBy) queryParams.append('sort_by', params.sortBy) + if (params.sortOrder) queryParams.append('sort_order', params.sortOrder) + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/tickets') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => { + // Use Basic Authentication with email/token format for Zendesk API tokens + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_tickets') + } + + const data = await response.json() + const tickets = data.tickets || [] + + return { + success: true, + output: { + tickets, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || tickets.length, + }, + metadata: { + operation: 'get_tickets' as const, + totalReturned: tickets.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Tickets data and metadata', + properties: { + tickets: { type: 'array', description: 'Array of ticket objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/zendesk/get_user.ts b/apps/sim/tools/zendesk/get_user.ts new file mode 100644 index 0000000000..7b1527c984 --- /dev/null +++ b/apps/sim/tools/zendesk/get_user.ts @@ -0,0 +1,103 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetUser') + +export interface ZendeskGetUserParams { + email: string + apiToken: string + subdomain: string + userId: string +} + +export interface ZendeskGetUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'get_user' + } + success: boolean + } +} + +export const zendeskGetUserTool: ToolConfig = { + id: 'zendesk_get_user', + name: 'Get Single User from Zendesk', + description: 'Get a single user by ID from Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User ID to retrieve', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/users/${params.userId}`), + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.user, + metadata: { + operation: 'get_user' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'User data', + properties: { + user: { type: 'object', description: 'User object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/get_users.ts b/apps/sim/tools/zendesk/get_users.ts new file mode 100644 index 0000000000..6492a1e77f --- /dev/null +++ b/apps/sim/tools/zendesk/get_users.ts @@ -0,0 +1,148 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskGetUsers') + +export interface ZendeskGetUsersParams { + email: string + apiToken: string + subdomain: string + role?: string + permissionSet?: string + perPage?: string + page?: string +} + +export interface ZendeskGetUsersResponse { + success: boolean + output: { + users: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'get_users' + totalReturned: number + } + success: boolean + } +} + +export const zendeskGetUsersTool: ToolConfig = { + id: 'zendesk_get_users', + name: 'Get Users from Zendesk', + description: 'Retrieve a list of users from Zendesk with optional filtering', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain (e.g., "mycompany" for mycompany.zendesk.com)', + }, + role: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by role (end-user, agent, admin)', + }, + permissionSet: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by permission set ID', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.role) queryParams.append('role', params.role) + if (params.permissionSet) queryParams.append('permission_set', params.permissionSet) + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/users') + return query ? `${url}?${query}` : url + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'get_users') + } + + const data = await response.json() + const users = data.users || [] + + return { + success: true, + output: { + users, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || users.length, + }, + metadata: { + operation: 'get_users' as const, + totalReturned: users.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Users data and metadata', + properties: { + users: { type: 'array', description: 'Array of user objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/index.ts b/apps/sim/tools/zendesk/index.ts new file mode 100644 index 0000000000..20e60c0e03 --- /dev/null +++ b/apps/sim/tools/zendesk/index.ts @@ -0,0 +1,26 @@ +export { zendeskAutocompleteOrganizationsTool } from './autocomplete_organizations' +export { zendeskCreateOrganizationTool } from './create_organization' +export { zendeskCreateOrganizationsBulkTool } from './create_organizations_bulk' +export { zendeskCreateTicketTool } from './create_ticket' +export { zendeskCreateTicketsBulkTool } from './create_tickets_bulk' +export { zendeskCreateUserTool } from './create_user' +export { zendeskCreateUsersBulkTool } from './create_users_bulk' +export { zendeskDeleteOrganizationTool } from './delete_organization' +export { zendeskDeleteTicketTool } from './delete_ticket' +export { zendeskDeleteUserTool } from './delete_user' +export { zendeskGetCurrentUserTool } from './get_current_user' +export { zendeskGetOrganizationTool } from './get_organization' +export { zendeskGetOrganizationsTool } from './get_organizations' +export { zendeskGetTicketTool } from './get_ticket' +export { zendeskGetTicketsTool } from './get_tickets' +export { zendeskGetUserTool } from './get_user' +export { zendeskGetUsersTool } from './get_users' +export { zendeskMergeTicketsTool } from './merge_tickets' +export { zendeskSearchTool } from './search' +export { zendeskSearchCountTool } from './search_count' +export { zendeskSearchUsersTool } from './search_users' +export { zendeskUpdateOrganizationTool } from './update_organization' +export { zendeskUpdateTicketTool } from './update_ticket' +export { zendeskUpdateTicketsBulkTool } from './update_tickets_bulk' +export { zendeskUpdateUserTool } from './update_user' +export { zendeskUpdateUsersBulkTool } from './update_users_bulk' diff --git a/apps/sim/tools/zendesk/merge_tickets.ts b/apps/sim/tools/zendesk/merge_tickets.ts new file mode 100644 index 0000000000..256ecc2ac6 --- /dev/null +++ b/apps/sim/tools/zendesk/merge_tickets.ts @@ -0,0 +1,135 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskMergeTickets') + +export interface ZendeskMergeTicketsParams { + email: string + apiToken: string + subdomain: string + targetTicketId: string + sourceTicketIds: string + targetComment?: string +} + +export interface ZendeskMergeTicketsResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'merge_tickets' + jobId?: string + targetTicketId: string + } + success: boolean + } +} + +export const zendeskMergeTicketsTool: ToolConfig< + ZendeskMergeTicketsParams, + ZendeskMergeTicketsResponse +> = { + id: 'zendesk_merge_tickets', + name: 'Merge Tickets in Zendesk', + description: 'Merge multiple tickets into a target ticket', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + targetTicketId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Target ticket ID (tickets will be merged into this one)', + }, + sourceTicketIds: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Comma-separated source ticket IDs to merge', + }, + targetComment: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comment to add to target ticket after merge', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/tickets/${params.targetTicketId}/merge`), + method: 'POST', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const ids = params.sourceTicketIds.split(',').map((id) => id.trim()) + const body: any = { ids } + if (params.targetComment) { + body.target_comment = { + body: params.targetComment, + public: true, + } + } + return body + }, + }, + + transformResponse: async (response: Response, params) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'merge_tickets') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'merge_tickets' as const, + jobId: data.job_status?.id, + targetTicketId: params?.targetTicketId || '', + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Merge job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/search.ts b/apps/sim/tools/zendesk/search.ts new file mode 100644 index 0000000000..1f76442975 --- /dev/null +++ b/apps/sim/tools/zendesk/search.ts @@ -0,0 +1,156 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskSearch') + +export interface ZendeskSearchParams { + email: string + apiToken: string + subdomain: string + query: string + sortBy?: string + sortOrder?: string + perPage?: string + page?: string +} + +export interface ZendeskSearchResponse { + success: boolean + output: { + results: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'search' + totalReturned: number + } + success: boolean + } +} + +export const zendeskSearchTool: ToolConfig = { + id: 'zendesk_search', + name: 'Search Zendesk', + description: 'Unified search across tickets, users, and organizations in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + query: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Search query string', + }, + sortBy: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Sort field (relevance, created_at, updated_at, priority, status, ticket_type)', + }, + sortOrder: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Sort order (asc or desc)', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + queryParams.append('query', params.query) + if (params.sortBy) queryParams.append('sort_by', params.sortBy) + if (params.sortOrder) queryParams.append('sort_order', params.sortOrder) + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/search') + return `${url}?${query}` + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'search') + } + + const data = await response.json() + const results = data.results || [] + + return { + success: true, + output: { + results, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || results.length, + }, + metadata: { + operation: 'search' as const, + totalReturned: results.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search results', + properties: { + results: { type: 'array', description: 'Array of result objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/search_count.ts b/apps/sim/tools/zendesk/search_count.ts new file mode 100644 index 0000000000..636cc321b7 --- /dev/null +++ b/apps/sim/tools/zendesk/search_count.ts @@ -0,0 +1,113 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskSearchCount') + +export interface ZendeskSearchCountParams { + email: string + apiToken: string + subdomain: string + query: string +} + +export interface ZendeskSearchCountResponse { + success: boolean + output: { + count: number + metadata: { + operation: 'search_count' + } + success: boolean + } +} + +export const zendeskSearchCountTool: ToolConfig< + ZendeskSearchCountParams, + ZendeskSearchCountResponse +> = { + id: 'zendesk_search_count', + name: 'Count Search Results in Zendesk', + description: 'Count the number of search results matching a query in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + query: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Search query string', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + queryParams.append('query', params.query) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/search/count') + return `${url}?${query}` + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'search_count') + } + + const data = await response.json() + + return { + success: true, + output: { + count: data.count || 0, + metadata: { + operation: 'search_count' as const, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Search count result', + properties: { + count: { type: 'number', description: 'Number of matching results' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/search_users.ts b/apps/sim/tools/zendesk/search_users.ts new file mode 100644 index 0000000000..5ad4c9a90a --- /dev/null +++ b/apps/sim/tools/zendesk/search_users.ts @@ -0,0 +1,151 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskSearchUsers') + +export interface ZendeskSearchUsersParams { + email: string + apiToken: string + subdomain: string + query?: string + externalId?: string + perPage?: string + page?: string +} + +export interface ZendeskSearchUsersResponse { + success: boolean + output: { + users: any[] + paging?: { + nextPage?: string | null + previousPage?: string | null + count: number + } + metadata: { + operation: 'search_users' + totalReturned: number + } + success: boolean + } +} + +export const zendeskSearchUsersTool: ToolConfig< + ZendeskSearchUsersParams, + ZendeskSearchUsersResponse +> = { + id: 'zendesk_search_users', + name: 'Search Users in Zendesk', + description: 'Search for users in Zendesk using a query string', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + query: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Search query string', + }, + externalId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'External ID to search by', + }, + perPage: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Results per page (default: 100, max: 100)', + }, + page: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Page number', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.query) queryParams.append('query', params.query) + if (params.externalId) queryParams.append('external_id', params.externalId) + if (params.page) queryParams.append('page', params.page) + if (params.perPage) queryParams.append('per_page', params.perPage) + + const query = queryParams.toString() + const url = buildZendeskUrl(params.subdomain, '/users/search') + return `${url}?${query}` + }, + method: 'GET', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'search_users') + } + + const data = await response.json() + const users = data.users || [] + + return { + success: true, + output: { + users, + paging: { + nextPage: data.next_page, + previousPage: data.previous_page, + count: data.count || users.length, + }, + metadata: { + operation: 'search_users' as const, + totalReturned: users.length, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Users search results', + properties: { + users: { type: 'array', description: 'Array of user objects' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/types.ts b/apps/sim/tools/zendesk/types.ts new file mode 100644 index 0000000000..aa7103b28e --- /dev/null +++ b/apps/sim/tools/zendesk/types.ts @@ -0,0 +1,48 @@ +import { createLogger } from '@/lib/logs/console/logger' + +const logger = createLogger('Zendesk') + +// Base params - following Sentry pattern where subdomain is user-provided +export interface ZendeskBaseParams { + email: string // Zendesk user email (required for API token authentication) + apiToken: string // API token (hidden) + subdomain: string // Zendesk subdomain (user-visible, required - e.g., "mycompany" for mycompany.zendesk.com) +} + +export interface ZendeskPaginationParams { + page?: string + perPage?: string +} + +export interface ZendeskPagingInfo { + nextPage?: string | null + previousPage?: string | null + count: number +} + +export interface ZendeskResponse { + success: boolean + output: { + data?: T + paging?: ZendeskPagingInfo + metadata: { + operation: string + [key: string]: any + } + success: boolean + } +} + +// Helper function to build Zendesk API URLs +// Subdomain is always provided by user as a parameter +export function buildZendeskUrl(subdomain: string, path: string): string { + return `https://${subdomain}.zendesk.com/api/v2${path}` +} + +// Helper function for consistent error handling +export function handleZendeskError(data: any, status: number, operation: string): never { + logger.error(`Zendesk API request failed for ${operation}`, { data, status }) + + const errorMessage = data.error || data.description || data.message || 'Unknown error' + throw new Error(`Zendesk ${operation} failed: ${errorMessage}`) +} diff --git a/apps/sim/tools/zendesk/update_organization.ts b/apps/sim/tools/zendesk/update_organization.ts new file mode 100644 index 0000000000..f8cb7609a4 --- /dev/null +++ b/apps/sim/tools/zendesk/update_organization.ts @@ -0,0 +1,171 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskUpdateOrganization') + +export interface ZendeskUpdateOrganizationParams { + email: string + apiToken: string + subdomain: string + organizationId: string + name?: string + domainNames?: string + details?: string + notes?: string + tags?: string + customFields?: string +} + +export interface ZendeskUpdateOrganizationResponse { + success: boolean + output: { + organization: any + metadata: { + operation: 'update_organization' + organizationId: string + } + success: boolean + } +} + +export const zendeskUpdateOrganizationTool: ToolConfig< + ZendeskUpdateOrganizationParams, + ZendeskUpdateOrganizationResponse +> = { + id: 'zendesk_update_organization', + name: 'Update Organization in Zendesk', + description: 'Update an existing organization in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + organizationId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Organization ID to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New organization name', + }, + domainNames: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated domain names', + }, + details: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization details', + }, + notes: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization notes', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/organizations/${params.organizationId}`), + method: 'PUT', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const organization: any = {} + + if (params.name) organization.name = params.name + if (params.domainNames) + organization.domain_names = params.domainNames.split(',').map((d) => d.trim()) + if (params.details) organization.details = params.details + if (params.notes) organization.notes = params.notes + if (params.tags) organization.tags = params.tags.split(',').map((t) => t.trim()) + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + organization.organization_fields = customFields + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { organization } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'update_organization') + } + + const data = await response.json() + + return { + success: true, + output: { + organization: data.organization, + metadata: { + operation: 'update_organization' as const, + organizationId: data.organization?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated organization data', + properties: { + organization: { type: 'object', description: 'Updated organization object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/update_ticket.ts b/apps/sim/tools/zendesk/update_ticket.ts new file mode 100644 index 0000000000..08196340c3 --- /dev/null +++ b/apps/sim/tools/zendesk/update_ticket.ts @@ -0,0 +1,195 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskUpdateTicket') + +export interface ZendeskUpdateTicketParams { + email: string + apiToken: string + subdomain: string + ticketId: string + subject?: string + comment?: string + priority?: string + status?: string + type?: string + tags?: string + assigneeId?: string + groupId?: string + customFields?: string +} + +export interface ZendeskUpdateTicketResponse { + success: boolean + output: { + ticket: any + metadata: { + operation: 'update_ticket' + ticketId: string + } + success: boolean + } +} + +export const zendeskUpdateTicketTool: ToolConfig< + ZendeskUpdateTicketParams, + ZendeskUpdateTicketResponse +> = { + id: 'zendesk_update_ticket', + name: 'Update Ticket in Zendesk', + description: 'Update an existing ticket in Zendesk with support for custom fields', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + ticketId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Ticket ID to update', + }, + subject: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New ticket subject', + }, + comment: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Add a comment to the ticket', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Priority (low, normal, high, urgent)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Status (new, open, pending, hold, solved, closed)', + }, + type: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Type (problem, incident, question, task)', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Assignee user ID', + }, + groupId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Group ID', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/tickets/${params.ticketId}`), + method: 'PUT', + headers: (params) => { + // Use Basic Authentication with email/token format for Zendesk API tokens + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const ticket: any = {} + + if (params.subject) ticket.subject = params.subject + if (params.priority) ticket.priority = params.priority + if (params.status) ticket.status = params.status + if (params.type) ticket.type = params.type + if (params.assigneeId) ticket.assignee_id = params.assigneeId + if (params.groupId) ticket.group_id = params.groupId + if (params.tags) ticket.tags = params.tags.split(',').map((t) => t.trim()) + if (params.comment) ticket.comment = { body: params.comment } + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + ticket.custom_fields = Object.entries(customFields).map(([id, value]) => ({ id, value })) + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { ticket } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'update_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: data.ticket, + metadata: { + operation: 'update_ticket' as const, + ticketId: data.ticket?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated ticket data', + properties: { + ticket: { type: 'object', description: 'Updated ticket object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/update_tickets_bulk.ts b/apps/sim/tools/zendesk/update_tickets_bulk.ts new file mode 100644 index 0000000000..19c602855b --- /dev/null +++ b/apps/sim/tools/zendesk/update_tickets_bulk.ts @@ -0,0 +1,155 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskUpdateTicketsBulk') + +export interface ZendeskUpdateTicketsBulkParams { + email: string + apiToken: string + subdomain: string + ticketIds: string + status?: string + priority?: string + assigneeId?: string + groupId?: string + tags?: string +} + +export interface ZendeskUpdateTicketsBulkResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'update_tickets_bulk' + jobId?: string + } + success: boolean + } +} + +export const zendeskUpdateTicketsBulkTool: ToolConfig< + ZendeskUpdateTicketsBulkParams, + ZendeskUpdateTicketsBulkResponse +> = { + id: 'zendesk_update_tickets_bulk', + name: 'Bulk Update Tickets in Zendesk', + description: 'Update multiple tickets in Zendesk at once (max 100)', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + ticketIds: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Comma-separated ticket IDs to update (max 100)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New status for all tickets', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New priority for all tickets', + }, + assigneeId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New assignee ID for all tickets', + }, + groupId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New group ID for all tickets', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags to add to all tickets', + }, + }, + + request: { + url: (params) => { + const ids = params.ticketIds.split(',').map((id) => id.trim()) + return buildZendeskUrl(params.subdomain, `/tickets/update_many?ids=${ids.join(',')}`) + }, + method: 'PUT', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const ticket: any = {} + if (params.status) ticket.status = params.status + if (params.priority) ticket.priority = params.priority + if (params.assigneeId) ticket.assignee_id = params.assigneeId + if (params.groupId) ticket.group_id = params.groupId + if (params.tags) ticket.tags = params.tags.split(',').map((t) => t.trim()) + return { ticket } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'update_tickets_bulk') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'update_tickets_bulk' as const, + jobId: data.job_status?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk update job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +} diff --git a/apps/sim/tools/zendesk/update_user.ts b/apps/sim/tools/zendesk/update_user.ts new file mode 100644 index 0000000000..2e529e0274 --- /dev/null +++ b/apps/sim/tools/zendesk/update_user.ts @@ -0,0 +1,184 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskUpdateUser') + +export interface ZendeskUpdateUserParams { + email: string + apiToken: string + subdomain: string + userId: string + name?: string + userEmail?: string + role?: string + phone?: string + organizationId?: string + verified?: string + tags?: string + customFields?: string +} + +export interface ZendeskUpdateUserResponse { + success: boolean + output: { + user: any + metadata: { + operation: 'update_user' + userId: string + } + success: boolean + } +} + +export const zendeskUpdateUserTool: ToolConfig = + { + id: 'zendesk_update_user', + name: 'Update User in Zendesk', + description: 'Update an existing user in Zendesk', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + userId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'User ID to update', + }, + name: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New user name', + }, + userEmail: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'New user email', + }, + role: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User role (end-user, agent, admin)', + }, + phone: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'User phone number', + }, + organizationId: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Organization ID', + }, + verified: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Set to "true" to mark user as verified', + }, + tags: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Comma-separated tags', + }, + customFields: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Custom fields as JSON object', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, `/users/${params.userId}`), + method: 'PUT', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + const user: any = {} + + if (params.name) user.name = params.name + if (params.userEmail) user.email = params.userEmail + if (params.role) user.role = params.role + if (params.phone) user.phone = params.phone + if (params.organizationId) user.organization_id = params.organizationId + if (params.verified) user.verified = params.verified === 'true' + if (params.tags) user.tags = params.tags.split(',').map((t) => t.trim()) + + if (params.customFields) { + try { + const customFields = JSON.parse(params.customFields) + user.user_fields = customFields + } catch (error) { + logger.warn('Failed to parse custom fields', { error }) + } + } + + return { user } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'update_user') + } + + const data = await response.json() + + return { + success: true, + output: { + user: data.user, + metadata: { + operation: 'update_user' as const, + userId: data.user?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Updated user data', + properties: { + user: { type: 'object', description: 'Updated user object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, + } diff --git a/apps/sim/tools/zendesk/update_users_bulk.ts b/apps/sim/tools/zendesk/update_users_bulk.ts new file mode 100644 index 0000000000..bafd9fba08 --- /dev/null +++ b/apps/sim/tools/zendesk/update_users_bulk.ts @@ -0,0 +1,117 @@ +import { createLogger } from '@/lib/logs/console/logger' +import type { ToolConfig } from '@/tools/types' +import { buildZendeskUrl, handleZendeskError } from './types' + +const logger = createLogger('ZendeskUpdateUsersBulk') + +export interface ZendeskUpdateUsersBulkParams { + email: string + apiToken: string + subdomain: string + users: string +} + +export interface ZendeskUpdateUsersBulkResponse { + success: boolean + output: { + jobStatus: any + metadata: { + operation: 'update_users_bulk' + jobId: string + } + success: boolean + } +} + +export const zendeskUpdateUsersBulkTool: ToolConfig< + ZendeskUpdateUsersBulkParams, + ZendeskUpdateUsersBulkResponse +> = { + id: 'zendesk_update_users_bulk', + name: 'Bulk Update Users in Zendesk', + description: 'Update multiple users in Zendesk using bulk update', + version: '1.0.0', + + params: { + email: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk email address', + }, + apiToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Zendesk API token', + }, + subdomain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Zendesk subdomain', + }, + users: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'JSON array of user objects to update (must include id field)', + }, + }, + + request: { + url: (params) => buildZendeskUrl(params.subdomain, '/users/update_many'), + method: 'PUT', + headers: (params) => { + const credentials = `${params.email}/token:${params.apiToken}` + const base64Credentials = Buffer.from(credentials).toString('base64') + return { + Authorization: `Basic ${base64Credentials}`, + 'Content-Type': 'application/json', + } + }, + body: (params) => { + try { + const users = JSON.parse(params.users) + return { users } + } catch (error) { + logger.error('Failed to parse users array', { error }) + throw new Error('Invalid users JSON format') + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleZendeskError(data, response.status, 'update_users_bulk') + } + + const data = await response.json() + + return { + success: true, + output: { + jobStatus: data.job_status, + metadata: { + operation: 'update_users_bulk' as const, + jobId: data.job_status?.id, + }, + success: true, + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Operation success status' }, + output: { + type: 'object', + description: 'Bulk update job status', + properties: { + jobStatus: { type: 'object', description: 'Job status object' }, + metadata: { type: 'object', description: 'Operation metadata' }, + success: { type: 'boolean', description: 'Operation success' }, + }, + }, + }, +}