From 31045ab191f8afdb3b645b64d643596308703c7a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 31 Mar 2026 16:21:10 -0700 Subject: [PATCH 1/2] improvement(attio): validate integration, fix event bug, add missing tool and triggers --- apps/docs/content/docs/en/tools/attio.mdx | 33 +++++++- .../integrations/data/integrations.json | 28 ++++++- apps/sim/blocks/blocks/attio.ts | 18 ++++- apps/sim/tools/attio/assert_record.ts | 2 +- apps/sim/tools/attio/create_list_entry.ts | 2 +- apps/sim/tools/attio/create_record.ts | 2 +- apps/sim/tools/attio/delete_comment.ts | 2 +- apps/sim/tools/attio/delete_list_entry.ts | 3 +- apps/sim/tools/attio/delete_note.ts | 2 +- apps/sim/tools/attio/delete_record.ts | 2 +- apps/sim/tools/attio/delete_task.ts | 2 +- apps/sim/tools/attio/delete_webhook.ts | 2 +- apps/sim/tools/attio/get_comment.ts | 2 +- apps/sim/tools/attio/get_list.ts | 2 +- apps/sim/tools/attio/get_list_entry.ts | 3 +- apps/sim/tools/attio/get_member.ts | 2 +- apps/sim/tools/attio/get_note.ts | 2 +- apps/sim/tools/attio/get_object.ts | 2 +- apps/sim/tools/attio/get_record.ts | 2 +- apps/sim/tools/attio/get_task.ts | 78 +++++++++++++++++++ apps/sim/tools/attio/get_thread.ts | 2 +- apps/sim/tools/attio/get_webhook.ts | 2 +- apps/sim/tools/attio/index.ts | 1 + apps/sim/tools/attio/list_records.ts | 2 +- apps/sim/tools/attio/query_list_entries.ts | 2 +- apps/sim/tools/attio/types.ts | 21 +++++ apps/sim/tools/attio/update_list.ts | 2 +- apps/sim/tools/attio/update_list_entry.ts | 3 +- apps/sim/tools/attio/update_object.ts | 2 +- apps/sim/tools/attio/update_record.ts | 2 +- apps/sim/tools/attio/update_task.ts | 2 +- apps/sim/tools/attio/update_webhook.ts | 17 ++-- apps/sim/tools/registry.ts | 2 + apps/sim/triggers/attio/index.ts | 4 + apps/sim/triggers/attio/list_created.ts | 29 +++++++ apps/sim/triggers/attio/list_deleted.ts | 29 +++++++ apps/sim/triggers/attio/list_updated.ts | 29 +++++++ apps/sim/triggers/attio/utils.ts | 75 +++++++++++++++++- .../attio/workspace_member_created.ts | 29 +++++++ apps/sim/triggers/registry.ts | 8 ++ 40 files changed, 410 insertions(+), 44 deletions(-) create mode 100644 apps/sim/tools/attio/get_task.ts create mode 100644 apps/sim/triggers/attio/list_created.ts create mode 100644 apps/sim/triggers/attio/list_deleted.ts create mode 100644 apps/sim/triggers/attio/list_updated.ts create mode 100644 apps/sim/triggers/attio/workspace_member_created.ts diff --git a/apps/docs/content/docs/en/tools/attio.mdx b/apps/docs/content/docs/en/tools/attio.mdx index 85ad63126b9..5a36922ee42 100644 --- a/apps/docs/content/docs/en/tools/attio.mdx +++ b/apps/docs/content/docs/en/tools/attio.mdx @@ -359,6 +359,35 @@ List tasks in Attio, optionally filtered by record, assignee, or completion stat | ↳ `createdAt` | string | When the task was created | | `count` | number | Number of tasks returned | +### `attio_get_task` + +Get a single task by ID from Attio + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskId` | string | Yes | The ID of the task to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `taskId` | string | The task ID | +| `content` | string | The task content | +| `deadlineAt` | string | The task deadline | +| `isCompleted` | boolean | Whether the task is completed | +| `linkedRecords` | array | Records linked to this task | +| ↳ `targetObjectId` | string | The linked object ID | +| ↳ `targetRecordId` | string | The linked record ID | +| `assignees` | array | Task assignees | +| ↳ `type` | string | The assignee actor type \(e.g. workspace-member\) | +| ↳ `id` | string | The assignee actor ID | +| `createdByActor` | object | The actor who created this task | +| ↳ `type` | string | The actor type \(e.g. workspace-member, api-token, system\) | +| ↳ `id` | string | The actor ID | +| `createdAt` | string | When the task was created | + ### `attio_create_task` Create a task in Attio @@ -1012,8 +1041,8 @@ Update a webhook in Attio (target URL and/or subscriptions) | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `webhookId` | string | Yes | The webhook ID to update | -| `targetUrl` | string | Yes | HTTPS target URL for webhook delivery | -| `subscriptions` | string | Yes | JSON array of subscriptions, e.g. \[\{"event_type":"note.created"\}\] | +| `targetUrl` | string | No | HTTPS target URL for webhook delivery | +| `subscriptions` | string | No | JSON array of subscriptions, e.g. \[\{"event_type":"note.created"\}\] | #### Output diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 6c6120a545c..9062a0b789a 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -926,6 +926,10 @@ "name": "List Tasks", "description": "List tasks in Attio, optionally filtered by record, assignee, or completion status" }, + { + "name": "Get Task", + "description": "Get a single task by ID from Attio" + }, { "name": "Create Task", "description": "Create a task in Attio" @@ -1039,7 +1043,7 @@ "description": "Delete a webhook from Attio" } ], - "operationCount": 40, + "operationCount": 41, "triggers": [ { "id": "attio_record_created", @@ -1126,13 +1130,33 @@ "name": "Attio List Entry Deleted", "description": "Trigger workflow when a list entry is deleted in Attio" }, + { + "id": "attio_list_created", + "name": "Attio List Created", + "description": "Trigger workflow when a list is created in Attio" + }, + { + "id": "attio_list_updated", + "name": "Attio List Updated", + "description": "Trigger workflow when a list is updated in Attio" + }, + { + "id": "attio_list_deleted", + "name": "Attio List Deleted", + "description": "Trigger workflow when a list is deleted in Attio" + }, + { + "id": "attio_workspace_member_created", + "name": "Attio Workspace Member Created", + "description": "Trigger workflow when a new member is added to the Attio workspace" + }, { "id": "attio_webhook", "name": "Attio Webhook (All Events)", "description": "Trigger workflow on any Attio webhook event" } ], - "triggerCount": 18, + "triggerCount": 22, "authType": "oauth", "category": "tools", "integrationType": "crm", diff --git a/apps/sim/blocks/blocks/attio.ts b/apps/sim/blocks/blocks/attio.ts index 21ee5ffdf82..9a6af86f6a3 100644 --- a/apps/sim/blocks/blocks/attio.ts +++ b/apps/sim/blocks/blocks/attio.ts @@ -36,6 +36,7 @@ export const AttioBlock: BlockConfig = { { label: 'Create Note', id: 'create_note' }, { label: 'Delete Note', id: 'delete_note' }, { label: 'List Tasks', id: 'list_tasks' }, + { label: 'Get Task', id: 'get_task' }, { label: 'Create Task', id: 'create_task' }, { label: 'Update Task', id: 'update_task' }, { label: 'Delete Task', id: 'delete_task' }, @@ -490,8 +491,8 @@ Return ONLY the JSON array. No explanations, no markdown, no extra text. title: 'Task ID', type: 'short-input', placeholder: 'Enter the task ID', - condition: { field: 'operation', value: ['update_task', 'delete_task'] }, - required: { field: 'operation', value: ['update_task', 'delete_task'] }, + condition: { field: 'operation', value: ['get_task', 'update_task', 'delete_task'] }, + required: { field: 'operation', value: ['get_task', 'update_task', 'delete_task'] }, }, { id: 'taskFilterObject', @@ -944,7 +945,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ type: 'short-input', placeholder: 'https://example.com/webhook', condition: { field: 'operation', value: ['create_webhook', 'update_webhook'] }, - required: { field: 'operation', value: ['create_webhook', 'update_webhook'] }, + required: { field: 'operation', value: 'create_webhook' }, }, { id: 'webhookSubscriptions', @@ -952,7 +953,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ type: 'code', placeholder: '[{"event_type":"record.created","filter":{"object_id":"..."}}]', condition: { field: 'operation', value: ['create_webhook', 'update_webhook'] }, - required: { field: 'operation', value: ['create_webhook', 'update_webhook'] }, + required: { field: 'operation', value: 'create_webhook' }, wandConfig: { enabled: true, maintainHistory: true, @@ -1040,6 +1041,10 @@ workspace-member.created ...getTrigger('attio_list_entry_created').subBlocks, ...getTrigger('attio_list_entry_updated').subBlocks, ...getTrigger('attio_list_entry_deleted').subBlocks, + ...getTrigger('attio_list_created').subBlocks, + ...getTrigger('attio_list_updated').subBlocks, + ...getTrigger('attio_list_deleted').subBlocks, + ...getTrigger('attio_workspace_member_created').subBlocks, ...getTrigger('attio_webhook').subBlocks, ], @@ -1063,6 +1068,10 @@ workspace-member.created 'attio_list_entry_created', 'attio_list_entry_updated', 'attio_list_entry_deleted', + 'attio_list_created', + 'attio_list_updated', + 'attio_list_deleted', + 'attio_workspace_member_created', 'attio_webhook', ], }, @@ -1081,6 +1090,7 @@ workspace-member.created 'attio_create_note', 'attio_delete_note', 'attio_list_tasks', + 'attio_get_task', 'attio_create_task', 'attio_update_task', 'attio_delete_task', diff --git a/apps/sim/tools/attio/assert_record.ts b/apps/sim/tools/attio/assert_record.ts index 3f1414b102c..00864d740a0 100644 --- a/apps/sim/tools/attio/assert_record.ts +++ b/apps/sim/tools/attio/assert_record.ts @@ -49,7 +49,7 @@ export const attioAssertRecordTool: ToolConfig - `https://api.attio.com/v2/objects/${params.objectType}/records?matching_attribute=${params.matchingAttribute}`, + `https://api.attio.com/v2/objects/${params.objectType.trim()}/records?matching_attribute=${params.matchingAttribute.trim()}`, method: 'PUT', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/create_list_entry.ts b/apps/sim/tools/attio/create_list_entry.ts index 381e61e1d7f..c194b3407b0 100644 --- a/apps/sim/tools/attio/create_list_entry.ts +++ b/apps/sim/tools/attio/create_list_entry.ts @@ -53,7 +53,7 @@ export const attioCreateListEntryTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/lists/${params.list}/entries`, + url: (params) => `https://api.attio.com/v2/lists/${params.list.trim()}/entries`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/create_record.ts b/apps/sim/tools/attio/create_record.ts index 3589c0072c0..4ec2bb13d33 100644 --- a/apps/sim/tools/attio/create_record.ts +++ b/apps/sim/tools/attio/create_record.ts @@ -39,7 +39,7 @@ export const attioCreateRecordTool: ToolConfig `https://api.attio.com/v2/objects/${params.objectType}/records`, + url: (params) => `https://api.attio.com/v2/objects/${params.objectType.trim()}/records`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_comment.ts b/apps/sim/tools/attio/delete_comment.ts index 5fc0aed80b1..e78d71e1b33 100644 --- a/apps/sim/tools/attio/delete_comment.ts +++ b/apps/sim/tools/attio/delete_comment.ts @@ -34,7 +34,7 @@ export const attioDeleteCommentTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/comments/${params.commentId}`, + url: (params) => `https://api.attio.com/v2/comments/${params.commentId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_list_entry.ts b/apps/sim/tools/attio/delete_list_entry.ts index 8aede08525d..e46da4d42ee 100644 --- a/apps/sim/tools/attio/delete_list_entry.ts +++ b/apps/sim/tools/attio/delete_list_entry.ts @@ -40,7 +40,8 @@ export const attioDeleteListEntryTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/lists/${params.list}/entries/${params.entryId}`, + url: (params) => + `https://api.attio.com/v2/lists/${params.list.trim()}/entries/${params.entryId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_note.ts b/apps/sim/tools/attio/delete_note.ts index 2980801cc66..bd5cfe1a05d 100644 --- a/apps/sim/tools/attio/delete_note.ts +++ b/apps/sim/tools/attio/delete_note.ts @@ -31,7 +31,7 @@ export const attioDeleteNoteTool: ToolConfig `https://api.attio.com/v2/notes/${params.noteId}`, + url: (params) => `https://api.attio.com/v2/notes/${params.noteId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_record.ts b/apps/sim/tools/attio/delete_record.ts index 82da6b68c61..cc28440030e 100644 --- a/apps/sim/tools/attio/delete_record.ts +++ b/apps/sim/tools/attio/delete_record.ts @@ -39,7 +39,7 @@ export const attioDeleteRecordTool: ToolConfig - `https://api.attio.com/v2/objects/${params.objectType}/records/${params.recordId}`, + `https://api.attio.com/v2/objects/${params.objectType.trim()}/records/${params.recordId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_task.ts b/apps/sim/tools/attio/delete_task.ts index 02672f6cd3f..088f74707c3 100644 --- a/apps/sim/tools/attio/delete_task.ts +++ b/apps/sim/tools/attio/delete_task.ts @@ -31,7 +31,7 @@ export const attioDeleteTaskTool: ToolConfig `https://api.attio.com/v2/tasks/${params.taskId}`, + url: (params) => `https://api.attio.com/v2/tasks/${params.taskId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/delete_webhook.ts b/apps/sim/tools/attio/delete_webhook.ts index 17f1aa6d978..eaacde34eb6 100644 --- a/apps/sim/tools/attio/delete_webhook.ts +++ b/apps/sim/tools/attio/delete_webhook.ts @@ -34,7 +34,7 @@ export const attioDeleteWebhookTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/webhooks/${params.webhookId}`, + url: (params) => `https://api.attio.com/v2/webhooks/${params.webhookId.trim()}`, method: 'DELETE', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_comment.ts b/apps/sim/tools/attio/get_comment.ts index c32aa84f5aa..3bfa823697c 100644 --- a/apps/sim/tools/attio/get_comment.ts +++ b/apps/sim/tools/attio/get_comment.ts @@ -32,7 +32,7 @@ export const attioGetCommentTool: ToolConfig `https://api.attio.com/v2/comments/${params.commentId}`, + url: (params) => `https://api.attio.com/v2/comments/${params.commentId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_list.ts b/apps/sim/tools/attio/get_list.ts index cf6cbb7b0f4..440b28e81c2 100644 --- a/apps/sim/tools/attio/get_list.ts +++ b/apps/sim/tools/attio/get_list.ts @@ -32,7 +32,7 @@ export const attioGetListTool: ToolConfig `https://api.attio.com/v2/lists/${params.list}`, + url: (params) => `https://api.attio.com/v2/lists/${params.list.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_list_entry.ts b/apps/sim/tools/attio/get_list_entry.ts index 8047a953e7e..22aa1c41481 100644 --- a/apps/sim/tools/attio/get_list_entry.ts +++ b/apps/sim/tools/attio/get_list_entry.ts @@ -39,7 +39,8 @@ export const attioGetListEntryTool: ToolConfig `https://api.attio.com/v2/lists/${params.list}/entries/${params.entryId}`, + url: (params) => + `https://api.attio.com/v2/lists/${params.list.trim()}/entries/${params.entryId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_member.ts b/apps/sim/tools/attio/get_member.ts index 3469856c5be..11f2c08c07d 100644 --- a/apps/sim/tools/attio/get_member.ts +++ b/apps/sim/tools/attio/get_member.ts @@ -32,7 +32,7 @@ export const attioGetMemberTool: ToolConfig `https://api.attio.com/v2/workspace_members/${params.memberId}`, + url: (params) => `https://api.attio.com/v2/workspace_members/${params.memberId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_note.ts b/apps/sim/tools/attio/get_note.ts index 7637edc40d7..0a9ad4a8e76 100644 --- a/apps/sim/tools/attio/get_note.ts +++ b/apps/sim/tools/attio/get_note.ts @@ -32,7 +32,7 @@ export const attioGetNoteTool: ToolConfig `https://api.attio.com/v2/notes/${params.noteId}`, + url: (params) => `https://api.attio.com/v2/notes/${params.noteId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_object.ts b/apps/sim/tools/attio/get_object.ts index 5f67495dda2..09ecc9c267f 100644 --- a/apps/sim/tools/attio/get_object.ts +++ b/apps/sim/tools/attio/get_object.ts @@ -32,7 +32,7 @@ export const attioGetObjectTool: ToolConfig `https://api.attio.com/v2/objects/${params.object}`, + url: (params) => `https://api.attio.com/v2/objects/${params.object.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_record.ts b/apps/sim/tools/attio/get_record.ts index 979492c6aed..2ae04ddbad7 100644 --- a/apps/sim/tools/attio/get_record.ts +++ b/apps/sim/tools/attio/get_record.ts @@ -39,7 +39,7 @@ export const attioGetRecordTool: ToolConfig - `https://api.attio.com/v2/objects/${params.objectType}/records/${params.recordId}`, + `https://api.attio.com/v2/objects/${params.objectType.trim()}/records/${params.recordId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_task.ts b/apps/sim/tools/attio/get_task.ts new file mode 100644 index 00000000000..82ac333861c --- /dev/null +++ b/apps/sim/tools/attio/get_task.ts @@ -0,0 +1,78 @@ +import { createLogger } from '@sim/logger' +import type { ToolConfig } from '@/tools/types' +import type { AttioGetTaskParams, AttioGetTaskResponse } from './types' +import { TASK_OUTPUT_PROPERTIES } from './types' + +const logger = createLogger('AttioGetTask') + +export const attioGetTaskTool: ToolConfig = { + id: 'attio_get_task', + name: 'Attio Get Task', + description: 'Get a single task by ID from Attio', + version: '1.0.0', + + oauth: { + required: true, + provider: 'attio', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'The OAuth access token for the Attio API', + }, + taskId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the task to retrieve', + }, + }, + + request: { + url: (params) => `https://api.attio.com/v2/tasks/${params.taskId.trim()}`, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + if (!response.ok) { + logger.error('Attio API request failed', { data, status: response.status }) + throw new Error(data.message || 'Failed to get task') + } + const task = data.data + const linkedRecords = (task.linked_records ?? []).map( + (r: { target_object_id?: string; target_record_id?: string }) => ({ + targetObjectId: r.target_object_id ?? null, + targetRecordId: r.target_record_id ?? null, + }) + ) + const assignees = (task.assignees ?? []).map( + (a: { referenced_actor_type?: string; referenced_actor_id?: string }) => ({ + type: a.referenced_actor_type ?? null, + id: a.referenced_actor_id ?? null, + }) + ) + return { + success: true, + output: { + taskId: task.id?.task_id ?? null, + content: task.content_plaintext ?? null, + deadlineAt: task.deadline_at ?? null, + isCompleted: task.is_completed ?? false, + linkedRecords, + assignees, + createdByActor: task.created_by_actor ?? null, + createdAt: task.created_at ?? null, + }, + } + }, + + outputs: TASK_OUTPUT_PROPERTIES, +} diff --git a/apps/sim/tools/attio/get_thread.ts b/apps/sim/tools/attio/get_thread.ts index 83bc0fec3c5..62f0a47f1d7 100644 --- a/apps/sim/tools/attio/get_thread.ts +++ b/apps/sim/tools/attio/get_thread.ts @@ -32,7 +32,7 @@ export const attioGetThreadTool: ToolConfig `https://api.attio.com/v2/threads/${params.threadId}`, + url: (params) => `https://api.attio.com/v2/threads/${params.threadId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/get_webhook.ts b/apps/sim/tools/attio/get_webhook.ts index 826899187e5..7cc76f9e1dc 100644 --- a/apps/sim/tools/attio/get_webhook.ts +++ b/apps/sim/tools/attio/get_webhook.ts @@ -32,7 +32,7 @@ export const attioGetWebhookTool: ToolConfig `https://api.attio.com/v2/webhooks/${params.webhookId}`, + url: (params) => `https://api.attio.com/v2/webhooks/${params.webhookId.trim()}`, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/index.ts b/apps/sim/tools/attio/index.ts index 7212fc424af..978993b87de 100644 --- a/apps/sim/tools/attio/index.ts +++ b/apps/sim/tools/attio/index.ts @@ -20,6 +20,7 @@ export { attioGetMemberTool } from './get_member' export { attioGetNoteTool } from './get_note' export { attioGetObjectTool } from './get_object' export { attioGetRecordTool } from './get_record' +export { attioGetTaskTool } from './get_task' export { attioGetThreadTool } from './get_thread' export { attioGetWebhookTool } from './get_webhook' export { attioListListsTool } from './list_lists' diff --git a/apps/sim/tools/attio/list_records.ts b/apps/sim/tools/attio/list_records.ts index 7fae18d4437..39cfef28349 100644 --- a/apps/sim/tools/attio/list_records.ts +++ b/apps/sim/tools/attio/list_records.ts @@ -56,7 +56,7 @@ export const attioListRecordsTool: ToolConfig `https://api.attio.com/v2/objects/${params.objectType}/records/query`, + url: (params) => `https://api.attio.com/v2/objects/${params.objectType.trim()}/records/query`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/query_list_entries.ts b/apps/sim/tools/attio/query_list_entries.ts index d234c2aec37..6b0574d88b9 100644 --- a/apps/sim/tools/attio/query_list_entries.ts +++ b/apps/sim/tools/attio/query_list_entries.ts @@ -60,7 +60,7 @@ export const attioQueryListEntriesTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/lists/${params.list}/entries/query`, + url: (params) => `https://api.attio.com/v2/lists/${params.list.trim()}/entries/query`, method: 'POST', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/types.ts b/apps/sim/tools/attio/types.ts index cb7c7264262..0f2bb2a9c22 100644 --- a/apps/sim/tools/attio/types.ts +++ b/apps/sim/tools/attio/types.ts @@ -529,6 +529,26 @@ export interface AttioUpdateTaskResponse extends ToolResponse { } } +/** Params for getting a single task */ +export interface AttioGetTaskParams { + accessToken: string + taskId: string +} + +/** Response for getting a single task */ +export interface AttioGetTaskResponse extends ToolResponse { + output: { + taskId: string | null + content: string | null + deadlineAt: string | null + isCompleted: boolean + linkedRecords: Array<{ targetObjectId: string | null; targetRecordId: string | null }> + assignees: Array<{ type: string | null; id: string | null }> + createdByActor: unknown + createdAt: string | null + } +} + /** Response for deleting a task */ export interface AttioDeleteTaskResponse extends ToolResponse { output: { @@ -1093,6 +1113,7 @@ export type AttioResponse = | AttioListTasksResponse | AttioCreateTaskResponse | AttioUpdateTaskResponse + | AttioGetTaskResponse | AttioDeleteTaskResponse | AttioListObjectsResponse | AttioGetObjectResponse diff --git a/apps/sim/tools/attio/update_list.ts b/apps/sim/tools/attio/update_list.ts index e54176a128a..e36f74b2ece 100644 --- a/apps/sim/tools/attio/update_list.ts +++ b/apps/sim/tools/attio/update_list.ts @@ -58,7 +58,7 @@ export const attioUpdateListTool: ToolConfig `https://api.attio.com/v2/lists/${params.list}`, + url: (params) => `https://api.attio.com/v2/lists/${params.list.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/update_list_entry.ts b/apps/sim/tools/attio/update_list_entry.ts index d6da7dc01e1..760e960cdfc 100644 --- a/apps/sim/tools/attio/update_list_entry.ts +++ b/apps/sim/tools/attio/update_list_entry.ts @@ -47,7 +47,8 @@ export const attioUpdateListEntryTool: ToolConfig< }, request: { - url: (params) => `https://api.attio.com/v2/lists/${params.list}/entries/${params.entryId}`, + url: (params) => + `https://api.attio.com/v2/lists/${params.list.trim()}/entries/${params.entryId.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/update_object.ts b/apps/sim/tools/attio/update_object.ts index 1b8c0024a9a..0136bae05dd 100644 --- a/apps/sim/tools/attio/update_object.ts +++ b/apps/sim/tools/attio/update_object.ts @@ -51,7 +51,7 @@ export const attioUpdateObjectTool: ToolConfig `https://api.attio.com/v2/objects/${params.object}`, + url: (params) => `https://api.attio.com/v2/objects/${params.object.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/update_record.ts b/apps/sim/tools/attio/update_record.ts index cf74d9a3b78..21d613f9292 100644 --- a/apps/sim/tools/attio/update_record.ts +++ b/apps/sim/tools/attio/update_record.ts @@ -46,7 +46,7 @@ export const attioUpdateRecordTool: ToolConfig - `https://api.attio.com/v2/objects/${params.objectType}/records/${params.recordId}`, + `https://api.attio.com/v2/objects/${params.objectType.trim()}/records/${params.recordId.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/update_task.ts b/apps/sim/tools/attio/update_task.ts index 9b1e4e21e82..e55394b44ce 100644 --- a/apps/sim/tools/attio/update_task.ts +++ b/apps/sim/tools/attio/update_task.ts @@ -56,7 +56,7 @@ export const attioUpdateTaskTool: ToolConfig `https://api.attio.com/v2/tasks/${params.taskId}`, + url: (params) => `https://api.attio.com/v2/tasks/${params.taskId.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, diff --git a/apps/sim/tools/attio/update_webhook.ts b/apps/sim/tools/attio/update_webhook.ts index 662691c4fde..0bee35a572c 100644 --- a/apps/sim/tools/attio/update_webhook.ts +++ b/apps/sim/tools/attio/update_webhook.ts @@ -34,41 +34,38 @@ export const attioUpdateWebhookTool: ToolConfig< }, targetUrl: { type: 'string', - required: true, + required: false, visibility: 'user-or-llm', description: 'HTTPS target URL for webhook delivery', }, subscriptions: { type: 'string', - required: true, + required: false, visibility: 'user-or-llm', description: 'JSON array of subscriptions, e.g. [{"event_type":"note.created"}]', }, }, request: { - url: (params) => `https://api.attio.com/v2/webhooks/${params.webhookId}`, + url: (params) => `https://api.attio.com/v2/webhooks/${params.webhookId.trim()}`, method: 'PATCH', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}`, 'Content-Type': 'application/json', }), body: (params) => { - let subscriptions: unknown = [] + const data: Record = {} + if (params.targetUrl) data.target_url = params.targetUrl if (params.subscriptions) { try { - subscriptions = + data.subscriptions = typeof params.subscriptions === 'string' ? JSON.parse(params.subscriptions) : params.subscriptions } catch { - subscriptions = [] + data.subscriptions = [] } } - const data: Record = { - target_url: params.targetUrl, - subscriptions, - } return { data } }, }, diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 36bb3e2739e..d7f6e9c62d6 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -149,6 +149,7 @@ import { attioGetNoteTool, attioGetObjectTool, attioGetRecordTool, + attioGetTaskTool, attioGetThreadTool, attioGetWebhookTool, attioListListsTool, @@ -3911,6 +3912,7 @@ export const tools: Record = { attio_get_note: attioGetNoteTool, attio_get_object: attioGetObjectTool, attio_get_record: attioGetRecordTool, + attio_get_task: attioGetTaskTool, attio_get_thread: attioGetThreadTool, attio_get_webhook: attioGetWebhookTool, attio_list_lists: attioListListsTool, diff --git a/apps/sim/triggers/attio/index.ts b/apps/sim/triggers/attio/index.ts index f8c0e27f294..a6e77f3a634 100644 --- a/apps/sim/triggers/attio/index.ts +++ b/apps/sim/triggers/attio/index.ts @@ -2,9 +2,12 @@ export { attioCommentCreatedTrigger } from './comment_created' export { attioCommentDeletedTrigger } from './comment_deleted' export { attioCommentResolvedTrigger } from './comment_resolved' export { attioCommentUnresolvedTrigger } from './comment_unresolved' +export { attioListCreatedTrigger } from './list_created' +export { attioListDeletedTrigger } from './list_deleted' export { attioListEntryCreatedTrigger } from './list_entry_created' export { attioListEntryDeletedTrigger } from './list_entry_deleted' export { attioListEntryUpdatedTrigger } from './list_entry_updated' +export { attioListUpdatedTrigger } from './list_updated' export { attioNoteCreatedTrigger } from './note_created' export { attioNoteDeletedTrigger } from './note_deleted' export { attioNoteUpdatedTrigger } from './note_updated' @@ -16,3 +19,4 @@ export { attioTaskCreatedTrigger } from './task_created' export { attioTaskDeletedTrigger } from './task_deleted' export { attioTaskUpdatedTrigger } from './task_updated' export { attioWebhookTrigger } from './webhook' +export { attioWorkspaceMemberCreatedTrigger } from './workspace_member_created' diff --git a/apps/sim/triggers/attio/list_created.ts b/apps/sim/triggers/attio/list_created.ts new file mode 100644 index 00000000000..5f55567356f --- /dev/null +++ b/apps/sim/triggers/attio/list_created.ts @@ -0,0 +1,29 @@ +import { AttioIcon } from '@/components/icons' +import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Attio List Created Trigger + * + * Triggers when a list is created in Attio. + */ +export const attioListCreatedTrigger: TriggerConfig = { + id: 'attio_list_created', + name: 'Attio List Created', + provider: 'attio', + description: 'Trigger workflow when a list is created in Attio', + version: '1.0.0', + icon: AttioIcon, + + subBlocks: buildAttioTriggerSubBlocks('attio_list_created'), + + outputs: buildListOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Attio-Signature': 'hmac-sha256-signature', + }, + }, +} diff --git a/apps/sim/triggers/attio/list_deleted.ts b/apps/sim/triggers/attio/list_deleted.ts new file mode 100644 index 00000000000..9796cf257bb --- /dev/null +++ b/apps/sim/triggers/attio/list_deleted.ts @@ -0,0 +1,29 @@ +import { AttioIcon } from '@/components/icons' +import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Attio List Deleted Trigger + * + * Triggers when a list is deleted in Attio. + */ +export const attioListDeletedTrigger: TriggerConfig = { + id: 'attio_list_deleted', + name: 'Attio List Deleted', + provider: 'attio', + description: 'Trigger workflow when a list is deleted in Attio', + version: '1.0.0', + icon: AttioIcon, + + subBlocks: buildAttioTriggerSubBlocks('attio_list_deleted'), + + outputs: buildListOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Attio-Signature': 'hmac-sha256-signature', + }, + }, +} diff --git a/apps/sim/triggers/attio/list_updated.ts b/apps/sim/triggers/attio/list_updated.ts new file mode 100644 index 00000000000..005cab5f6fe --- /dev/null +++ b/apps/sim/triggers/attio/list_updated.ts @@ -0,0 +1,29 @@ +import { AttioIcon } from '@/components/icons' +import { buildAttioTriggerSubBlocks, buildListOutputs } from '@/triggers/attio/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Attio List Updated Trigger + * + * Triggers when a list is updated in Attio. + */ +export const attioListUpdatedTrigger: TriggerConfig = { + id: 'attio_list_updated', + name: 'Attio List Updated', + provider: 'attio', + description: 'Trigger workflow when a list is updated in Attio', + version: '1.0.0', + icon: AttioIcon, + + subBlocks: buildAttioTriggerSubBlocks('attio_list_updated'), + + outputs: buildListOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Attio-Signature': 'hmac-sha256-signature', + }, + }, +} diff --git a/apps/sim/triggers/attio/utils.ts b/apps/sim/triggers/attio/utils.ts index 11dfa89c309..9276f91ad46 100644 --- a/apps/sim/triggers/attio/utils.ts +++ b/apps/sim/triggers/attio/utils.ts @@ -19,6 +19,10 @@ export const attioTriggerOptions = [ { label: 'List Entry Created', id: 'attio_list_entry_created' }, { label: 'List Entry Updated', id: 'attio_list_entry_updated' }, { label: 'List Entry Deleted', id: 'attio_list_entry_deleted' }, + { label: 'List Created', id: 'attio_list_created' }, + { label: 'List Updated', id: 'attio_list_updated' }, + { label: 'List Deleted', id: 'attio_list_deleted' }, + { label: 'Workspace Member Created', id: 'attio_workspace_member_created' }, { label: 'Generic Webhook (All Events)', id: 'attio_webhook' }, ] @@ -235,6 +239,42 @@ export function buildCommentOutputs(): Record { } } +/** + * List event outputs. + */ +function buildListIdOutputs(): Record { + return { + workspaceId: { type: 'string', description: 'The workspace ID' }, + listId: { type: 'string', description: 'The list ID' }, + } +} + +/** List created/updated/deleted outputs. */ +export function buildListOutputs(): Record { + return { + ...buildBaseWebhookOutputs(), + ...buildListIdOutputs(), + } +} + +/** + * Workspace member event outputs. + */ +function buildWorkspaceMemberIdOutputs(): Record { + return { + workspaceId: { type: 'string', description: 'The workspace ID' }, + workspaceMemberId: { type: 'string', description: 'The workspace member ID' }, + } +} + +/** Workspace member outputs. */ +export function buildWorkspaceMemberOutputs(): Record { + return { + ...buildBaseWebhookOutputs(), + ...buildWorkspaceMemberIdOutputs(), + } +} + /** List entry created/deleted outputs. */ export function buildListEntryOutputs(): Record { return { @@ -276,7 +316,7 @@ export const TRIGGER_EVENT_MAP: Record = { attio_record_deleted: ['record.deleted'], attio_record_merged: ['record.merged'], attio_note_created: ['note.created'], - attio_note_updated: ['note.updated', 'note.content-updated'], + attio_note_updated: ['note.updated', 'note-content.updated'], attio_note_deleted: ['note.deleted'], attio_task_created: ['task.created'], attio_task_updated: ['task.updated'], @@ -288,6 +328,10 @@ export const TRIGGER_EVENT_MAP: Record = { attio_list_entry_created: ['list-entry.created'], attio_list_entry_updated: ['list-entry.updated'], attio_list_entry_deleted: ['list-entry.deleted'], + attio_list_created: ['list.created'], + attio_list_updated: ['list.updated'], + attio_list_deleted: ['list.deleted'], + attio_workspace_member_created: ['workspace-member.created'], } /** @@ -445,6 +489,35 @@ export function extractAttioListEntryUpdatedData( } } +/** + * Extracts formatted data from an Attio list event payload. + * Used for list.created, list.updated, list.deleted triggers. + */ +export function extractAttioListData(body: Record): Record { + const event = getAttioEvent(body) ?? {} + const id = (event.id as Record) ?? {} + return { + eventType: event.event_type ?? null, + workspaceId: id.workspace_id ?? null, + listId: id.list_id ?? null, + } +} + +/** + * Extracts formatted data from an Attio workspace-member.created event payload. + */ +export function extractAttioWorkspaceMemberData( + body: Record +): Record { + const event = getAttioEvent(body) ?? {} + const id = (event.id as Record) ?? {} + return { + eventType: event.event_type ?? null, + workspaceId: id.workspace_id ?? null, + workspaceMemberId: id.workspace_member_id ?? null, + } +} + /** * Extracts formatted data from a generic Attio webhook payload. * Passes through the first event with camelCase field mapping. diff --git a/apps/sim/triggers/attio/workspace_member_created.ts b/apps/sim/triggers/attio/workspace_member_created.ts new file mode 100644 index 00000000000..6d6d8145e62 --- /dev/null +++ b/apps/sim/triggers/attio/workspace_member_created.ts @@ -0,0 +1,29 @@ +import { AttioIcon } from '@/components/icons' +import { buildAttioTriggerSubBlocks, buildWorkspaceMemberOutputs } from '@/triggers/attio/utils' +import type { TriggerConfig } from '@/triggers/types' + +/** + * Attio Workspace Member Created Trigger + * + * Triggers when a new member is added to the Attio workspace. + */ +export const attioWorkspaceMemberCreatedTrigger: TriggerConfig = { + id: 'attio_workspace_member_created', + name: 'Attio Workspace Member Created', + provider: 'attio', + description: 'Trigger workflow when a new member is added to the Attio workspace', + version: '1.0.0', + icon: AttioIcon, + + subBlocks: buildAttioTriggerSubBlocks('attio_workspace_member_created'), + + outputs: buildWorkspaceMemberOutputs(), + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Attio-Signature': 'hmac-sha256-signature', + }, + }, +} diff --git a/apps/sim/triggers/registry.ts b/apps/sim/triggers/registry.ts index b0c61fe8c1c..4390bfeefff 100644 --- a/apps/sim/triggers/registry.ts +++ b/apps/sim/triggers/registry.ts @@ -12,9 +12,12 @@ import { attioCommentDeletedTrigger, attioCommentResolvedTrigger, attioCommentUnresolvedTrigger, + attioListCreatedTrigger, + attioListDeletedTrigger, attioListEntryCreatedTrigger, attioListEntryDeletedTrigger, attioListEntryUpdatedTrigger, + attioListUpdatedTrigger, attioNoteCreatedTrigger, attioNoteDeletedTrigger, attioNoteUpdatedTrigger, @@ -26,6 +29,7 @@ import { attioTaskDeletedTrigger, attioTaskUpdatedTrigger, attioWebhookTrigger, + attioWorkspaceMemberCreatedTrigger, } from '@/triggers/attio' import { calcomBookingCancelledTrigger, @@ -200,6 +204,10 @@ export const TRIGGER_REGISTRY: TriggerRegistry = { attio_list_entry_created: attioListEntryCreatedTrigger, attio_list_entry_updated: attioListEntryUpdatedTrigger, attio_list_entry_deleted: attioListEntryDeletedTrigger, + attio_list_created: attioListCreatedTrigger, + attio_list_updated: attioListUpdatedTrigger, + attio_list_deleted: attioListDeletedTrigger, + attio_workspace_member_created: attioWorkspaceMemberCreatedTrigger, calendly_webhook: calendlyWebhookTrigger, calendly_invitee_created: calendlyInviteeCreatedTrigger, calendly_invitee_canceled: calendlyInviteeCanceledTrigger, From c4b93116cf5de0a05f0c936deb2abada6d492943 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 31 Mar 2026 16:28:52 -0700 Subject: [PATCH 2/2] fix(attio): wire new trigger extractors into dispatcher, trim targetUrl Add extractAttioListData and extractAttioWorkspaceMemberData dispatch branches in utils.server.ts so the four new triggers return correct outputs instead of falling through to generic extraction. Also add missing .trim() on targetUrl in update_webhook. Co-Authored-By: Claude Opus 4.6 --- apps/sim/lib/webhooks/utils.server.ts | 12 ++++++++++++ apps/sim/tools/attio/update_webhook.ts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index 3eec55697f4..4decf492eb5 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -1311,6 +1311,8 @@ export async function formatWebhookInput( extractAttioCommentData, extractAttioListEntryData, extractAttioListEntryUpdatedData, + extractAttioListData, + extractAttioWorkspaceMemberData, extractAttioGenericData, } = await import('@/triggers/attio/utils') @@ -1341,6 +1343,16 @@ export async function formatWebhookInput( if (triggerId === 'attio_list_entry_created' || triggerId === 'attio_list_entry_deleted') { return extractAttioListEntryData(body) } + if ( + triggerId === 'attio_list_created' || + triggerId === 'attio_list_updated' || + triggerId === 'attio_list_deleted' + ) { + return extractAttioListData(body) + } + if (triggerId === 'attio_workspace_member_created') { + return extractAttioWorkspaceMemberData(body) + } return extractAttioGenericData(body) } diff --git a/apps/sim/tools/attio/update_webhook.ts b/apps/sim/tools/attio/update_webhook.ts index 0bee35a572c..659bb8f9648 100644 --- a/apps/sim/tools/attio/update_webhook.ts +++ b/apps/sim/tools/attio/update_webhook.ts @@ -55,7 +55,7 @@ export const attioUpdateWebhookTool: ToolConfig< }), body: (params) => { const data: Record = {} - if (params.targetUrl) data.target_url = params.targetUrl + if (params.targetUrl) data.target_url = params.targetUrl.trim() if (params.subscriptions) { try { data.subscriptions =