Agent skill
Attio Webhooks Skill
Receive and verify Attio CRM webhooks. Use when setting up Attio webhook handlers, debugging Attio-Signature verification, deduplicating with the Idempotency-Key header, or handling Attio events like record.created, record.updated, list-entry.created, note.created, and task.created.
Install this skill
npx skills add hookdeck/webhook-skills --skill attio-webhooks
When to Use This Skill
- How do I receive Attio webhooks?
- How do I verify the Attio-Signature header?
- How do I handle
record.created/record.updated/list-entry.updatedevents? - Why is my Attio webhook signature verification failing?
- How do I deduplicate Attio webhook retries?
Verification (core)
Attio signs every webhook with the webhook's secret (shown in developer settings and returned once when the webhook is created via the API). The Attio-Signature header (duplicated as X-Attio-Signature for legacy middleware) is the hex-encoded HMAC-SHA256 of the raw request body. There is no timestamp or prefix in the signed content. Use the raw body and compare timing-safe.
Node:
const crypto = require('crypto');
function verifyAttioSignature(rawBody, signature, secret) {
if (!signature || !secret || !/^[0-9a-f]{64}$/i.test(signature)) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase(), 'hex'), Buffer.from(expected, 'hex'));
}
// signature = req.headers['attio-signature'] ?? req.headers['x-attio-signature']
Python:
import hmac, hashlib
def verify_attio_signature(raw_body: bytes, signature: str | None, secret: str | None) -> bool:
if not signature or not secret:
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
# Compare bytes: compare_digest raises TypeError on non-ASCII str input
return hmac.compare_digest(signature.lower().encode(), expected.encode())
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Payload Shape
Each delivery is a JSON envelope with a webhook_id and an events array. Today each delivery contains exactly one event, but Attio documents that batching may come later, so always iterate events:
{
"webhook_id": "23e42eaf-323a-41da-b5bb-fd67eebda553",
"events": [
{
"event_type": "record.created",
"id": { "workspace_id": "…", "object_id": "…", "record_id": "…" },
"actor": { "type": "workspace-member", "id": "…" }
}
]
}
Payloads carry IDs only. Fetch the full record, entry, note or task from the Attio REST API if you need its data.
Common Event Types
| Event | Description |
|---|---|
record.created | A record (person, company, deal, custom object) was created |
record.updated | An attribute on a record changed (id.attribute_id says which) |
record.deleted | A record was deleted |
record.merged | Two records were merged (fires once per original) |
list-entry.created | A record was added to a list |
list-entry.updated | A list attribute on an entry changed |
list-entry.deleted | A record was removed from a list |
note.created | A note was created |
task.created | A task was created |
comment.created | A comment was created |
For the full event list, see references/overview.md and Attio's webhook reference.
Important Headers
| Header | Description |
|---|---|
Attio-Signature | Hex HMAC-SHA256 of the raw body, keyed with the webhook secret |
X-Attio-Signature | Same value, for legacy middleware |
Idempotency-Key | Unique per message, stable across retries; use it to deduplicate |
Delivery Behavior
- Respond with any
2xxwithin 5 seconds or the delivery counts as failed. - Failed deliveries are retried up to 10 times with exponential back-off over about 3 days, after which the webhook is marked
degraded. - Delivery is at-least-once; deduplicate on
Idempotency-Key. - Target URLs must be HTTPS.
Environment Variables
ATTIO_WEBHOOK_SECRET=your_webhook_secret # From developer settings or the create-webhook API response
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 attio --path /webhooks/attio
Attio requires an HTTPS target URL, so use the Hookdeck URL the CLI prints when you create the webhook, then use Send test event to target URL in developer settings.
Reference Materials
- references/overview.md - Attio webhook concepts, full event list, payload fields
- references/setup.md - Create webhooks in developer settings or via API, get the secret
- references/verification.md - Signature verification details and gotchas