Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/docs/content/docs/en/tools/attio.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1012,8 +1012,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 | No | New HTTPS target URL |
| `subscriptions` | string | No | New JSON array of subscriptions |
| `targetUrl` | string | Yes | HTTPS target URL for webhook delivery |
| `subscriptions` | string | Yes | JSON array of subscriptions, e.g. \[\{"event_type":"note.created"\}\] |

#### Output

Expand Down
90 changes: 57 additions & 33 deletions apps/sim/blocks/blocks/attio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const AttioBlock: BlockConfig<AttioResponse> = {
{
id: 'values',
title: 'Values',
type: 'long-input',
type: 'code',
placeholder: '{"name": "Acme Corp", "domains": [{"domain": "acme.com"}]}',
condition: {
field: 'operation',
Expand All @@ -161,21 +161,25 @@ export const AttioBlock: BlockConfig<AttioResponse> = {
Return ONLY the JSON object with Attio attribute values. No explanations, no markdown, no extra text.

### ATTIO VALUES STRUCTURE
Keys are attribute slugs, values follow Attio's attribute format. Simple values can be strings; complex values are arrays of objects.
Keys are attribute slugs. Most text attributes use array-of-objects format [{"value": "..."}]. Special attributes have their own format.

### COMMON PEOPLE ATTRIBUTES
- name: [{"first_name": "...", "last_name": "..."}]
- name: [{"first_name": "...", "last_name": "...", "full_name": "..."}] (personal-name type, full_name is required)
- email_addresses: [{"email_address": "..."}]
- phone_numbers: [{"original_phone_number": "...", "country_code": "US"}]
- job_title, description, linkedin, twitter
- job_title: [{"value": "..."}]
- description: [{"value": "..."}]
- linkedin: [{"value": "https://linkedin.com/in/..."}]
- twitter: [{"value": "@handle"}]

### COMMON COMPANY ATTRIBUTES
- name: [{"value": "..."}]
- domains: [{"domain": "..."}]
- description, primary_location, categories
- description: [{"value": "..."}]
- primary_location: [{"line_1": "...", "locality": "...", "region": "...", "postcode": "...", "country_code": "US"}]

### EXAMPLES
Person: {"name": [{"first_name": "John", "last_name": "Doe"}], "email_addresses": [{"email_address": "john@example.com"}]}
Person: {"name": [{"first_name": "John", "last_name": "Doe", "full_name": "John Doe"}], "email_addresses": [{"email_address": "john@example.com"}], "job_title": [{"value": "Engineer"}]}
Company: {"name": [{"value": "Acme Corp"}], "domains": [{"domain": "acme.com"}]}`,
placeholder: 'Describe the record values you want to set...',
generationType: 'json-object',
Expand All @@ -184,8 +188,8 @@ Company: {"name": [{"value": "Acme Corp"}], "domains": [{"domain": "acme.com"}]}
{
id: 'filter',
title: 'Filter',
type: 'long-input',
placeholder: '{"name": "John Smith"} (optional)',
type: 'code',
placeholder: '{"name": "John Smith"}',
condition: { field: 'operation', value: 'list_records' },
wandConfig: {
enabled: true,
Expand Down Expand Up @@ -215,8 +219,8 @@ Empty (list all): {}`,
{
id: 'sorts',
title: 'Sort',
type: 'long-input',
placeholder: '[{"direction":"asc","attribute":"name"}] (optional)',
type: 'code',
placeholder: '[{"direction":"asc","attribute":"name"}]',
condition: { field: 'operation', value: 'list_records' },
wandConfig: {
enabled: true,
Expand Down Expand Up @@ -332,7 +336,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ
id: 'noteMeetingId',
title: 'Meeting ID',
type: 'short-input',
placeholder: 'Link to a meeting (optional)',
placeholder: 'Link to a meeting',
condition: { field: 'operation', value: 'create_note' },
mode: 'advanced',
},
Expand All @@ -358,7 +362,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ
id: 'taskDeadline',
title: 'Deadline',
type: 'short-input',
placeholder: '2024-12-01T15:00:00.000Z (optional)',
placeholder: '2024-12-01T15:00:00.000Z',
condition: { field: 'operation', value: ['create_task', 'update_task'] },
wandConfig: {
enabled: true,
Expand Down Expand Up @@ -394,8 +398,8 @@ YYYY-MM-DDTHH:mm:ss.SSSZ
{
id: 'taskLinkedRecords',
title: 'Linked Records',
type: 'long-input',
placeholder: '[{"target_object":"people","target_record_id":"..."}] (optional)',
type: 'code',
placeholder: '[{"target_object":"people","target_record_id":"..."}]',
condition: { field: 'operation', value: ['create_task', 'update_task'] },
wandConfig: {
enabled: true,
Expand All @@ -420,9 +424,8 @@ Return ONLY the JSON array. No explanations, no markdown, no extra text.
{
id: 'taskAssignees',
title: 'Assignees',
type: 'long-input',
placeholder:
'[{"referenced_actor_type":"workspace-member","referenced_actor_id":"..."}] (optional)',
type: 'code',
placeholder: '[{"referenced_actor_type":"workspace-member","referenced_actor_id":"..."}]',
condition: { field: 'operation', value: ['create_task', 'update_task'] },
wandConfig: {
enabled: true,
Expand Down Expand Up @@ -456,21 +459,21 @@ Return ONLY the JSON array. No explanations, no markdown, no extra text.
id: 'taskFilterObject',
title: 'Linked Object Type',
type: 'short-input',
placeholder: 'e.g. people, companies (optional)',
placeholder: 'e.g. people, companies',
condition: { field: 'operation', value: 'list_tasks' },
},
{
id: 'taskFilterRecordId',
title: 'Linked Record ID',
type: 'short-input',
placeholder: 'Filter by linked record ID (optional)',
placeholder: 'Filter by linked record ID',
condition: { field: 'operation', value: 'list_tasks' },
},
{
id: 'taskFilterAssignee',
title: 'Assignee',
type: 'short-input',
placeholder: 'Filter by assignee email or ID (optional)',
placeholder: 'Filter by assignee email or ID',
condition: { field: 'operation', value: 'list_tasks' },
},
{
Expand Down Expand Up @@ -571,7 +574,7 @@ Return ONLY the JSON array. No explanations, no markdown, no extra text.
id: 'listApiSlug',
title: 'API Slug',
type: 'short-input',
placeholder: 'e.g. my_list (optional, auto-generated)',
placeholder: 'e.g. my_list (auto-generated from name)',
condition: { field: 'operation', value: ['create_list', 'update_list'] },
mode: 'advanced',
},
Expand Down Expand Up @@ -622,8 +625,8 @@ Return ONLY the JSON array. No explanations, no markdown, no extra text.
{
id: 'entryValues',
title: 'Entry Values',
type: 'long-input',
placeholder: '{"attribute_slug": "value"} (optional)',
type: 'code',
placeholder: '{"attribute_slug": "value"}',
condition: {
field: 'operation',
value: ['create_list_entry', 'update_list_entry'],
Expand Down Expand Up @@ -652,8 +655,8 @@ Keys are list attribute slugs. Values follow Attio attribute format.
{
id: 'entryFilter',
title: 'Filter',
type: 'long-input',
placeholder: '{"attribute": {"$operator": "value"}} (optional)',
type: 'code',
placeholder: '{"attribute": {"$operator": "value"}}',
condition: { field: 'operation', value: 'query_list_entries' },
wandConfig: {
enabled: true,
Expand All @@ -679,8 +682,8 @@ Logical: $and, $or, $not
{
id: 'entrySorts',
title: 'Sort',
type: 'long-input',
placeholder: '[{"direction":"asc","attribute":"created_at"}] (optional)',
type: 'code',
placeholder: '[{"direction":"asc","attribute":"created_at"}]',
condition: { field: 'operation', value: 'query_list_entries' },
wandConfig: {
enabled: true,
Expand Down Expand Up @@ -819,7 +822,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ
id: 'threadFilterRecordId',
title: 'Record ID',
type: 'short-input',
placeholder: 'Filter by record ID (optional)',
placeholder: 'Filter by record ID',
condition: { field: 'operation', value: 'list_threads' },
},
{
Expand All @@ -833,7 +836,7 @@ YYYY-MM-DDTHH:mm:ss.SSSZ
id: 'threadFilterEntryId',
title: 'Entry ID',
type: 'short-input',
placeholder: 'Filter by entry ID (optional)',
placeholder: 'Filter by entry ID',
condition: { field: 'operation', value: 'list_threads' },
},
{
Expand Down Expand Up @@ -865,15 +868,15 @@ 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' },
required: { field: 'operation', value: ['create_webhook', 'update_webhook'] },
},
{
id: 'webhookSubscriptions',
title: 'Subscriptions',
type: 'long-input',
type: 'code',
placeholder: '[{"event_type":"record.created","filter":{"object_id":"..."}}]',
condition: { field: 'operation', value: ['create_webhook', 'update_webhook'] },
required: { field: 'operation', value: 'create_webhook' },
required: { field: 'operation', value: ['create_webhook', 'update_webhook'] },
wandConfig: {
enabled: true,
maintainHistory: true,
Expand Down Expand Up @@ -911,7 +914,8 @@ workspace-member.created
id: 'limit',
title: 'Limit',
type: 'short-input',
placeholder: 'Max results (optional)',
placeholder: 'Max results',
mode: 'advanced',
condition: {
field: 'operation',
value: [
Expand All @@ -925,6 +929,24 @@ workspace-member.created
],
},
},
{
id: 'offset',
title: 'Offset',
type: 'short-input',
placeholder: 'Number of results to skip',
mode: 'advanced',
condition: {
field: 'operation',
value: [
'list_records',
'list_notes',
'list_tasks',
'query_list_entries',
'list_threads',
'list_webhooks',
],
},
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offset field missing search_records operation condition

Medium Severity

The new offset sub-block's condition.value array omits search_records, while the limit sub-block includes it. The Attio search records API supports offset-based pagination, so users won't be able to paginate search results using offset, creating an inconsistency between the two pagination controls.

Additional Locations (1)

Fix in Cursor Fix in Web

...getTrigger('attio_record_created').subBlocks,
...getTrigger('attio_record_updated').subBlocks,
...getTrigger('attio_record_deleted').subBlocks,
Expand Down Expand Up @@ -1103,6 +1125,7 @@ workspace-member.created

// Shared params
if (params.limit) cleanParams.limit = Number(params.limit)
if (params.offset) cleanParams.offset = Number(params.offset)

return cleanParams
},
Expand Down Expand Up @@ -1164,6 +1187,7 @@ workspace-member.created
webhookTargetUrl: { type: 'string', description: 'Webhook target URL' },
webhookSubscriptions: { type: 'json', description: 'Webhook event subscriptions' },
limit: { type: 'string', description: 'Maximum number of results' },
offset: { type: 'string', description: 'Number of results to skip for pagination' },
},

outputs: {
Expand Down
28 changes: 28 additions & 0 deletions apps/sim/lib/webhooks/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { convertSquareBracketsToTwiML } from '@/lib/webhooks/utils'
import {
handleSlackChallenge,
handleWhatsAppVerification,
validateAttioSignature,
validateCalcomSignature,
validateCirclebackSignature,
validateFirefliesSignature,
Expand Down Expand Up @@ -597,6 +598,33 @@ export async function verifyProviderAuth(
}
}

if (foundWebhook.provider === 'attio') {
const secret = providerConfig.webhookSecret as string | undefined

if (!secret) {
logger.debug(
`[${requestId}] Attio webhook ${foundWebhook.id} has no signing secret, skipping signature verification`
)
} else {
const signature = request.headers.get('Attio-Signature')

if (!signature) {
logger.warn(`[${requestId}] Attio webhook missing signature header`)
return new NextResponse('Unauthorized - Missing Attio signature', { status: 401 })
}

const isValidSignature = validateAttioSignature(secret, signature, rawBody)

if (!isValidSignature) {
logger.warn(`[${requestId}] Attio signature verification failed`, {
signatureLength: signature.length,
secretLength: secret.length,
})
return new NextResponse('Unauthorized - Invalid Attio signature', { status: 401 })
}
}
}

if (foundWebhook.provider === 'linear') {
const secret = providerConfig.webhookSecret as string | undefined

Expand Down
Loading