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.updated events?
  • 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

EventDescription
record.createdA record (person, company, deal, custom object) was created
record.updatedAn attribute on a record changed (id.attribute_id says which)
record.deletedA record was deleted
record.mergedTwo records were merged (fires once per original)
list-entry.createdA record was added to a list
list-entry.updatedA list attribute on an entry changed
list-entry.deletedA record was removed from a list
note.createdA note was created
task.createdA task was created
comment.createdA comment was created

For the full event list, see references/overview.md and Attio's webhook reference.

Important Headers

HeaderDescription
Attio-SignatureHex HMAC-SHA256 of the raw body, keyed with the webhook secret
X-Attio-SignatureSame value, for legacy middleware
Idempotency-KeyUnique per message, stable across retries; use it to deduplicate

Delivery Behavior

  • Respond with any 2xx within 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →