diff --git a/apps/docs/content/docs/en/tools/intercom.mdx b/apps/docs/content/docs/en/tools/intercom.mdx index 2b21d1cba..f79707472 100644 --- a/apps/docs/content/docs/en/tools/intercom.mdx +++ b/apps/docs/content/docs/en/tools/intercom.mdx @@ -647,6 +647,42 @@ Retrieve a single ticket by ID from Intercom. Returns API-aligned fields only. | `ticketId` | string | ID of the retrieved ticket | | `success` | boolean | Operation success status | +### `intercom_update_ticket` + +Update a ticket in Intercom (change state, assignment, attributes) + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `ticketId` | string | Yes | The ID of the ticket to update | +| `ticket_attributes` | string | No | JSON object with ticket attributes \(e.g., \{"_default_title_":"New Title","_default_description_":"Updated description"\}\) | +| `open` | boolean | No | Set to false to close the ticket, true to keep it open | +| `is_shared` | boolean | No | Whether the ticket is visible to users | +| `snoozed_until` | number | No | Unix timestamp for when the ticket should reopen | +| `admin_id` | string | No | The ID of the admin performing the update \(needed for workflows and attribution\) | +| `assignee_id` | string | No | The ID of the admin or team to assign the ticket to. Set to "0" to unassign. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `ticket` | object | The updated ticket object | +| ↳ `id` | string | Unique identifier for the ticket | +| ↳ `type` | string | Object type \(ticket\) | +| ↳ `ticket_id` | string | Ticket ID shown in Intercom UI | +| ↳ `ticket_state` | string | State of the ticket | +| ↳ `ticket_attributes` | object | Attributes of the ticket | +| ↳ `open` | boolean | Whether the ticket is open | +| ↳ `is_shared` | boolean | Whether the ticket is visible to users | +| ↳ `snoozed_until` | number | Unix timestamp when ticket will reopen | +| ↳ `admin_assignee_id` | string | ID of assigned admin | +| ↳ `team_assignee_id` | string | ID of assigned team | +| ↳ `created_at` | number | Unix timestamp when ticket was created | +| ↳ `updated_at` | number | Unix timestamp when ticket was last updated | +| `ticketId` | string | ID of the updated ticket | +| `ticket_state` | string | Current state of the ticket | + ### `intercom_create_message` Create and send a new admin-initiated message in Intercom. Returns API-aligned fields only. @@ -680,4 +716,340 @@ Create and send a new admin-initiated message in Intercom. Returns API-aligned f | `messageId` | string | ID of the created message | | `success` | boolean | Operation success status | +### `intercom_list_admins` + +Fetch a list of all admins for the workspace + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `admins` | array | Array of admin objects | +| ↳ `id` | string | Unique identifier for the admin | +| ↳ `type` | string | Object type \(admin\) | +| ↳ `name` | string | Name of the admin | +| ↳ `email` | string | Email of the admin | +| ↳ `job_title` | string | Job title of the admin | +| ↳ `away_mode_enabled` | boolean | Whether admin is in away mode | +| ↳ `away_mode_reassign` | boolean | Whether to reassign conversations when away | +| ↳ `has_inbox_seat` | boolean | Whether admin has a paid inbox seat | +| ↳ `team_ids` | array | List of team IDs the admin belongs to | +| ↳ `avatar` | object | Avatar information | +| ↳ `email_verified` | boolean | Whether email is verified | +| `type` | string | Object type \(admin.list\) | + +### `intercom_close_conversation` + +Close a conversation in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | The ID of the conversation to close | +| `admin_id` | string | Yes | The ID of the admin performing the action | +| `body` | string | No | Optional closing message to add to the conversation | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `conversation` | object | The closed conversation object | +| ↳ `id` | string | Unique identifier for the conversation | +| ↳ `type` | string | Object type \(conversation\) | +| ↳ `state` | string | State of the conversation \(closed\) | +| ↳ `open` | boolean | Whether the conversation is open \(false\) | +| ↳ `read` | boolean | Whether the conversation has been read | +| ↳ `created_at` | number | Unix timestamp when conversation was created | +| ↳ `updated_at` | number | Unix timestamp when conversation was last updated | +| `conversationId` | string | ID of the closed conversation | +| `state` | string | State of the conversation \(closed\) | + +### `intercom_open_conversation` + +Open a closed or snoozed conversation in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | The ID of the conversation to open | +| `admin_id` | string | Yes | The ID of the admin performing the action | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `conversation` | object | The opened conversation object | +| ↳ `id` | string | Unique identifier for the conversation | +| ↳ `type` | string | Object type \(conversation\) | +| ↳ `state` | string | State of the conversation \(open\) | +| ↳ `open` | boolean | Whether the conversation is open \(true\) | +| ↳ `read` | boolean | Whether the conversation has been read | +| ↳ `created_at` | number | Unix timestamp when conversation was created | +| ↳ `updated_at` | number | Unix timestamp when conversation was last updated | +| `conversationId` | string | ID of the opened conversation | +| `state` | string | State of the conversation \(open\) | + +### `intercom_snooze_conversation` + +Snooze a conversation to reopen at a future time + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | The ID of the conversation to snooze | +| `admin_id` | string | Yes | The ID of the admin performing the action | +| `snoozed_until` | number | Yes | Unix timestamp for when the conversation should reopen | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `conversation` | object | The snoozed conversation object | +| ↳ `id` | string | Unique identifier for the conversation | +| ↳ `type` | string | Object type \(conversation\) | +| ↳ `state` | string | State of the conversation \(snoozed\) | +| ↳ `open` | boolean | Whether the conversation is open | +| ↳ `snoozed_until` | number | Unix timestamp when conversation will reopen | +| ↳ `created_at` | number | Unix timestamp when conversation was created | +| ↳ `updated_at` | number | Unix timestamp when conversation was last updated | +| `conversationId` | string | ID of the snoozed conversation | +| `state` | string | State of the conversation \(snoozed\) | +| `snoozed_until` | number | Unix timestamp when conversation will reopen | + +### `intercom_assign_conversation` + +Assign a conversation to an admin or team in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | The ID of the conversation to assign | +| `admin_id` | string | Yes | The ID of the admin performing the assignment | +| `assignee_id` | string | Yes | The ID of the admin or team to assign the conversation to. Set to "0" to unassign. | +| `body` | string | No | Optional message to add when assigning \(e.g., "Passing to the support team"\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `conversation` | object | The assigned conversation object | +| ↳ `id` | string | Unique identifier for the conversation | +| ↳ `type` | string | Object type \(conversation\) | +| ↳ `state` | string | State of the conversation | +| ↳ `open` | boolean | Whether the conversation is open | +| ↳ `admin_assignee_id` | number | ID of the assigned admin | +| ↳ `team_assignee_id` | string | ID of the assigned team | +| ↳ `created_at` | number | Unix timestamp when conversation was created | +| ↳ `updated_at` | number | Unix timestamp when conversation was last updated | +| `conversationId` | string | ID of the assigned conversation | +| `admin_assignee_id` | number | ID of the assigned admin | +| `team_assignee_id` | string | ID of the assigned team | + +### `intercom_list_tags` + +Fetch a list of all tags in the workspace + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tags` | array | Array of tag objects | +| ↳ `id` | string | Unique identifier for the tag | +| ↳ `type` | string | Object type \(tag\) | +| ↳ `name` | string | Name of the tag | +| `type` | string | Object type \(list\) | + +### `intercom_create_tag` + +Create a new tag or update an existing tag name + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `name` | string | Yes | The name of the tag. Will create a new tag if not found, or update the name if id is provided. | +| `id` | string | No | The ID of an existing tag to update. Omit to create a new tag. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Unique identifier for the tag | +| `name` | string | Name of the tag | +| `type` | string | Object type \(tag\) | + +### `intercom_tag_contact` + +Add a tag to a specific contact + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | The ID of the contact to tag | +| `tagId` | string | Yes | The ID of the tag to apply | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Unique identifier for the tag | +| `name` | string | Name of the tag | +| `type` | string | Object type \(tag\) | + +### `intercom_untag_contact` + +Remove a tag from a specific contact + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | The ID of the contact to untag | +| `tagId` | string | Yes | The ID of the tag to remove | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Unique identifier for the tag that was removed | +| `name` | string | Name of the tag that was removed | +| `type` | string | Object type \(tag\) | + +### `intercom_tag_conversation` + +Add a tag to a specific conversation + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `conversationId` | string | Yes | The ID of the conversation to tag | +| `tagId` | string | Yes | The ID of the tag to apply | +| `admin_id` | string | Yes | The ID of the admin applying the tag | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Unique identifier for the tag | +| `name` | string | Name of the tag | +| `type` | string | Object type \(tag\) | + +### `intercom_create_note` + +Add a note to a specific contact + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | The ID of the contact to add the note to | +| `body` | string | Yes | The text content of the note | +| `admin_id` | string | No | The ID of the admin creating the note | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Unique identifier for the note | +| `body` | string | The text content of the note | +| `created_at` | number | Unix timestamp when the note was created | +| `type` | string | Object type \(note\) | +| `author` | object | The admin who created the note | +| ↳ `type` | string | Author type \(admin\) | +| ↳ `id` | string | Author ID | +| ↳ `name` | string | Author name | +| ↳ `email` | string | Author email | +| `contact` | object | The contact the note was created for | +| ↳ `type` | string | Contact type | +| ↳ `id` | string | Contact ID | + +### `intercom_create_event` + +Track a custom event for a contact in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `event_name` | string | Yes | The name of the event \(e.g., "order-completed"\). Use past-tense verb-noun format for readability. | +| `created_at` | number | No | Unix timestamp for when the event occurred. Strongly recommended for uniqueness. | +| `user_id` | string | No | Your identifier for the user \(external_id\) | +| `email` | string | No | Email address of the user. Use only if your app uses email to uniquely identify users. | +| `id` | string | No | The Intercom contact ID | +| `metadata` | string | No | JSON object with up to 10 metadata key-value pairs about the event \(e.g., \{"order_value": 99.99\}\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `accepted` | boolean | Whether the event was accepted \(202 Accepted\) | + +### `intercom_attach_contact_to_company` + +Attach a contact to a company in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | The ID of the contact to attach to the company | +| `companyId` | string | Yes | The ID of the company to attach the contact to | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `company` | object | The company object the contact was attached to | +| ↳ `id` | string | Unique identifier for the company | +| ↳ `type` | string | Object type \(company\) | +| ↳ `company_id` | string | The company_id you defined | +| ↳ `name` | string | Name of the company | +| ↳ `created_at` | number | Unix timestamp when company was created | +| ↳ `updated_at` | number | Unix timestamp when company was updated | +| ↳ `user_count` | number | Number of users in the company | +| ↳ `session_count` | number | Number of sessions | +| ↳ `monthly_spend` | number | Monthly spend amount | +| ↳ `plan` | object | Company plan details | +| `companyId` | string | ID of the company | +| `name` | string | Name of the company | + +### `intercom_detach_contact_from_company` + +Remove a contact from a company in Intercom + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `contactId` | string | Yes | The ID of the contact to detach from the company | +| `companyId` | string | Yes | The ID of the company to detach the contact from | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `company` | object | The company object the contact was detached from | +| ↳ `id` | string | Unique identifier for the company | +| ↳ `type` | string | Object type \(company\) | +| ↳ `company_id` | string | The company_id you defined | +| ↳ `name` | string | Name of the company | +| `companyId` | string | ID of the company | +| `name` | string | Name of the company | + diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx index f883a96fb..ec52e6bce 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx @@ -58,7 +58,6 @@ export function ExecutionSnapshot({ onClose = () => {}, }: ExecutionSnapshotProps) { const { data, isLoading, error } = useExecutionSnapshot(executionId) - const lastExecutionIdRef = useRef(null) const [isMenuOpen, setIsMenuOpen] = useState(false) const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }) @@ -82,12 +81,6 @@ export function ExecutionSnapshot({ const workflowState = data?.workflowState as WorkflowState | undefined - // Track execution ID changes for key reset - const executionKey = executionId !== lastExecutionIdRef.current ? executionId : undefined - if (executionId !== lastExecutionIdRef.current) { - lastExecutionIdRef.current = executionId - } - const renderContent = () => { if (isLoading) { return ( @@ -152,7 +145,7 @@ export function ExecutionSnapshot({ return ( 'create_contact', }, @@ -384,7 +399,15 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, required: true, condition: { field: 'operation', - value: ['get_conversation', 'reply_conversation'], + value: [ + 'get_conversation', + 'reply_conversation', + 'close_conversation', + 'open_conversation', + 'snooze_conversation', + 'assign_conversation', + 'tag_conversation', + ], }, }, { @@ -477,11 +500,20 @@ Return ONLY the message text - no explanations.`, id: 'admin_id', title: 'Admin ID', type: 'short-input', - placeholder: 'ID of the admin sending the message', + placeholder: 'ID of the admin performing the action', required: true, condition: { field: 'operation', - value: ['reply_conversation'], + value: [ + 'reply_conversation', + 'close_conversation', + 'open_conversation', + 'snooze_conversation', + 'assign_conversation', + 'tag_conversation', + 'create_note', + 'update_ticket', + ], }, }, { @@ -526,7 +558,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, required: true, condition: { field: 'operation', - value: ['get_ticket'], + value: ['get_ticket', 'update_ticket'], }, }, { @@ -799,6 +831,307 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, value: ['list_companies'], }, }, + // Close/Open conversation body + { + id: 'close_body', + title: 'Closing Message', + type: 'long-input', + placeholder: 'Optional message to add when closing', + condition: { + field: 'operation', + value: ['close_conversation'], + }, + }, + // Snooze conversation + { + id: 'snoozed_until', + title: 'Snooze Until', + type: 'short-input', + placeholder: 'Unix timestamp when conversation should reopen', + required: true, + condition: { + field: 'operation', + value: ['snooze_conversation'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a Unix timestamp in seconds based on the user's description. +The timestamp should be a Unix epoch time in seconds (10 digits). +Examples: +- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp +- "in 2 hours" -> Current time plus 7200 seconds +- "next Monday" -> Next Monday at 09:00:00 as Unix timestamp + +Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, + placeholder: 'Describe when to unsnooze (e.g., "tomorrow", "in 2 hours")...', + generationType: 'timestamp', + }, + }, + // Assign conversation + { + id: 'assignee_id', + title: 'Assignee ID', + type: 'short-input', + placeholder: 'Admin or team ID to assign to (0 to unassign)', + required: true, + condition: { + field: 'operation', + value: ['assign_conversation'], + }, + }, + { + id: 'assign_body', + title: 'Assignment Message', + type: 'long-input', + placeholder: 'Optional message when assigning', + condition: { + field: 'operation', + value: ['assign_conversation'], + }, + }, + // Update ticket fields + { + id: 'update_ticket_attributes', + title: 'Ticket Attributes', + type: 'long-input', + placeholder: 'JSON object with ticket attributes to update', + condition: { + field: 'operation', + value: ['update_ticket'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a JSON object for Intercom ticket attributes based on the user's description. +Example: {"_default_title_": "Updated title", "_default_description_": "Updated description"} + +Return ONLY the JSON object - no explanations or markdown formatting.`, + placeholder: 'Describe the ticket updates (e.g., "change title to Bug Fixed")...', + generationType: 'json-object', + }, + }, + { + id: 'ticket_open', + title: 'Ticket Open', + type: 'dropdown', + options: [ + { label: 'Keep Open', id: 'true' }, + { label: 'Close Ticket', id: 'false' }, + ], + condition: { + field: 'operation', + value: ['update_ticket'], + }, + }, + { + id: 'ticket_is_shared', + title: 'Ticket Visible to Users', + type: 'dropdown', + options: [ + { label: 'Yes', id: 'true' }, + { label: 'No', id: 'false' }, + ], + condition: { + field: 'operation', + value: ['update_ticket'], + }, + }, + { + id: 'ticket_snoozed_until', + title: 'Snooze Ticket Until', + type: 'short-input', + placeholder: 'Unix timestamp when ticket should reopen', + condition: { + field: 'operation', + value: ['update_ticket'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a Unix timestamp in seconds based on the user's description. +Examples: +- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp +- "next week" -> 7 days from now + +Return ONLY the numeric timestamp.`, + placeholder: 'Describe when to unsnooze (e.g., "tomorrow")...', + generationType: 'timestamp', + }, + }, + { + id: 'ticket_assignee_id', + title: 'Ticket Assignee ID', + type: 'short-input', + placeholder: 'Admin or team ID to assign to (0 to unassign)', + condition: { + field: 'operation', + value: ['update_ticket'], + }, + }, + // Tag fields + { + id: 'tagId', + title: 'Tag ID', + type: 'short-input', + placeholder: 'ID of the tag', + required: true, + condition: { + field: 'operation', + value: ['tag_contact', 'untag_contact', 'tag_conversation'], + }, + }, + { + id: 'tag_name', + title: 'Tag Name', + type: 'short-input', + placeholder: 'Name of the tag to create', + required: true, + condition: { + field: 'operation', + value: ['create_tag'], + }, + }, + { + id: 'tag_id_update', + title: 'Tag ID (for update)', + type: 'short-input', + placeholder: 'ID of existing tag to update (leave empty to create new)', + condition: { + field: 'operation', + value: ['create_tag'], + }, + }, + // Contact ID for tag/untag/note operations + { + id: 'tag_contact_id', + title: 'Contact ID', + type: 'short-input', + placeholder: 'ID of the contact', + required: true, + condition: { + field: 'operation', + value: [ + 'tag_contact', + 'untag_contact', + 'create_note', + 'attach_contact_to_company', + 'detach_contact_from_company', + ], + }, + }, + // Note fields + { + id: 'note_body', + title: 'Note Content', + type: 'long-input', + placeholder: 'Text content of the note', + required: true, + condition: { + field: 'operation', + value: ['create_note'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a note for Intercom based on the user's description. +The note should be clear, professional, and capture the key information. + +Return ONLY the note text - no explanations.`, + placeholder: 'Describe the note content (e.g., "customer requested callback")...', + }, + }, + // Event fields + { + id: 'event_name', + title: 'Event Name', + type: 'short-input', + placeholder: 'Event name (e.g., order-completed)', + required: true, + condition: { + field: 'operation', + value: ['create_event'], + }, + }, + { + id: 'event_user_id', + title: 'User ID', + type: 'short-input', + placeholder: 'Your identifier for the user', + condition: { + field: 'operation', + value: ['create_event'], + }, + }, + { + id: 'event_email', + title: 'User Email', + type: 'short-input', + placeholder: 'Email address of the user', + condition: { + field: 'operation', + value: ['create_event'], + }, + }, + { + id: 'event_contact_id', + title: 'Contact ID', + type: 'short-input', + placeholder: 'Intercom contact ID', + condition: { + field: 'operation', + value: ['create_event'], + }, + }, + { + id: 'event_metadata', + title: 'Event Metadata', + type: 'long-input', + placeholder: 'JSON object with event metadata (max 10 keys)', + condition: { + field: 'operation', + value: ['create_event'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a JSON object for Intercom event metadata based on the user's description. +The object should contain key-value pairs (max 10 keys). +Example: {"order_value": 99.99, "items": 3, "coupon_used": true} + +Return ONLY the JSON object - no explanations or markdown formatting.`, + placeholder: 'Describe the event data (e.g., "order value $50, 2 items")...', + generationType: 'json-object', + }, + }, + { + id: 'event_created_at', + title: 'Event Time', + type: 'short-input', + placeholder: 'Unix timestamp when event occurred', + condition: { + field: 'operation', + value: ['create_event'], + }, + wandConfig: { + enabled: true, + prompt: `Generate a Unix timestamp in seconds based on the user's description. +Examples: +- "now" -> Current Unix timestamp +- "5 minutes ago" -> Current time minus 300 seconds + +Return ONLY the numeric timestamp.`, + placeholder: 'Describe when the event occurred (e.g., "now")...', + generationType: 'timestamp', + }, + }, + // Company attachment fields + { + id: 'attach_company_id', + title: 'Company ID', + type: 'short-input', + placeholder: 'ID of the company to attach/detach', + required: true, + condition: { + field: 'operation', + value: ['attach_contact_to_company', 'detach_contact_from_company'], + }, + }, ], tools: { access: [ @@ -818,6 +1151,21 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, 'intercom_create_ticket', 'intercom_get_ticket', 'intercom_create_message', + 'intercom_update_ticket_v2', + 'intercom_list_admins_v2', + 'intercom_close_conversation_v2', + 'intercom_open_conversation_v2', + 'intercom_snooze_conversation_v2', + 'intercom_assign_conversation_v2', + 'intercom_list_tags_v2', + 'intercom_create_tag_v2', + 'intercom_tag_contact_v2', + 'intercom_untag_contact_v2', + 'intercom_tag_conversation_v2', + 'intercom_create_note_v2', + 'intercom_create_event_v2', + 'intercom_attach_contact_to_company_v2', + 'intercom_detach_contact_from_company_v2', ], config: { tool: (params) => { @@ -854,6 +1202,36 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, return 'intercom_get_ticket' case 'create_message': return 'intercom_create_message' + case 'update_ticket': + return 'intercom_update_ticket_v2' + case 'list_admins': + return 'intercom_list_admins_v2' + case 'close_conversation': + return 'intercom_close_conversation_v2' + case 'open_conversation': + return 'intercom_open_conversation_v2' + case 'snooze_conversation': + return 'intercom_snooze_conversation_v2' + case 'assign_conversation': + return 'intercom_assign_conversation_v2' + case 'list_tags': + return 'intercom_list_tags_v2' + case 'create_tag': + return 'intercom_create_tag_v2' + case 'tag_contact': + return 'intercom_tag_contact_v2' + case 'untag_contact': + return 'intercom_untag_contact_v2' + case 'tag_conversation': + return 'intercom_tag_conversation_v2' + case 'create_note': + return 'intercom_create_note_v2' + case 'create_event': + return 'intercom_create_event_v2' + case 'attach_contact_to_company': + return 'intercom_attach_contact_to_company_v2' + case 'detach_contact_from_company': + return 'intercom_detach_contact_from_company_v2' default: throw new Error(`Unknown operation: ${params.operation}`) } @@ -870,6 +1248,23 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, message_created_at, include_translations, disable_notifications, + close_body, + assign_body, + tag_contact_id, + attach_company_id, + update_ticket_attributes, + ticket_open, + ticket_is_shared, + ticket_snoozed_until, + ticket_assignee_id, + tag_name, + tag_id_update, + note_body, + event_user_id, + event_email, + event_contact_id, + event_metadata, + event_created_at, ...rest } = params const cleanParams: Record = {} @@ -897,7 +1292,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, cleanParams.created_at = Number(reply_created_at) } - // Map ticket fields + // Map ticket fields for create_ticket if (operation === 'create_ticket') { if (ticket_company_id) cleanParams.company_id = ticket_company_id if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at) @@ -920,6 +1315,71 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`, cleanParams.include_translations = include_translations === 'true' } + // Map close_body to body for close_conversation + if (operation === 'close_conversation' && close_body) { + cleanParams.body = close_body + } + + // Map assign_body to body for assign_conversation + if (operation === 'assign_conversation' && assign_body) { + cleanParams.body = assign_body + } + + // Map tag_contact_id to contactId for tag/note/company attachment operations + if ( + [ + 'tag_contact', + 'untag_contact', + 'create_note', + 'attach_contact_to_company', + 'detach_contact_from_company', + ].includes(operation) && + tag_contact_id + ) { + cleanParams.contactId = tag_contact_id + } + + // Map attach_company_id to companyId for company attachment operations + if ( + ['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) && + attach_company_id + ) { + cleanParams.companyId = attach_company_id + } + + // Map update_ticket fields + if (operation === 'update_ticket') { + if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes + if (ticket_open !== undefined && ticket_open !== '') { + cleanParams.open = ticket_open === 'true' + } + if (ticket_is_shared !== undefined && ticket_is_shared !== '') { + cleanParams.is_shared = ticket_is_shared === 'true' + } + if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until) + if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id + } + + // Map tag fields for create_tag + if (operation === 'create_tag') { + if (tag_name) cleanParams.name = tag_name + if (tag_id_update) cleanParams.id = tag_id_update + } + + // Map note_body to body for create_note + if (operation === 'create_note' && note_body) { + cleanParams.body = note_body + } + + // Map event fields for create_event + if (operation === 'create_event') { + if (event_user_id) cleanParams.user_id = event_user_id + if (event_email) cleanParams.email = event_email + if (event_contact_id) cleanParams.id = event_contact_id + if (event_metadata) cleanParams.metadata = event_metadata + if (event_created_at) cleanParams.created_at = Number(event_created_at) + } + Object.entries(rest).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { cleanParams[key] = value @@ -963,7 +1423,22 @@ export const IntercomV2Block: BlockConfig = { 'intercom_search_conversations_v2', 'intercom_create_ticket_v2', 'intercom_get_ticket_v2', + 'intercom_update_ticket_v2', 'intercom_create_message_v2', + 'intercom_list_admins_v2', + 'intercom_close_conversation_v2', + 'intercom_open_conversation_v2', + 'intercom_snooze_conversation_v2', + 'intercom_assign_conversation_v2', + 'intercom_list_tags_v2', + 'intercom_create_tag_v2', + 'intercom_tag_contact_v2', + 'intercom_untag_contact_v2', + 'intercom_tag_conversation_v2', + 'intercom_create_note_v2', + 'intercom_create_event_v2', + 'intercom_attach_contact_to_company_v2', + 'intercom_detach_contact_from_company_v2', ], config: { tool: createVersionedToolSelector({ @@ -999,8 +1474,38 @@ export const IntercomV2Block: BlockConfig = { return 'intercom_create_ticket' case 'get_ticket': return 'intercom_get_ticket' + case 'update_ticket': + return 'intercom_update_ticket' case 'create_message': return 'intercom_create_message' + case 'list_admins': + return 'intercom_list_admins' + case 'close_conversation': + return 'intercom_close_conversation' + case 'open_conversation': + return 'intercom_open_conversation' + case 'snooze_conversation': + return 'intercom_snooze_conversation' + case 'assign_conversation': + return 'intercom_assign_conversation' + case 'list_tags': + return 'intercom_list_tags' + case 'create_tag': + return 'intercom_create_tag' + case 'tag_contact': + return 'intercom_tag_contact' + case 'untag_contact': + return 'intercom_untag_contact' + case 'tag_conversation': + return 'intercom_tag_conversation' + case 'create_note': + return 'intercom_create_note' + case 'create_event': + return 'intercom_create_event' + case 'attach_contact_to_company': + return 'intercom_attach_contact_to_company' + case 'detach_contact_from_company': + return 'intercom_detach_contact_from_company' default: return 'intercom_create_contact' } @@ -1008,7 +1513,158 @@ export const IntercomV2Block: BlockConfig = { suffix: '_v2', fallbackToolId: 'intercom_create_contact_v2', }), - params: IntercomBlock.tools!.config!.params, + params: (params) => { + const { + operation, + message_type_msg, + company_name, + contact_company_id, + reply_created_at, + ticket_company_id, + ticket_created_at, + message_created_at, + include_translations, + disable_notifications, + close_body, + assign_body, + tag_contact_id, + attach_company_id, + update_ticket_attributes, + ticket_open, + ticket_is_shared, + ticket_snoozed_until, + ticket_assignee_id, + tag_name, + tag_id_update, + note_body, + event_user_id, + event_email, + event_contact_id, + event_metadata, + event_created_at, + ...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 + } + + // Map contact_company_id to company_id for contact operations + if ( + (operation === 'create_contact' || operation === 'update_contact') && + contact_company_id + ) { + cleanParams.company_id = contact_company_id + } + + // Map reply_created_at to created_at for reply_conversation + if (operation === 'reply_conversation' && reply_created_at) { + cleanParams.created_at = Number(reply_created_at) + } + + // Map ticket fields for create_ticket + if (operation === 'create_ticket') { + if (ticket_company_id) cleanParams.company_id = ticket_company_id + if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at) + if (disable_notifications !== undefined && disable_notifications !== '') { + cleanParams.disable_notifications = disable_notifications === 'true' + } + } + + // Map message_created_at to created_at for create_message + if (operation === 'create_message' && message_created_at) { + cleanParams.created_at = Number(message_created_at) + } + + // Convert include_translations string to boolean for get_conversation + if ( + operation === 'get_conversation' && + include_translations !== undefined && + include_translations !== '' + ) { + cleanParams.include_translations = include_translations === 'true' + } + + // Map close_body to body for close_conversation + if (operation === 'close_conversation' && close_body) { + cleanParams.body = close_body + } + + // Map assign_body to body for assign_conversation + if (operation === 'assign_conversation' && assign_body) { + cleanParams.body = assign_body + } + + // Map tag_contact_id to contactId for tag/note/company attachment operations + if ( + [ + 'tag_contact', + 'untag_contact', + 'create_note', + 'attach_contact_to_company', + 'detach_contact_from_company', + ].includes(operation) && + tag_contact_id + ) { + cleanParams.contactId = tag_contact_id + } + + // Map attach_company_id to companyId for company attachment operations + if ( + ['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) && + attach_company_id + ) { + cleanParams.companyId = attach_company_id + } + + // Map update_ticket fields + if (operation === 'update_ticket') { + if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes + if (ticket_open !== undefined && ticket_open !== '') { + cleanParams.open = ticket_open === 'true' + } + if (ticket_is_shared !== undefined && ticket_is_shared !== '') { + cleanParams.is_shared = ticket_is_shared === 'true' + } + if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until) + if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id + } + + // Map tag fields for create_tag + if (operation === 'create_tag') { + if (tag_name) cleanParams.name = tag_name + if (tag_id_update) cleanParams.id = tag_id_update + } + + // Map note_body to body for create_note + if (operation === 'create_note' && note_body) { + cleanParams.body = note_body + } + + // Map event fields for create_event + if (operation === 'create_event') { + if (event_user_id) cleanParams.user_id = event_user_id + if (event_email) cleanParams.email = event_email + if (event_contact_id) cleanParams.id = event_contact_id + if (event_metadata) cleanParams.metadata = event_metadata + if (event_created_at) cleanParams.created_at = Number(event_created_at) + } + + Object.entries(rest).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + cleanParams[key] = value + } + }) + + return cleanParams + }, }, }, outputs: { @@ -1031,10 +1687,23 @@ export const IntercomV2Block: BlockConfig = { type: 'array', description: 'Array of conversations (for list/search operations)', }, + state: { type: 'string', description: 'Conversation state (for close/open/snooze operations)' }, ticket: { type: 'json', description: 'Ticket object with id, ticket_id, ticket_state' }, - ticketId: { type: 'string', description: 'ID of the ticket (for create operations)' }, + ticketId: { type: 'string', description: 'ID of the ticket (for create/update operations)' }, + ticket_state: { type: 'string', description: 'Ticket state (for update_ticket operation)' }, message: { type: 'json', description: 'Message object with id, type' }, messageId: { type: 'string', description: 'ID of the message (for create operations)' }, + admins: { type: 'array', description: 'Array of admin objects (for list_admins operation)' }, + tags: { type: 'array', description: 'Array of tag objects (for list_tags operation)' }, + tag: { type: 'json', description: 'Tag object with id and name (for tag operations)' }, + tagId: { type: 'string', description: 'ID of the tag (for create_tag operation)' }, + note: { type: 'json', description: 'Note object with id and body (for create_note operation)' }, + noteId: { type: 'string', description: 'ID of the note (for create_note operation)' }, + event_name: { + type: 'string', + description: 'Name of the tracked event (for create_event operation)', + }, + name: { type: 'string', description: 'Name of the resource (for various operations)' }, total_count: { type: 'number', description: 'Total count (for list/search operations)' }, pages: { type: 'json', description: 'Pagination info with page, per_page, total_pages' }, id: { type: 'string', description: 'ID of the deleted item (for delete operations)' }, diff --git a/apps/sim/tools/intercom/assign_conversation.ts b/apps/sim/tools/intercom/assign_conversation.ts new file mode 100644 index 000000000..1780c52c9 --- /dev/null +++ b/apps/sim/tools/intercom/assign_conversation.ts @@ -0,0 +1,146 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomAssignConversationParams { + accessToken: string + conversationId: string + admin_id: string + assignee_id: string + body?: string +} + +export interface IntercomAssignConversationV2Response { + success: boolean + output: { + conversation: any + conversationId: string + admin_assignee_id: number | null + team_assignee_id: string | null + } +} + +const assignConversationBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the conversation to assign', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the admin performing the assignment', + }, + assignee_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'The ID of the admin or team to assign the conversation to. Set to "0" to unassign.', + }, + body: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional message to add when assigning (e.g., "Passing to the support team")', + }, + }, + + request: { + url: (params: IntercomAssignConversationParams) => + buildIntercomUrl(`/conversations/${params.conversationId}/parts`), + method: 'POST', + headers: (params: IntercomAssignConversationParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomAssignConversationParams) => { + const payload: any = { + message_type: 'assignment', + type: 'admin', + admin_id: params.admin_id, + assignee_id: params.assignee_id, + } + + if (params.body) { + payload.body = params.body + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomAssignConversationV2Tool: ToolConfig< + IntercomAssignConversationParams, + IntercomAssignConversationV2Response +> = { + ...assignConversationBase, + id: 'intercom_assign_conversation_v2', + name: 'Assign Conversation in Intercom', + description: 'Assign a conversation to an admin or team in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'assign_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: data, + conversationId: data.id, + admin_assignee_id: data.admin_assignee_id ?? null, + team_assignee_id: data.team_assignee_id ?? null, + }, + } + }, + + outputs: { + conversation: { + type: 'object', + description: 'The assigned conversation object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + state: { type: 'string', description: 'State of the conversation' }, + open: { type: 'boolean', description: 'Whether the conversation is open' }, + admin_assignee_id: { + type: 'number', + description: 'ID of the assigned admin', + optional: true, + }, + team_assignee_id: { + type: 'string', + description: 'ID of the assigned team', + optional: true, + }, + created_at: { type: 'number', description: 'Unix timestamp when conversation was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + }, + }, + conversationId: { type: 'string', description: 'ID of the assigned conversation' }, + admin_assignee_id: { + type: 'number', + description: 'ID of the assigned admin', + optional: true, + }, + team_assignee_id: { type: 'string', description: 'ID of the assigned team', optional: true }, + }, +} diff --git a/apps/sim/tools/intercom/attach_contact_to_company.ts b/apps/sim/tools/intercom/attach_contact_to_company.ts new file mode 100644 index 000000000..9f6cf834b --- /dev/null +++ b/apps/sim/tools/intercom/attach_contact_to_company.ts @@ -0,0 +1,115 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomAttachContactToCompanyParams { + accessToken: string + contactId: string + companyId: string +} + +export interface IntercomAttachContactToCompanyV2Response { + success: boolean + output: { + company: any + companyId: string + name: string | null + } +} + +const attachContactToCompanyBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the contact to attach to the company', + }, + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the company to attach the contact to', + }, + }, + + request: { + url: (params: IntercomAttachContactToCompanyParams) => + buildIntercomUrl(`/contacts/${params.contactId}/companies`), + method: 'POST', + headers: (params: IntercomAttachContactToCompanyParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomAttachContactToCompanyParams) => ({ + id: params.companyId, + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomAttachContactToCompanyV2Tool: ToolConfig< + IntercomAttachContactToCompanyParams, + IntercomAttachContactToCompanyV2Response +> = { + ...attachContactToCompanyBase, + id: 'intercom_attach_contact_to_company_v2', + name: 'Attach Contact to Company in Intercom', + description: 'Attach a contact to a company in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'attach_contact_to_company') + } + + const data = await response.json() + + return { + success: true, + output: { + company: { + id: data.id, + type: data.type ?? 'company', + company_id: data.company_id ?? null, + name: data.name ?? null, + created_at: data.created_at ?? null, + updated_at: data.updated_at ?? null, + user_count: data.user_count ?? null, + session_count: data.session_count ?? null, + monthly_spend: data.monthly_spend ?? null, + plan: data.plan ?? null, + }, + companyId: data.id, + name: data.name ?? null, + }, + } + }, + + outputs: { + company: { + type: 'object', + description: 'The company object the contact was attached to', + properties: { + id: { type: 'string', description: 'Unique identifier for the company' }, + type: { type: 'string', description: 'Object type (company)' }, + company_id: { type: 'string', description: 'The company_id you defined' }, + name: { type: 'string', description: 'Name of the company' }, + created_at: { type: 'number', description: 'Unix timestamp when company was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when company was updated' }, + user_count: { type: 'number', description: 'Number of users in the company' }, + session_count: { type: 'number', description: 'Number of sessions' }, + monthly_spend: { type: 'number', description: 'Monthly spend amount' }, + plan: { type: 'object', description: 'Company plan details' }, + }, + }, + companyId: { type: 'string', description: 'ID of the company' }, + name: { type: 'string', description: 'Name of the company', optional: true }, + }, +} diff --git a/apps/sim/tools/intercom/close_conversation.ts b/apps/sim/tools/intercom/close_conversation.ts new file mode 100644 index 000000000..424b5aa63 --- /dev/null +++ b/apps/sim/tools/intercom/close_conversation.ts @@ -0,0 +1,129 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomCloseConversationParams { + accessToken: string + conversationId: string + admin_id: string + body?: string +} + +export interface IntercomCloseConversationV2Response { + success: boolean + output: { + conversation: any + conversationId: string + state: string + } +} + +const closeConversationBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the conversation to close', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the admin performing the action', + }, + body: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional closing message to add to the conversation', + }, + }, + + request: { + url: (params: IntercomCloseConversationParams) => + buildIntercomUrl(`/conversations/${params.conversationId}/parts`), + method: 'POST', + headers: (params: IntercomCloseConversationParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomCloseConversationParams) => { + const payload: any = { + message_type: 'close', + type: 'admin', + admin_id: params.admin_id, + } + + if (params.body) { + payload.body = params.body + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomCloseConversationV2Tool: ToolConfig< + IntercomCloseConversationParams, + IntercomCloseConversationV2Response +> = { + ...closeConversationBase, + id: 'intercom_close_conversation_v2', + name: 'Close Conversation in Intercom', + description: 'Close a conversation in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'close_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: { + id: data.id, + type: data.type ?? 'conversation', + state: data.state ?? 'closed', + open: data.open ?? false, + read: data.read ?? false, + created_at: data.created_at ?? null, + updated_at: data.updated_at ?? null, + }, + conversationId: data.id, + state: data.state ?? 'closed', + }, + } + }, + + outputs: { + conversation: { + type: 'object', + description: 'The closed conversation object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + state: { type: 'string', description: 'State of the conversation (closed)' }, + open: { type: 'boolean', description: 'Whether the conversation is open (false)' }, + read: { type: 'boolean', description: 'Whether the conversation has been read' }, + created_at: { type: 'number', description: 'Unix timestamp when conversation was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + }, + }, + conversationId: { type: 'string', description: 'ID of the closed conversation' }, + state: { type: 'string', description: 'State of the conversation (closed)' }, + }, +} diff --git a/apps/sim/tools/intercom/create_company.ts b/apps/sim/tools/intercom/create_company.ts index 5795afce3..f8848868a 100644 --- a/apps/sim/tools/intercom/create_company.ts +++ b/apps/sim/tools/intercom/create_company.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomCreateCompany') @@ -60,7 +60,7 @@ const createCompanyBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, company_id: { diff --git a/apps/sim/tools/intercom/create_contact.ts b/apps/sim/tools/intercom/create_contact.ts index 6e9a15a99..9d8aded58 100644 --- a/apps/sim/tools/intercom/create_contact.ts +++ b/apps/sim/tools/intercom/create_contact.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomCreateContact') @@ -88,7 +88,7 @@ const intercomCreateContactBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, role: { diff --git a/apps/sim/tools/intercom/create_event.ts b/apps/sim/tools/intercom/create_event.ts new file mode 100644 index 000000000..016ade9d2 --- /dev/null +++ b/apps/sim/tools/intercom/create_event.ts @@ -0,0 +1,148 @@ +import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('IntercomCreateEvent') + +export interface IntercomCreateEventParams { + accessToken: string + event_name: string + created_at?: number + user_id?: string + email?: string + id?: string + metadata?: string +} + +export interface IntercomCreateEventV2Response { + success: boolean + output: { + accepted: boolean + } +} + +const createEventBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + event_name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'The name of the event (e.g., "order-completed"). Use past-tense verb-noun format for readability.', + }, + created_at: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: + 'Unix timestamp for when the event occurred. Strongly recommended for uniqueness.', + }, + user_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Your identifier for the user (external_id)', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Email address of the user. Use only if your app uses email to uniquely identify users.', + }, + id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The Intercom contact ID', + }, + metadata: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON object with up to 10 metadata key-value pairs about the event (e.g., {"order_value": 99.99})', + }, + }, + + request: { + url: () => buildIntercomUrl('/events'), + method: 'POST', + headers: (params: IntercomCreateEventParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomCreateEventParams) => { + const payload: any = { + event_name: params.event_name, + } + + if (params.created_at) { + payload.created_at = params.created_at + } else { + payload.created_at = Math.floor(Date.now() / 1000) + } + + if (params.user_id) { + payload.user_id = params.user_id + } + + if (params.email) { + payload.email = params.email + } + + if (params.id) { + payload.id = params.id + } + + if (params.metadata) { + try { + payload.metadata = JSON.parse(params.metadata) + } catch (error) { + logger.warn('Failed to parse metadata, ignoring', { error }) + } + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomCreateEventV2Tool: ToolConfig< + IntercomCreateEventParams, + IntercomCreateEventV2Response +> = { + ...createEventBase, + id: 'intercom_create_event_v2', + name: 'Create Event in Intercom', + description: 'Track a custom event for a contact in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_event') + } + + return { + success: true, + output: { + accepted: true, + }, + } + }, + + outputs: { + accepted: { + type: 'boolean', + description: 'Whether the event was accepted (202 Accepted)', + }, + }, +} diff --git a/apps/sim/tools/intercom/create_message.ts b/apps/sim/tools/intercom/create_message.ts index 2cea1ce07..0b6ef2fc5 100644 --- a/apps/sim/tools/intercom/create_message.ts +++ b/apps/sim/tools/intercom/create_message.ts @@ -1,5 +1,5 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' export interface IntercomCreateMessageParams { accessToken: string @@ -40,7 +40,7 @@ const createMessageBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, message_type: { diff --git a/apps/sim/tools/intercom/create_note.ts b/apps/sim/tools/intercom/create_note.ts new file mode 100644 index 000000000..56fa3df26 --- /dev/null +++ b/apps/sim/tools/intercom/create_note.ts @@ -0,0 +1,131 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomCreateNoteParams { + accessToken: string + contactId: string + body: string + admin_id?: string +} + +export interface IntercomCreateNoteV2Response { + success: boolean + output: { + id: string + body: string + created_at: number + type: string + author: any | null + contact: any | null + } +} + +const createNoteBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the contact to add the note to', + }, + body: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The text content of the note', + }, + admin_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The ID of the admin creating the note', + }, + }, + + request: { + url: (params: IntercomCreateNoteParams) => + buildIntercomUrl(`/contacts/${params.contactId}/notes`), + method: 'POST', + headers: (params: IntercomCreateNoteParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomCreateNoteParams) => { + const payload: any = { + body: params.body, + } + + if (params.admin_id) { + payload.admin_id = params.admin_id + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomCreateNoteV2Tool: ToolConfig< + IntercomCreateNoteParams, + IntercomCreateNoteV2Response +> = { + ...createNoteBase, + id: 'intercom_create_note_v2', + name: 'Create Note in Intercom', + description: 'Add a note to a specific contact', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_note') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + body: data.body, + created_at: data.created_at, + type: data.type ?? 'note', + author: data.author ?? null, + contact: data.contact ?? null, + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Unique identifier for the note' }, + body: { type: 'string', description: 'The text content of the note' }, + created_at: { type: 'number', description: 'Unix timestamp when the note was created' }, + type: { type: 'string', description: 'Object type (note)' }, + author: { + type: 'object', + description: 'The admin who created the note', + optional: true, + properties: { + type: { type: 'string', description: 'Author type (admin)' }, + id: { type: 'string', description: 'Author ID' }, + name: { type: 'string', description: 'Author name' }, + email: { type: 'string', description: 'Author email' }, + }, + }, + contact: { + type: 'object', + description: 'The contact the note was created for', + optional: true, + properties: { + type: { type: 'string', description: 'Contact type' }, + id: { type: 'string', description: 'Contact ID' }, + }, + }, + }, +} diff --git a/apps/sim/tools/intercom/create_tag.ts b/apps/sim/tools/intercom/create_tag.ts new file mode 100644 index 000000000..85561f180 --- /dev/null +++ b/apps/sim/tools/intercom/create_tag.ts @@ -0,0 +1,97 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomCreateTagParams { + accessToken: string + name: string + id?: string +} + +export interface IntercomCreateTagV2Response { + success: boolean + output: { + id: string + name: string + type: string + } +} + +const createTagBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'The name of the tag. Will create a new tag if not found, or update the name if id is provided.', + }, + id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The ID of an existing tag to update. Omit to create a new tag.', + }, + }, + + request: { + url: () => buildIntercomUrl('/tags'), + method: 'POST', + headers: (params: IntercomCreateTagParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomCreateTagParams) => { + const payload: any = { + name: params.name, + } + + if (params.id) { + payload.id = params.id + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomCreateTagV2Tool: ToolConfig< + IntercomCreateTagParams, + IntercomCreateTagV2Response +> = { + ...createTagBase, + id: 'intercom_create_tag_v2', + name: 'Create Tag in Intercom', + description: 'Create a new tag or update an existing tag name', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'create_tag') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + name: data.name, + type: data.type ?? 'tag', + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Unique identifier for the tag' }, + name: { type: 'string', description: 'Name of the tag' }, + type: { type: 'string', description: 'Object type (tag)' }, + }, +} diff --git a/apps/sim/tools/intercom/create_ticket.ts b/apps/sim/tools/intercom/create_ticket.ts index 34fae4838..cdcf83375 100644 --- a/apps/sim/tools/intercom/create_ticket.ts +++ b/apps/sim/tools/intercom/create_ticket.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomCreateTicket') @@ -41,7 +41,7 @@ const createTicketBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, ticket_type_id: { diff --git a/apps/sim/tools/intercom/delete_contact.ts b/apps/sim/tools/intercom/delete_contact.ts index 5b5bd9432..87e01e3cf 100644 --- a/apps/sim/tools/intercom/delete_contact.ts +++ b/apps/sim/tools/intercom/delete_contact.ts @@ -1,5 +1,5 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' export interface IntercomDeleteContactParams { accessToken: string @@ -23,7 +23,7 @@ const intercomDeleteContactBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, contactId: { diff --git a/apps/sim/tools/intercom/detach_contact_from_company.ts b/apps/sim/tools/intercom/detach_contact_from_company.ts new file mode 100644 index 000000000..df18e4998 --- /dev/null +++ b/apps/sim/tools/intercom/detach_contact_from_company.ts @@ -0,0 +1,101 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomDetachContactFromCompanyParams { + accessToken: string + contactId: string + companyId: string +} + +export interface IntercomDetachContactFromCompanyV2Response { + success: boolean + output: { + company: any + companyId: string + name: string | null + } +} + +const detachContactFromCompanyBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the contact to detach from the company', + }, + companyId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the company to detach the contact from', + }, + }, + + request: { + url: (params: IntercomDetachContactFromCompanyParams) => + buildIntercomUrl(`/contacts/${params.contactId}/companies/${params.companyId}`), + method: 'DELETE', + headers: (params: IntercomDetachContactFromCompanyParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomDetachContactFromCompanyV2Tool: ToolConfig< + IntercomDetachContactFromCompanyParams, + IntercomDetachContactFromCompanyV2Response +> = { + ...detachContactFromCompanyBase, + id: 'intercom_detach_contact_from_company_v2', + name: 'Detach Contact from Company in Intercom', + description: 'Remove a contact from a company in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'detach_contact_from_company') + } + + const data = await response.json() + + return { + success: true, + output: { + company: { + id: data.id, + type: data.type ?? 'company', + company_id: data.company_id ?? null, + name: data.name ?? null, + }, + companyId: data.id, + name: data.name ?? null, + }, + } + }, + + outputs: { + company: { + type: 'object', + description: 'The company object the contact was detached from', + properties: { + id: { type: 'string', description: 'Unique identifier for the company' }, + type: { type: 'string', description: 'Object type (company)' }, + company_id: { type: 'string', description: 'The company_id you defined' }, + name: { type: 'string', description: 'Name of the company' }, + }, + }, + companyId: { type: 'string', description: 'ID of the company' }, + name: { type: 'string', description: 'Name of the company', optional: true }, + }, +} diff --git a/apps/sim/tools/intercom/get_company.ts b/apps/sim/tools/intercom/get_company.ts index ae311a074..1d2474d02 100644 --- a/apps/sim/tools/intercom/get_company.ts +++ b/apps/sim/tools/intercom/get_company.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomGetCompany') @@ -25,7 +25,7 @@ const getCompanyBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, companyId: { diff --git a/apps/sim/tools/intercom/get_contact.ts b/apps/sim/tools/intercom/get_contact.ts index bcba2d892..1090116ed 100644 --- a/apps/sim/tools/intercom/get_contact.ts +++ b/apps/sim/tools/intercom/get_contact.ts @@ -1,5 +1,5 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' export interface IntercomGetContactParams { accessToken: string @@ -22,7 +22,7 @@ const intercomGetContactBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, contactId: { diff --git a/apps/sim/tools/intercom/get_conversation.ts b/apps/sim/tools/intercom/get_conversation.ts index b5f708fd9..21413b226 100644 --- a/apps/sim/tools/intercom/get_conversation.ts +++ b/apps/sim/tools/intercom/get_conversation.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomGetConversation') @@ -27,7 +27,7 @@ const getConversationBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, conversationId: { diff --git a/apps/sim/tools/intercom/get_ticket.ts b/apps/sim/tools/intercom/get_ticket.ts index da111a6bf..1febb2b7e 100644 --- a/apps/sim/tools/intercom/get_ticket.ts +++ b/apps/sim/tools/intercom/get_ticket.ts @@ -1,5 +1,5 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' export interface IntercomGetTicketParams { accessToken: string @@ -31,7 +31,7 @@ const getTicketBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, ticketId: { diff --git a/apps/sim/tools/intercom/index.ts b/apps/sim/tools/intercom/index.ts index 626c93cff..143bca0c6 100644 --- a/apps/sim/tools/intercom/index.ts +++ b/apps/sim/tools/intercom/index.ts @@ -1,24 +1,28 @@ -// Contact tools - -// Company tools +export { intercomAssignConversationV2Tool } from './assign_conversation' +export { intercomAttachContactToCompanyV2Tool } from './attach_contact_to_company' +export { intercomCloseConversationV2Tool } from './close_conversation' export { intercomCreateCompanyTool, intercomCreateCompanyV2Tool } from './create_company' export { intercomCreateContactTool, intercomCreateContactV2Tool } from './create_contact' -// Message tools +export { intercomCreateEventV2Tool } from './create_event' export { intercomCreateMessageTool, intercomCreateMessageV2Tool } from './create_message' -// Ticket tools +export { intercomCreateNoteV2Tool } from './create_note' +export { intercomCreateTagV2Tool } from './create_tag' export { intercomCreateTicketTool, intercomCreateTicketV2Tool } from './create_ticket' export { intercomDeleteContactTool, intercomDeleteContactV2Tool } from './delete_contact' +export { intercomDetachContactFromCompanyV2Tool } from './detach_contact_from_company' export { intercomGetCompanyTool, intercomGetCompanyV2Tool } from './get_company' export { intercomGetContactTool, intercomGetContactV2Tool } from './get_contact' -// Conversation tools export { intercomGetConversationTool, intercomGetConversationV2Tool } from './get_conversation' export { intercomGetTicketTool, intercomGetTicketV2Tool } from './get_ticket' +export { intercomListAdminsV2Tool } from './list_admins' export { intercomListCompaniesTool, intercomListCompaniesV2Tool } from './list_companies' export { intercomListContactsTool, intercomListContactsV2Tool } from './list_contacts' export { intercomListConversationsTool, intercomListConversationsV2Tool, } from './list_conversations' +export { intercomListTagsV2Tool } from './list_tags' +export { intercomOpenConversationV2Tool } from './open_conversation' export { intercomReplyConversationTool, intercomReplyConversationV2Tool, @@ -28,4 +32,9 @@ export { intercomSearchConversationsTool, intercomSearchConversationsV2Tool, } from './search_conversations' +export { intercomSnoozeConversationV2Tool } from './snooze_conversation' +export { intercomTagContactV2Tool } from './tag_contact' +export { intercomTagConversationV2Tool } from './tag_conversation' +export { intercomUntagContactV2Tool } from './untag_contact' export { intercomUpdateContactTool, intercomUpdateContactV2Tool } from './update_contact' +export { intercomUpdateTicketV2Tool } from './update_ticket' diff --git a/apps/sim/tools/intercom/list_admins.ts b/apps/sim/tools/intercom/list_admins.ts new file mode 100644 index 000000000..d6ce5c81e --- /dev/null +++ b/apps/sim/tools/intercom/list_admins.ts @@ -0,0 +1,125 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomListAdminsParams { + accessToken: string +} + +interface IntercomAdmin { + type: string + id: string + name: string + email: string + job_title: string | null + away_mode_enabled: boolean + away_mode_reassign: boolean + has_inbox_seat: boolean + team_ids: number[] + avatar: { + type: string + image_url: string | null + } | null + email_verified: boolean | null +} + +export interface IntercomListAdminsV2Response { + success: boolean + output: { + admins: IntercomAdmin[] + type: string + } +} + +const listAdminsBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + }, + + request: { + url: () => buildIntercomUrl('/admins'), + method: 'GET', + headers: (params: IntercomListAdminsParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomListAdminsV2Tool: ToolConfig< + IntercomListAdminsParams, + IntercomListAdminsV2Response +> = { + ...listAdminsBase, + id: 'intercom_list_admins_v2', + name: 'List Admins from Intercom', + description: 'Fetch a list of all admins for the workspace', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'list_admins') + } + + const data = await response.json() + + return { + success: true, + output: { + admins: data.admins ?? [], + type: data.type ?? 'admin.list', + }, + } + }, + + outputs: { + admins: { + type: 'array', + description: 'Array of admin objects', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique identifier for the admin' }, + type: { type: 'string', description: 'Object type (admin)' }, + name: { type: 'string', description: 'Name of the admin' }, + email: { type: 'string', description: 'Email of the admin' }, + job_title: { type: 'string', description: 'Job title of the admin', optional: true }, + away_mode_enabled: { + type: 'boolean', + description: 'Whether admin is in away mode', + }, + away_mode_reassign: { + type: 'boolean', + description: 'Whether to reassign conversations when away', + }, + has_inbox_seat: { + type: 'boolean', + description: 'Whether admin has a paid inbox seat', + }, + team_ids: { + type: 'array', + description: 'List of team IDs the admin belongs to', + }, + avatar: { + type: 'object', + description: 'Avatar information', + optional: true, + }, + email_verified: { + type: 'boolean', + description: 'Whether email is verified', + optional: true, + }, + }, + }, + }, + type: { type: 'string', description: 'Object type (admin.list)' }, + }, +} diff --git a/apps/sim/tools/intercom/list_companies.ts b/apps/sim/tools/intercom/list_companies.ts index d06030405..10f95ac10 100644 --- a/apps/sim/tools/intercom/list_companies.ts +++ b/apps/sim/tools/intercom/list_companies.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomListCompanies') @@ -29,7 +29,7 @@ const listCompaniesBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, per_page: { diff --git a/apps/sim/tools/intercom/list_contacts.ts b/apps/sim/tools/intercom/list_contacts.ts index 44d7b5c06..b3f483788 100644 --- a/apps/sim/tools/intercom/list_contacts.ts +++ b/apps/sim/tools/intercom/list_contacts.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomListContacts') @@ -28,7 +28,7 @@ const listContactsBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, per_page: { diff --git a/apps/sim/tools/intercom/list_conversations.ts b/apps/sim/tools/intercom/list_conversations.ts index 50542515d..92b52060c 100644 --- a/apps/sim/tools/intercom/list_conversations.ts +++ b/apps/sim/tools/intercom/list_conversations.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomListConversations') @@ -30,7 +30,7 @@ const listConversationsBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, per_page: { diff --git a/apps/sim/tools/intercom/list_tags.ts b/apps/sim/tools/intercom/list_tags.ts new file mode 100644 index 000000000..c59fc2899 --- /dev/null +++ b/apps/sim/tools/intercom/list_tags.ts @@ -0,0 +1,86 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomListTagsParams { + accessToken: string +} + +interface IntercomTag { + type: string + id: string + name: string +} + +export interface IntercomListTagsV2Response { + success: boolean + output: { + tags: IntercomTag[] + type: string + } +} + +const listTagsBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + }, + + request: { + url: () => buildIntercomUrl('/tags'), + method: 'GET', + headers: (params: IntercomListTagsParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomListTagsV2Tool: ToolConfig< + IntercomListTagsParams, + IntercomListTagsV2Response +> = { + ...listTagsBase, + id: 'intercom_list_tags_v2', + name: 'List Tags from Intercom', + description: 'Fetch a list of all tags in the workspace', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'list_tags') + } + + const data = await response.json() + + return { + success: true, + output: { + tags: data.tags ?? [], + type: data.type ?? 'tag.list', + }, + } + }, + + outputs: { + tags: { + type: 'array', + description: 'Array of tag objects', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique identifier for the tag' }, + type: { type: 'string', description: 'Object type (tag)' }, + name: { type: 'string', description: 'Name of the tag' }, + }, + }, + }, + type: { type: 'string', description: 'Object type (list)' }, + }, +} diff --git a/apps/sim/tools/intercom/open_conversation.ts b/apps/sim/tools/intercom/open_conversation.ts new file mode 100644 index 000000000..243d889b3 --- /dev/null +++ b/apps/sim/tools/intercom/open_conversation.ts @@ -0,0 +1,114 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomOpenConversationParams { + accessToken: string + conversationId: string + admin_id: string +} + +export interface IntercomOpenConversationV2Response { + success: boolean + output: { + conversation: any + conversationId: string + state: string + } +} + +const openConversationBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the conversation to open', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the admin performing the action', + }, + }, + + request: { + url: (params: IntercomOpenConversationParams) => + buildIntercomUrl(`/conversations/${params.conversationId}/parts`), + method: 'POST', + headers: (params: IntercomOpenConversationParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomOpenConversationParams) => ({ + message_type: 'open', + type: 'admin', + admin_id: params.admin_id, + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomOpenConversationV2Tool: ToolConfig< + IntercomOpenConversationParams, + IntercomOpenConversationV2Response +> = { + ...openConversationBase, + id: 'intercom_open_conversation_v2', + name: 'Open Conversation in Intercom', + description: 'Open a closed or snoozed conversation in Intercom', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'open_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: { + id: data.id, + type: data.type ?? 'conversation', + state: data.state ?? 'open', + open: data.open ?? true, + read: data.read ?? false, + created_at: data.created_at ?? null, + updated_at: data.updated_at ?? null, + }, + conversationId: data.id, + state: data.state ?? 'open', + }, + } + }, + + outputs: { + conversation: { + type: 'object', + description: 'The opened conversation object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + state: { type: 'string', description: 'State of the conversation (open)' }, + open: { type: 'boolean', description: 'Whether the conversation is open (true)' }, + read: { type: 'boolean', description: 'Whether the conversation has been read' }, + created_at: { type: 'number', description: 'Unix timestamp when conversation was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + }, + }, + conversationId: { type: 'string', description: 'ID of the opened conversation' }, + state: { type: 'string', description: 'State of the conversation (open)' }, + }, +} diff --git a/apps/sim/tools/intercom/reply_conversation.ts b/apps/sim/tools/intercom/reply_conversation.ts index a6f8cb18f..bf6304af8 100644 --- a/apps/sim/tools/intercom/reply_conversation.ts +++ b/apps/sim/tools/intercom/reply_conversation.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomReplyConversation') @@ -31,7 +31,7 @@ const replyConversationBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, conversationId: { diff --git a/apps/sim/tools/intercom/search_contacts.ts b/apps/sim/tools/intercom/search_contacts.ts index 5c4d7788c..32d314523 100644 --- a/apps/sim/tools/intercom/search_contacts.ts +++ b/apps/sim/tools/intercom/search_contacts.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomSearchContacts') @@ -87,7 +87,7 @@ const searchContactsBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, query: { diff --git a/apps/sim/tools/intercom/search_conversations.ts b/apps/sim/tools/intercom/search_conversations.ts index 8ab9c8f71..4b2c36752 100644 --- a/apps/sim/tools/intercom/search_conversations.ts +++ b/apps/sim/tools/intercom/search_conversations.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomSearchConversations') @@ -41,7 +41,7 @@ const searchConversationsBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, query: { diff --git a/apps/sim/tools/intercom/snooze_conversation.ts b/apps/sim/tools/intercom/snooze_conversation.ts new file mode 100644 index 000000000..717e821e8 --- /dev/null +++ b/apps/sim/tools/intercom/snooze_conversation.ts @@ -0,0 +1,124 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomSnoozeConversationParams { + accessToken: string + conversationId: string + admin_id: string + snoozed_until: number +} + +export interface IntercomSnoozeConversationV2Response { + success: boolean + output: { + conversation: any + conversationId: string + state: string + snoozed_until: number | null + } +} + +const snoozeConversationBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the conversation to snooze', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the admin performing the action', + }, + snoozed_until: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'Unix timestamp for when the conversation should reopen', + }, + }, + + request: { + url: (params: IntercomSnoozeConversationParams) => + buildIntercomUrl(`/conversations/${params.conversationId}/reply`), + method: 'POST', + headers: (params: IntercomSnoozeConversationParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomSnoozeConversationParams) => ({ + message_type: 'snoozed', + admin_id: params.admin_id, + snoozed_until: params.snoozed_until, + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomSnoozeConversationV2Tool: ToolConfig< + IntercomSnoozeConversationParams, + IntercomSnoozeConversationV2Response +> = { + ...snoozeConversationBase, + id: 'intercom_snooze_conversation_v2', + name: 'Snooze Conversation in Intercom', + description: 'Snooze a conversation to reopen at a future time', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'snooze_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + conversation: data, + conversationId: data.id, + state: data.state ?? 'snoozed', + snoozed_until: data.snoozed_until ?? null, + }, + } + }, + + outputs: { + conversation: { + type: 'object', + description: 'The snoozed conversation object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + state: { type: 'string', description: 'State of the conversation (snoozed)' }, + open: { type: 'boolean', description: 'Whether the conversation is open' }, + snoozed_until: { + type: 'number', + description: 'Unix timestamp when conversation will reopen', + optional: true, + }, + created_at: { type: 'number', description: 'Unix timestamp when conversation was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + }, + }, + conversationId: { type: 'string', description: 'ID of the snoozed conversation' }, + state: { type: 'string', description: 'State of the conversation (snoozed)' }, + snoozed_until: { + type: 'number', + description: 'Unix timestamp when conversation will reopen', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/intercom/tag_contact.ts b/apps/sim/tools/intercom/tag_contact.ts new file mode 100644 index 000000000..c2a2628a2 --- /dev/null +++ b/apps/sim/tools/intercom/tag_contact.ts @@ -0,0 +1,89 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomTagContactParams { + accessToken: string + contactId: string + tagId: string +} + +export interface IntercomTagContactV2Response { + success: boolean + output: { + id: string + name: string + type: string + } +} + +const tagContactBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the contact to tag', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to apply', + }, + }, + + request: { + url: (params: IntercomTagContactParams) => + buildIntercomUrl(`/contacts/${params.contactId}/tags`), + method: 'POST', + headers: (params: IntercomTagContactParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomTagContactParams) => ({ + id: params.tagId, + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomTagContactV2Tool: ToolConfig< + IntercomTagContactParams, + IntercomTagContactV2Response +> = { + ...tagContactBase, + id: 'intercom_tag_contact_v2', + name: 'Tag Contact in Intercom', + description: 'Add a tag to a specific contact', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'tag_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + name: data.name, + type: data.type ?? 'tag', + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Unique identifier for the tag' }, + name: { type: 'string', description: 'Name of the tag' }, + type: { type: 'string', description: 'Object type (tag)' }, + }, +} diff --git a/apps/sim/tools/intercom/tag_conversation.ts b/apps/sim/tools/intercom/tag_conversation.ts new file mode 100644 index 000000000..18485f74f --- /dev/null +++ b/apps/sim/tools/intercom/tag_conversation.ts @@ -0,0 +1,97 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomTagConversationParams { + accessToken: string + conversationId: string + tagId: string + admin_id: string +} + +export interface IntercomTagConversationV2Response { + success: boolean + output: { + id: string + name: string + type: string + } +} + +const tagConversationBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + conversationId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the conversation to tag', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to apply', + }, + admin_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the admin applying the tag', + }, + }, + + request: { + url: (params: IntercomTagConversationParams) => + buildIntercomUrl(`/conversations/${params.conversationId}/tags`), + method: 'POST', + headers: (params: IntercomTagConversationParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomTagConversationParams) => ({ + id: params.tagId, + admin_id: params.admin_id, + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomTagConversationV2Tool: ToolConfig< + IntercomTagConversationParams, + IntercomTagConversationV2Response +> = { + ...tagConversationBase, + id: 'intercom_tag_conversation_v2', + name: 'Tag Conversation in Intercom', + description: 'Add a tag to a specific conversation', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'tag_conversation') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + name: data.name, + type: data.type ?? 'tag', + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Unique identifier for the tag' }, + name: { type: 'string', description: 'Name of the tag' }, + type: { type: 'string', description: 'Object type (tag)' }, + }, +} diff --git a/apps/sim/tools/intercom/types.ts b/apps/sim/tools/intercom/types.ts index d15940d3f..c2a17b787 100644 --- a/apps/sim/tools/intercom/types.ts +++ b/apps/sim/tools/intercom/types.ts @@ -2,14 +2,13 @@ import { createLogger } from '@sim/logger' const logger = createLogger('Intercom') -// Base params for Intercom API export interface IntercomBaseParams { - accessToken: string // OAuth token or API token (hidden) + accessToken: string } export interface IntercomPaginationParams { per_page?: number - starting_after?: string // Cursor for pagination + starting_after?: string } export interface IntercomPagingInfo { @@ -33,12 +32,10 @@ export interface IntercomResponse { } } -// 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 }) diff --git a/apps/sim/tools/intercom/untag_contact.ts b/apps/sim/tools/intercom/untag_contact.ts new file mode 100644 index 000000000..c5e8cc351 --- /dev/null +++ b/apps/sim/tools/intercom/untag_contact.ts @@ -0,0 +1,87 @@ +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +export interface IntercomUntagContactParams { + accessToken: string + contactId: string + tagId: string +} + +export interface IntercomUntagContactV2Response { + success: boolean + output: { + id: string + name: string + type: string + } +} + +const untagContactBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + contactId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the contact to untag', + }, + tagId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the tag to remove', + }, + }, + + request: { + url: (params: IntercomUntagContactParams) => + buildIntercomUrl(`/contacts/${params.contactId}/tags/${params.tagId}`), + method: 'DELETE', + headers: (params: IntercomUntagContactParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomUntagContactV2Tool: ToolConfig< + IntercomUntagContactParams, + IntercomUntagContactV2Response +> = { + ...untagContactBase, + id: 'intercom_untag_contact_v2', + name: 'Untag Contact in Intercom', + description: 'Remove a tag from a specific contact', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'untag_contact') + } + + const data = await response.json() + + return { + success: true, + output: { + id: data.id, + name: data.name, + type: data.type ?? 'tag', + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Unique identifier for the tag that was removed' }, + name: { type: 'string', description: 'Name of the tag that was removed' }, + type: { type: 'string', description: 'Object type (tag)' }, + }, +} diff --git a/apps/sim/tools/intercom/update_contact.ts b/apps/sim/tools/intercom/update_contact.ts index 7ac467d9f..9a5ce7e9e 100644 --- a/apps/sim/tools/intercom/update_contact.ts +++ b/apps/sim/tools/intercom/update_contact.ts @@ -1,6 +1,6 @@ import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' import type { ToolConfig } from '@/tools/types' -import { buildIntercomUrl, handleIntercomError } from './types' const logger = createLogger('IntercomUpdateContact') @@ -38,7 +38,7 @@ const intercomUpdateContactBase = { accessToken: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Intercom API access token', }, contactId: { diff --git a/apps/sim/tools/intercom/update_ticket.ts b/apps/sim/tools/intercom/update_ticket.ts new file mode 100644 index 000000000..6f8bc1f66 --- /dev/null +++ b/apps/sim/tools/intercom/update_ticket.ts @@ -0,0 +1,193 @@ +import { createLogger } from '@sim/logger' +import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('IntercomUpdateTicket') + +export interface IntercomUpdateTicketParams { + accessToken: string + ticketId: string + ticket_attributes?: string + open?: boolean + is_shared?: boolean + snoozed_until?: number + admin_id?: string + assignee_id?: string +} + +export interface IntercomUpdateTicketV2Response { + success: boolean + output: { + ticket: any + ticketId: string + ticket_state: string + } +} + +const updateTicketBase = { + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Intercom API access token', + }, + ticketId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the ticket to update', + }, + ticket_attributes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'JSON object with ticket attributes (e.g., {"_default_title_":"New Title","_default_description_":"Updated description"})', + }, + open: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Set to false to close the ticket, true to keep it open', + }, + is_shared: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the ticket is visible to users', + }, + snoozed_until: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Unix timestamp for when the ticket should reopen', + }, + admin_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'The ID of the admin performing the update (needed for workflows and attribution)', + }, + assignee_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The ID of the admin or team to assign the ticket to. Set to "0" to unassign.', + }, + }, + + request: { + url: (params: IntercomUpdateTicketParams) => buildIntercomUrl(`/tickets/${params.ticketId}`), + method: 'PUT', + headers: (params: IntercomUpdateTicketParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + 'Intercom-Version': '2.14', + }), + body: (params: IntercomUpdateTicketParams) => { + const payload: any = {} + + if (params.ticket_attributes) { + try { + payload.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') + } + } + + if (params.open !== undefined) { + payload.open = params.open + } + + if (params.is_shared !== undefined) { + payload.is_shared = params.is_shared + } + + if (params.snoozed_until !== undefined) { + payload.snoozed_until = params.snoozed_until + } + + if (params.admin_id) { + payload.admin_id = params.admin_id + } + + if (params.assignee_id) { + payload.assignee_id = params.assignee_id + } + + return payload + }, + }, +} satisfies Pick, 'params' | 'request'> + +export const intercomUpdateTicketV2Tool: ToolConfig< + IntercomUpdateTicketParams, + IntercomUpdateTicketV2Response +> = { + ...updateTicketBase, + id: 'intercom_update_ticket_v2', + name: 'Update Ticket in Intercom', + description: 'Update a ticket in Intercom (change state, assignment, attributes)', + version: '2.0.0', + + transformResponse: async (response: Response) => { + if (!response.ok) { + const data = await response.json() + handleIntercomError(data, response.status, 'update_ticket') + } + + const data = await response.json() + + return { + success: true, + output: { + ticket: { + id: data.id, + type: data.type ?? 'ticket', + ticket_id: data.ticket_id ?? null, + ticket_state: data.ticket_state ?? null, + ticket_attributes: data.ticket_attributes ?? null, + open: data.open ?? null, + is_shared: data.is_shared ?? null, + snoozed_until: data.snoozed_until ?? null, + admin_assignee_id: data.admin_assignee_id ?? null, + team_assignee_id: data.team_assignee_id ?? null, + created_at: data.created_at ?? null, + updated_at: data.updated_at ?? null, + }, + ticketId: data.id, + ticket_state: data.ticket_state ?? null, + }, + } + }, + + outputs: { + ticket: { + type: 'object', + description: 'The updated ticket object', + properties: { + id: { type: 'string', description: 'Unique identifier for the ticket' }, + type: { type: 'string', description: 'Object type (ticket)' }, + ticket_id: { type: 'string', description: 'Ticket ID shown in Intercom UI' }, + ticket_state: { type: 'string', description: 'State of the ticket' }, + ticket_attributes: { type: 'object', description: 'Attributes of the ticket' }, + open: { type: 'boolean', description: 'Whether the ticket is open' }, + is_shared: { type: 'boolean', description: 'Whether the ticket is visible to users' }, + snoozed_until: { + type: 'number', + description: 'Unix timestamp when ticket will reopen', + optional: true, + }, + admin_assignee_id: { type: 'string', description: 'ID of assigned admin', optional: true }, + team_assignee_id: { type: 'string', description: 'ID of assigned team', optional: true }, + created_at: { type: 'number', description: 'Unix timestamp when ticket was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when ticket was last updated' }, + }, + }, + ticketId: { type: 'string', description: 'ID of the updated ticket' }, + ticket_state: { type: 'string', description: 'Current state of the ticket' }, + }, +} diff --git a/apps/sim/tools/polymarket/get_activity.ts b/apps/sim/tools/polymarket/get_activity.ts index 33a4daa34..5f04d5976 100644 --- a/apps/sim/tools/polymarket/get_activity.ts +++ b/apps/sim/tools/polymarket/get_activity.ts @@ -1,6 +1,6 @@ +import type { PolymarketActivity } from '@/tools/polymarket/types' +import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketActivity } from './types' -import { buildDataUrl, handlePolymarketError } from './types' export interface PolymarketGetActivityParams { user: string diff --git a/apps/sim/tools/polymarket/get_event.ts b/apps/sim/tools/polymarket/get_event.ts index 037f22687..e957f6dd2 100644 --- a/apps/sim/tools/polymarket/get_event.ts +++ b/apps/sim/tools/polymarket/get_event.ts @@ -1,6 +1,6 @@ +import type { PolymarketEvent } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketEvent } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetEventParams { eventId?: string // Event ID diff --git a/apps/sim/tools/polymarket/get_events.ts b/apps/sim/tools/polymarket/get_events.ts index 8eb9e5d92..8b030f46f 100644 --- a/apps/sim/tools/polymarket/get_events.ts +++ b/apps/sim/tools/polymarket/get_events.ts @@ -1,6 +1,6 @@ +import type { PolymarketEvent, PolymarketPaginationParams } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketEvent, PolymarketPaginationParams } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetEventsParams extends PolymarketPaginationParams { closed?: string diff --git a/apps/sim/tools/polymarket/get_holders.ts b/apps/sim/tools/polymarket/get_holders.ts index 197ea2a22..8fe772a92 100644 --- a/apps/sim/tools/polymarket/get_holders.ts +++ b/apps/sim/tools/polymarket/get_holders.ts @@ -1,6 +1,6 @@ +import type { PolymarketMarketHolders } from '@/tools/polymarket/types' +import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketMarketHolders } from './types' -import { buildDataUrl, handlePolymarketError } from './types' export interface PolymarketGetHoldersParams { market: string diff --git a/apps/sim/tools/polymarket/get_last_trade_price.ts b/apps/sim/tools/polymarket/get_last_trade_price.ts index d429bec08..92f4e52b0 100644 --- a/apps/sim/tools/polymarket/get_last_trade_price.ts +++ b/apps/sim/tools/polymarket/get_last_trade_price.ts @@ -1,5 +1,5 @@ +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetLastTradePriceParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_leaderboard.ts b/apps/sim/tools/polymarket/get_leaderboard.ts index 039f3e83e..0fb0485e7 100644 --- a/apps/sim/tools/polymarket/get_leaderboard.ts +++ b/apps/sim/tools/polymarket/get_leaderboard.ts @@ -1,6 +1,6 @@ +import type { PolymarketLeaderboardEntry } from '@/tools/polymarket/types' +import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketLeaderboardEntry } from './types' -import { buildDataUrl, handlePolymarketError } from './types' export interface PolymarketGetLeaderboardParams { category?: string diff --git a/apps/sim/tools/polymarket/get_market.ts b/apps/sim/tools/polymarket/get_market.ts index 606bff4f6..493a1bbec 100644 --- a/apps/sim/tools/polymarket/get_market.ts +++ b/apps/sim/tools/polymarket/get_market.ts @@ -1,6 +1,6 @@ +import type { PolymarketMarket } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketMarket } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetMarketParams { marketId?: string // Market ID diff --git a/apps/sim/tools/polymarket/get_markets.ts b/apps/sim/tools/polymarket/get_markets.ts index 9a480e0a4..d4215bf50 100644 --- a/apps/sim/tools/polymarket/get_markets.ts +++ b/apps/sim/tools/polymarket/get_markets.ts @@ -1,6 +1,6 @@ +import type { PolymarketMarket, PolymarketPaginationParams } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketMarket, PolymarketPaginationParams } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetMarketsParams extends PolymarketPaginationParams { closed?: string diff --git a/apps/sim/tools/polymarket/get_midpoint.ts b/apps/sim/tools/polymarket/get_midpoint.ts index 7ea9abc1a..17723163e 100644 --- a/apps/sim/tools/polymarket/get_midpoint.ts +++ b/apps/sim/tools/polymarket/get_midpoint.ts @@ -1,5 +1,5 @@ +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetMidpointParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_orderbook.ts b/apps/sim/tools/polymarket/get_orderbook.ts index 6bd400fe6..0bd792443 100644 --- a/apps/sim/tools/polymarket/get_orderbook.ts +++ b/apps/sim/tools/polymarket/get_orderbook.ts @@ -1,6 +1,6 @@ +import type { PolymarketOrderBook } from '@/tools/polymarket/types' +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketOrderBook } from './types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetOrderbookParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_positions.ts b/apps/sim/tools/polymarket/get_positions.ts index 0968b59b2..cc2d66e7a 100644 --- a/apps/sim/tools/polymarket/get_positions.ts +++ b/apps/sim/tools/polymarket/get_positions.ts @@ -1,6 +1,6 @@ +import type { PolymarketPosition } from '@/tools/polymarket/types' +import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketPosition } from './types' -import { buildDataUrl, handlePolymarketError } from './types' export interface PolymarketGetPositionsParams { user: string diff --git a/apps/sim/tools/polymarket/get_price.ts b/apps/sim/tools/polymarket/get_price.ts index 8506c5101..4bae1f67f 100644 --- a/apps/sim/tools/polymarket/get_price.ts +++ b/apps/sim/tools/polymarket/get_price.ts @@ -1,5 +1,5 @@ +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetPriceParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_price_history.ts b/apps/sim/tools/polymarket/get_price_history.ts index 0c258cda4..deb4f362c 100644 --- a/apps/sim/tools/polymarket/get_price_history.ts +++ b/apps/sim/tools/polymarket/get_price_history.ts @@ -1,6 +1,6 @@ +import type { PolymarketPriceHistoryEntry } from '@/tools/polymarket/types' +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketPriceHistoryEntry } from './types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetPriceHistoryParams { tokenId: string diff --git a/apps/sim/tools/polymarket/get_series.ts b/apps/sim/tools/polymarket/get_series.ts index 1c61fe1d2..c209dfbb5 100644 --- a/apps/sim/tools/polymarket/get_series.ts +++ b/apps/sim/tools/polymarket/get_series.ts @@ -1,6 +1,6 @@ +import type { PolymarketPaginationParams, PolymarketSeries } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketPaginationParams, PolymarketSeries } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetSeriesParams extends PolymarketPaginationParams {} diff --git a/apps/sim/tools/polymarket/get_series_by_id.ts b/apps/sim/tools/polymarket/get_series_by_id.ts index f1a1101a3..790d7ab4e 100644 --- a/apps/sim/tools/polymarket/get_series_by_id.ts +++ b/apps/sim/tools/polymarket/get_series_by_id.ts @@ -1,6 +1,6 @@ +import type { PolymarketSeries } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketSeries } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetSeriesByIdParams { seriesId: string // Series ID (required) diff --git a/apps/sim/tools/polymarket/get_spread.ts b/apps/sim/tools/polymarket/get_spread.ts index 73a210352..0eff8c44c 100644 --- a/apps/sim/tools/polymarket/get_spread.ts +++ b/apps/sim/tools/polymarket/get_spread.ts @@ -1,6 +1,6 @@ +import type { PolymarketSpread } from '@/tools/polymarket/types' +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketSpread } from './types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetSpreadParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_tags.ts b/apps/sim/tools/polymarket/get_tags.ts index a2af8ddf9..8dba40c10 100644 --- a/apps/sim/tools/polymarket/get_tags.ts +++ b/apps/sim/tools/polymarket/get_tags.ts @@ -1,6 +1,6 @@ +import type { PolymarketPaginationParams, PolymarketTag } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketPaginationParams, PolymarketTag } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketGetTagsParams extends PolymarketPaginationParams {} diff --git a/apps/sim/tools/polymarket/get_tick_size.ts b/apps/sim/tools/polymarket/get_tick_size.ts index 956c44930..e869c5cac 100644 --- a/apps/sim/tools/polymarket/get_tick_size.ts +++ b/apps/sim/tools/polymarket/get_tick_size.ts @@ -1,5 +1,5 @@ +import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import { buildClobUrl, handlePolymarketError } from './types' export interface PolymarketGetTickSizeParams { tokenId: string // The token ID (CLOB token ID from market) diff --git a/apps/sim/tools/polymarket/get_trades.ts b/apps/sim/tools/polymarket/get_trades.ts index fe0f7893d..2bdeb9f15 100644 --- a/apps/sim/tools/polymarket/get_trades.ts +++ b/apps/sim/tools/polymarket/get_trades.ts @@ -1,6 +1,6 @@ +import type { PolymarketTrade } from '@/tools/polymarket/types' +import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketTrade } from './types' -import { buildDataUrl, handlePolymarketError } from './types' export interface PolymarketGetTradesParams { user?: string diff --git a/apps/sim/tools/polymarket/index.ts b/apps/sim/tools/polymarket/index.ts index 80727214a..bc85dc348 100644 --- a/apps/sim/tools/polymarket/index.ts +++ b/apps/sim/tools/polymarket/index.ts @@ -1,21 +1,20 @@ -export * from './get_activity' -export * from './get_event' -export * from './get_events' -export * from './get_holders' -export * from './get_last_trade_price' -export * from './get_leaderboard' -export * from './get_market' -export * from './get_markets' -export * from './get_midpoint' -export * from './get_orderbook' -export * from './get_positions' -export * from './get_price' -export * from './get_price_history' -export * from './get_series' -export * from './get_series_by_id' -export * from './get_spread' -export * from './get_tags' -export * from './get_tick_size' -export * from './get_trades' -export * from './search' -export * from './types' +export { polymarketGetActivityTool } from './get_activity' +export { polymarketGetEventTool } from './get_event' +export { polymarketGetEventsTool } from './get_events' +export { polymarketGetHoldersTool } from './get_holders' +export { polymarketGetLastTradePriceTool } from './get_last_trade_price' +export { polymarketGetLeaderboardTool } from './get_leaderboard' +export { polymarketGetMarketTool } from './get_market' +export { polymarketGetMarketsTool } from './get_markets' +export { polymarketGetMidpointTool } from './get_midpoint' +export { polymarketGetOrderbookTool } from './get_orderbook' +export { polymarketGetPositionsTool } from './get_positions' +export { polymarketGetPriceTool } from './get_price' +export { polymarketGetPriceHistoryTool } from './get_price_history' +export { polymarketGetSeriesTool } from './get_series' +export { polymarketGetSeriesByIdTool } from './get_series_by_id' +export { polymarketGetSpreadTool } from './get_spread' +export { polymarketGetTagsTool } from './get_tags' +export { polymarketGetTickSizeTool } from './get_tick_size' +export { polymarketGetTradesTool } from './get_trades' +export { polymarketSearchTool } from './search' diff --git a/apps/sim/tools/polymarket/search.ts b/apps/sim/tools/polymarket/search.ts index c3b9c48aa..342185824 100644 --- a/apps/sim/tools/polymarket/search.ts +++ b/apps/sim/tools/polymarket/search.ts @@ -1,6 +1,6 @@ +import type { PolymarketSearchResult } from '@/tools/polymarket/types' +import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types' import type { ToolConfig } from '@/tools/types' -import type { PolymarketSearchResult } from './types' -import { buildGammaUrl, handlePolymarketError } from './types' export interface PolymarketSearchParams { query: string diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 30665b721..ac477b5ed 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -663,16 +663,23 @@ import { incidentioWorkflowsUpdateTool, } from '@/tools/incidentio' import { + intercomAssignConversationV2Tool, + intercomAttachContactToCompanyV2Tool, + intercomCloseConversationV2Tool, intercomCreateCompanyTool, intercomCreateCompanyV2Tool, intercomCreateContactTool, intercomCreateContactV2Tool, + intercomCreateEventV2Tool, intercomCreateMessageTool, intercomCreateMessageV2Tool, + intercomCreateNoteV2Tool, + intercomCreateTagV2Tool, intercomCreateTicketTool, intercomCreateTicketV2Tool, intercomDeleteContactTool, intercomDeleteContactV2Tool, + intercomDetachContactFromCompanyV2Tool, intercomGetCompanyTool, intercomGetCompanyV2Tool, intercomGetContactTool, @@ -681,20 +688,28 @@ import { intercomGetConversationV2Tool, intercomGetTicketTool, intercomGetTicketV2Tool, + intercomListAdminsV2Tool, intercomListCompaniesTool, intercomListCompaniesV2Tool, intercomListContactsTool, intercomListContactsV2Tool, intercomListConversationsTool, intercomListConversationsV2Tool, + intercomListTagsV2Tool, + intercomOpenConversationV2Tool, intercomReplyConversationTool, intercomReplyConversationV2Tool, intercomSearchContactsTool, intercomSearchContactsV2Tool, intercomSearchConversationsTool, intercomSearchConversationsV2Tool, + intercomSnoozeConversationV2Tool, + intercomTagContactV2Tool, + intercomTagConversationV2Tool, + intercomUntagContactV2Tool, intercomUpdateContactTool, intercomUpdateContactV2Tool, + intercomUpdateTicketV2Tool, } from '@/tools/intercom' import { jinaReadUrlTool, jinaSearchTool } from '@/tools/jina' import { @@ -3126,8 +3141,23 @@ export const tools: Record = { intercom_create_ticket_v2: intercomCreateTicketV2Tool, intercom_get_ticket: intercomGetTicketTool, intercom_get_ticket_v2: intercomGetTicketV2Tool, + intercom_update_ticket_v2: intercomUpdateTicketV2Tool, intercom_create_message: intercomCreateMessageTool, intercom_create_message_v2: intercomCreateMessageV2Tool, + intercom_list_admins_v2: intercomListAdminsV2Tool, + intercom_close_conversation_v2: intercomCloseConversationV2Tool, + intercom_open_conversation_v2: intercomOpenConversationV2Tool, + intercom_snooze_conversation_v2: intercomSnoozeConversationV2Tool, + intercom_assign_conversation_v2: intercomAssignConversationV2Tool, + intercom_list_tags_v2: intercomListTagsV2Tool, + intercom_create_tag_v2: intercomCreateTagV2Tool, + intercom_tag_contact_v2: intercomTagContactV2Tool, + intercom_untag_contact_v2: intercomUntagContactV2Tool, + intercom_tag_conversation_v2: intercomTagConversationV2Tool, + intercom_create_note_v2: intercomCreateNoteV2Tool, + intercom_create_event_v2: intercomCreateEventV2Tool, + intercom_attach_contact_to_company_v2: intercomAttachContactToCompanyV2Tool, + intercom_detach_contact_from_company_v2: intercomDetachContactFromCompanyV2Tool, sentry_issues_list: listIssuesTool, sentry_issues_get: getIssueTool, sentry_issues_update: updateIssueTool,