improvement(attio): validate integration, fix event bug, add missing tool and triggers (#3872)

* improvement(attio): validate integration, fix event bug, add missing tool and triggers

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-03-31 16:44:49 -07:00
committed by GitHub
parent 512558dcb3
commit e45fbe0184
41 changed files with 422 additions and 44 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -36,6 +36,7 @@ export const AttioBlock: BlockConfig<AttioResponse> = {
{ 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',

View File

@@ -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)
}

View File

@@ -49,7 +49,7 @@ export const attioAssertRecordTool: ToolConfig<AttioAssertRecordParams, AttioAss
request: {
url: (params) =>
`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}`,

View File

@@ -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}`,

View File

@@ -39,7 +39,7 @@ export const attioCreateRecordTool: ToolConfig<AttioCreateRecordParams, AttioCre
},
request: {
url: (params) => `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}`,

View File

@@ -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}`,

View File

@@ -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}`,

View File

@@ -31,7 +31,7 @@ export const attioDeleteNoteTool: ToolConfig<AttioDeleteNoteParams, AttioDeleteN
},
request: {
url: (params) => `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}`,

View File

@@ -39,7 +39,7 @@ export const attioDeleteRecordTool: ToolConfig<AttioDeleteRecordParams, AttioDel
request: {
url: (params) =>
`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}`,

View File

@@ -31,7 +31,7 @@ export const attioDeleteTaskTool: ToolConfig<AttioDeleteTaskParams, AttioDeleteT
},
request: {
url: (params) => `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}`,

View File

@@ -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}`,

View File

@@ -32,7 +32,7 @@ export const attioGetCommentTool: ToolConfig<AttioGetCommentParams, AttioGetComm
},
request: {
url: (params) => `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}`,

View File

@@ -32,7 +32,7 @@ export const attioGetListTool: ToolConfig<AttioGetListParams, AttioGetListRespon
},
request: {
url: (params) => `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}`,

View File

@@ -39,7 +39,8 @@ export const attioGetListEntryTool: ToolConfig<AttioGetListEntryParams, AttioGet
},
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: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,

View File

@@ -32,7 +32,7 @@ export const attioGetMemberTool: ToolConfig<AttioGetMemberParams, AttioGetMember
},
request: {
url: (params) => `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}`,

View File

@@ -32,7 +32,7 @@ export const attioGetNoteTool: ToolConfig<AttioGetNoteParams, AttioGetNoteRespon
},
request: {
url: (params) => `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}`,

View File

@@ -32,7 +32,7 @@ export const attioGetObjectTool: ToolConfig<AttioGetObjectParams, AttioGetObject
},
request: {
url: (params) => `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}`,

View File

@@ -39,7 +39,7 @@ export const attioGetRecordTool: ToolConfig<AttioGetRecordParams, AttioGetRecord
request: {
url: (params) =>
`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}`,

View File

@@ -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<AttioGetTaskParams, AttioGetTaskResponse> = {
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,
}

View File

@@ -32,7 +32,7 @@ export const attioGetThreadTool: ToolConfig<AttioGetThreadParams, AttioGetThread
},
request: {
url: (params) => `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}`,

View File

@@ -32,7 +32,7 @@ export const attioGetWebhookTool: ToolConfig<AttioGetWebhookParams, AttioGetWebh
},
request: {
url: (params) => `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}`,

View File

@@ -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'

View File

@@ -56,7 +56,7 @@ export const attioListRecordsTool: ToolConfig<AttioListRecordsParams, AttioListR
},
request: {
url: (params) => `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}`,

View File

@@ -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}`,

View File

@@ -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

View File

@@ -58,7 +58,7 @@ export const attioUpdateListTool: ToolConfig<AttioUpdateListParams, AttioUpdateL
},
request: {
url: (params) => `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}`,

View File

@@ -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}`,

View File

@@ -51,7 +51,7 @@ export const attioUpdateObjectTool: ToolConfig<AttioUpdateObjectParams, AttioUpd
},
request: {
url: (params) => `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}`,

View File

@@ -46,7 +46,7 @@ export const attioUpdateRecordTool: ToolConfig<AttioUpdateRecordParams, AttioUpd
request: {
url: (params) =>
`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}`,

View File

@@ -56,7 +56,7 @@ export const attioUpdateTaskTool: ToolConfig<AttioUpdateTaskParams, AttioUpdateT
},
request: {
url: (params) => `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}`,

View File

@@ -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<string, unknown> = {}
if (params.targetUrl) data.target_url = params.targetUrl.trim()
if (params.subscriptions) {
try {
subscriptions =
data.subscriptions =
typeof params.subscriptions === 'string'
? JSON.parse(params.subscriptions)
: params.subscriptions
} catch {
subscriptions = []
data.subscriptions = []
}
}
const data: Record<string, unknown> = {
target_url: params.targetUrl,
subscriptions,
}
return { data }
},
},

View File

@@ -149,6 +149,7 @@ import {
attioGetNoteTool,
attioGetObjectTool,
attioGetRecordTool,
attioGetTaskTool,
attioGetThreadTool,
attioGetWebhookTool,
attioListListsTool,
@@ -3970,6 +3971,7 @@ export const tools: Record<string, ToolConfig> = {
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,

View File

@@ -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'

View File

@@ -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',
},
},
}

View File

@@ -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',
},
},
}

View File

@@ -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',
},
},
}

View File

@@ -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<string, TriggerOutput> {
}
}
/**
* List event outputs.
*/
function buildListIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
listId: { type: 'string', description: 'The list ID' },
}
}
/** List created/updated/deleted outputs. */
export function buildListOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildListIdOutputs(),
}
}
/**
* Workspace member event outputs.
*/
function buildWorkspaceMemberIdOutputs(): Record<string, TriggerOutput> {
return {
workspaceId: { type: 'string', description: 'The workspace ID' },
workspaceMemberId: { type: 'string', description: 'The workspace member ID' },
}
}
/** Workspace member outputs. */
export function buildWorkspaceMemberOutputs(): Record<string, TriggerOutput> {
return {
...buildBaseWebhookOutputs(),
...buildWorkspaceMemberIdOutputs(),
}
}
/** List entry created/deleted outputs. */
export function buildListEntryOutputs(): Record<string, TriggerOutput> {
return {
@@ -276,7 +316,7 @@ export const TRIGGER_EVENT_MAP: Record<string, string[]> = {
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<string, string[]> = {
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<string, unknown>): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
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<string, unknown>
): Record<string, unknown> {
const event = getAttioEvent(body) ?? {}
const id = (event.id as Record<string, unknown>) ?? {}
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.

View File

@@ -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',
},
},
}

View File

@@ -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,