attio-reference-architecture

Production reference architecture for Attio CRM integrations -- layered project structure, sync patterns, webhook processing, and multi-environment setup. Trigger: "attio architecture", "attio best practices", "attio project structure", "how to organize attio", "attio integration design".

25 stars

Best use case

attio-reference-architecture is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Production reference architecture for Attio CRM integrations -- layered project structure, sync patterns, webhook processing, and multi-environment setup. Trigger: "attio architecture", "attio best practices", "attio project structure", "how to organize attio", "attio integration design".

Teams using attio-reference-architecture should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/attio-reference-architecture/SKILL.md --create-dirs "https://raw.githubusercontent.com/ComeOnOliver/skillshub/main/skills/jeremylongshore/claude-code-plugins-plus-skills/attio-reference-architecture/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/attio-reference-architecture/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How attio-reference-architecture Compares

Feature / Agentattio-reference-architectureStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Production reference architecture for Attio CRM integrations -- layered project structure, sync patterns, webhook processing, and multi-environment setup. Trigger: "attio architecture", "attio best practices", "attio project structure", "how to organize attio", "attio integration design".

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Attio Reference Architecture

## Overview

Production architecture for applications that integrate with the Attio REST API (`https://api.attio.com/v2`). Covers project layout, layered service design, sync patterns, and operational concerns.

## Project Structure

```
my-attio-integration/
├── src/
│   ├── attio/                        # Attio API layer (isolated)
│   │   ├── client.ts                 # Typed fetch wrapper with retry
│   │   ├── types.ts                  # Attio API types (AttioRecord, AttioError, etc.)
│   │   ├── config.ts                 # Environment-based config loader
│   │   └── errors.ts                 # AttioApiError class
│   ├── services/                     # Business logic (uses attio/ layer)
│   │   ├── contacts.ts              # People/company sync logic
│   │   ├── pipeline.ts              # Deal pipeline management
│   │   ├── activity.ts              # Notes, tasks, comments
│   │   └── sync.ts                  # Bi-directional sync orchestrator
│   ├── webhooks/                     # Incoming webhook handlers
│   │   ├── router.ts                # Event type routing
│   │   ├── verify.ts                # Signature verification
│   │   └── handlers/
│   │       ├── record-events.ts     # record.created/updated/deleted/merged
│   │       ├── entry-events.ts      # list-entry.created/updated/deleted
│   │       └── activity-events.ts   # note/task/comment events
│   ├── api/                         # Outbound API routes
│   │   ├── health.ts                # Health check (includes Attio)
│   │   └── webhooks.ts              # Webhook receiver endpoint
│   ├── cache/                       # Caching layer
│   │   ├── schema-cache.ts          # Object/attribute definitions (30min TTL)
│   │   └── record-cache.ts          # Record data (5min TTL, webhook invalidation)
│   └── index.ts                     # App entrypoint
├── tests/
│   ├── mocks/                       # MSW handlers for Attio API
│   ├── unit/                        # Service logic tests (mocked API)
│   └── integration/                 # Live API tests (CI-gated)
├── config/
│   ├── attio.development.json
│   ├── attio.staging.json
│   └── attio.production.json
├── .env.example
└── .github/workflows/attio.yml
```

## Layered Architecture

```
┌──────────────────────────────────────────────────┐
│  API Layer (routes, webhook endpoint)            │
│  - Receives HTTP requests                        │
│  - Validates webhook signatures                  │
│  - Returns health status                         │
├──────────────────────────────────────────────────┤
│  Service Layer (business logic)                  │
│  - Contact sync, pipeline management             │
│  - Bi-directional data mapping                   │
│  - Event-driven automations                      │
├──────────────────────────────────────────────────┤
│  Attio Layer (API client, types, errors)         │
│  - Typed fetch wrapper with retry                │
│  - Error normalization (AttioApiError)            │
│  - Pagination helpers                            │
├──────────────────────────────────────────────────┤
│  Infrastructure Layer (cache, queue, monitoring)  │
│  - LRU + Redis caching with webhook invalidation │
│  - Rate limit queue (p-queue)                    │
│  - Structured logging and metrics                │
└──────────────────────────────────────────────────┘
```

**Rule:** Each layer only calls the layer directly below it. The API layer never calls the Attio client directly.

## Core Components

### Component 1: Service Layer Facade

```typescript
// src/services/contacts.ts
import { AttioClient } from "../attio/client";
import { cachedGet, invalidateRecord } from "../cache/record-cache";
import type { AttioRecord } from "../attio/types";

export class ContactService {
  constructor(private client: AttioClient) {}

  async findByEmail(email: string): Promise<AttioRecord | null> {
    const res = await this.client.post<{ data: AttioRecord[] }>(
      "/objects/people/records/query",
      {
        filter: { email_addresses: email },
        limit: 1,
      }
    );
    return res.data[0] || null;
  }

  async upsertPerson(data: {
    email: string;
    firstName: string;
    lastName: string;
    company?: string;
  }): Promise<AttioRecord> {
    // Use PUT (assert) for idempotent upsert
    const res = await this.client.put<{ data: AttioRecord }>(
      "/objects/people/records",
      {
        data: {
          values: {
            email_addresses: [data.email],
            name: [{
              first_name: data.firstName,
              last_name: data.lastName,
              full_name: `${data.firstName} ${data.lastName}`,
            }],
            ...(data.company ? { company: [{ target_object: "companies", target_record_id: data.company }] } : {}),
          },
        },
      }
    );
    return res.data;
  }

  async addToPipeline(
    recordId: string,
    listSlug: string,
    stage: string,
    value?: { currency: string; amount: number }
  ): Promise<void> {
    await this.client.post(`/lists/${listSlug}/entries`, {
      data: {
        parent_record_id: recordId,
        parent_object: "people",
        values: {
          stage: [{ status: stage }],
          ...(value ? {
            deal_value: [{ currency_code: value.currency, currency_value: value.amount }],
          } : {}),
        },
      },
    });
  }

  async addNote(recordId: string, title: string, content: string): Promise<void> {
    await this.client.post("/notes", {
      data: {
        parent_object: "people",
        parent_record_id: recordId,
        title,
        format: "markdown",
        content,
      },
    });
  }
}
```

### Component 2: Webhook Event Router

```typescript
// src/webhooks/router.ts
import type { AttioWebhookEvent } from "../attio/types";

type EventHandler = (event: AttioWebhookEvent) => Promise<void>;

export class WebhookRouter {
  private handlers = new Map<string, EventHandler[]>();

  on(eventType: string, handler: EventHandler): void {
    const existing = this.handlers.get(eventType) || [];
    this.handlers.set(eventType, [...existing, handler]);
  }

  async route(event: AttioWebhookEvent): Promise<void> {
    const handlers = this.handlers.get(event.event_type) || [];
    if (handlers.length === 0) {
      console.log(`No handler for event: ${event.event_type}`);
      return;
    }
    await Promise.allSettled(handlers.map((h) => h(event)));
  }
}

// Usage
const router = new WebhookRouter();
router.on("record.created", async (event) => {
  if (event.object?.api_slug === "people") {
    await syncNewContactToExternalCRM(event.record!.id.record_id);
  }
});
router.on("record.updated", async (event) => {
  invalidateRecord(event.record!.id.record_id);
});
router.on("list-entry.created", async (event) => {
  await triggerPipelineAutomation(event);
});
```

### Component 3: Bi-Directional Sync

```typescript
// src/services/sync.ts
export class AttioSyncService {
  private lastSyncCursor: string | null = null;

  /** Outbound: push local changes to Attio */
  async pushToAttio(localContact: LocalContact): Promise<string> {
    const attioRecord = await this.contacts.upsertPerson({
      email: localContact.email,
      firstName: localContact.firstName,
      lastName: localContact.lastName,
    });
    return attioRecord.id.record_id;
  }

  /** Inbound: pull Attio changes to local (webhook-driven) */
  async handleAttioChange(event: AttioWebhookEvent): Promise<void> {
    if (event.event_type === "record.updated") {
      const record = await this.client.get<{ data: AttioRecord }>(
        `/objects/${event.object!.api_slug}/records/${event.record!.id.record_id}`
      );
      await this.updateLocalFromAttio(record.data);
    }
  }

  /** Full sync: reconcile all records (run periodically or on demand) */
  async fullSync(objectSlug: string): Promise<{ created: number; updated: number }> {
    let created = 0, updated = 0;
    const PAGE_SIZE = 500;
    let offset = 0;

    while (true) {
      const page = await this.client.post<{ data: AttioRecord[] }>(
        `/objects/${objectSlug}/records/query`,
        { limit: PAGE_SIZE, offset }
      );

      for (const record of page.data) {
        const existed = await this.upsertLocal(record);
        existed ? updated++ : created++;
      }

      if (page.data.length < PAGE_SIZE) break;
      offset += PAGE_SIZE;
    }

    return { created, updated };
  }
}
```

### Component 4: Multi-Environment Config

```typescript
// src/attio/config.ts
interface AttioEnvironmentConfig {
  apiKey: string;
  webhookSecret: string;
  baseUrl: string;
  cache: { schemaTtlMs: number; recordTtlMs: number };
  rateLimit: { concurrency: number; intervalCap: number };
}

const configs: Record<string, Partial<AttioEnvironmentConfig>> = {
  development: {
    cache: { schemaTtlMs: 60_000, recordTtlMs: 10_000 },
    rateLimit: { concurrency: 2, intervalCap: 5 },
  },
  staging: {
    cache: { schemaTtlMs: 300_000, recordTtlMs: 60_000 },
    rateLimit: { concurrency: 5, intervalCap: 8 },
  },
  production: {
    cache: { schemaTtlMs: 1_800_000, recordTtlMs: 300_000 },
    rateLimit: { concurrency: 10, intervalCap: 15 },
  },
};

export function loadConfig(): AttioEnvironmentConfig {
  const env = process.env.NODE_ENV || "development";
  const envConfig = configs[env] || configs.development;
  return {
    apiKey: requireEnv("ATTIO_API_KEY"),
    webhookSecret: process.env.ATTIO_WEBHOOK_SECRET || "",
    baseUrl: "https://api.attio.com/v2",
    cache: envConfig.cache!,
    rateLimit: envConfig.rateLimit!,
  };
}

function requireEnv(key: string): string {
  const val = process.env[key];
  if (!val) throw new Error(`Missing required env: ${key}`);
  return val;
}
```

## Data Flow Diagram

```
External System                    Your Application                      Attio CRM
     │                                   │                                  │
     │  Local change ──────────────────▶ │                                  │
     │                                   │  PUT /objects/people/records ──▶ │
     │                                   │  ◀── 200 { data: record }       │
     │                                   │                                  │
     │                                   │       Webhook: record.updated    │
     │                                   │  ◀──────────────────────────── │
     │  ◀── Sync update ──────────────  │                                  │
     │                                   │  GET /objects/.../records/... ─▶ │
     │                                   │  ◀── 200 { data: record }       │
```

## Error Handling

| Architecture issue | Symptom | Fix |
|-------------------|---------|-----|
| Service calls client directly | Tight coupling, hard to test | Add service layer facade |
| No cache invalidation | Stale data after updates | Webhook-driven cache invalidation |
| Sync conflicts | Both sides updated same record | Last-write-wins or conflict resolution queue |
| No circuit breaker | Attio outage cascades | Add circuit breaker in Attio layer |

## Resources

- [Attio REST API Overview](https://docs.attio.com/rest-api/overview)
- [Attio Objects and Lists](https://docs.attio.com/docs/objects-and-lists)
- [Attio Webhooks Guide](https://docs.attio.com/rest-api/guides/webhooks)
- [Attio Developer Platform](https://attio.com/platform/developers)

## Next Steps

This is the capstone skill. For specific implementations, refer to the individual skills in this pack.

Related Skills

exa-reference-architecture

25
from ComeOnOliver/skillshub

Implement Exa reference architecture for search pipelines, RAG, and content discovery. Use when designing new Exa integrations, reviewing project structure, or establishing architecture standards for neural search applications. Trigger with phrases like "exa architecture", "exa project structure", "exa RAG pipeline", "exa reference design", "exa search pipeline".

exa-architecture-variants

25
from ComeOnOliver/skillshub

Choose and implement Exa architecture patterns at different scales: direct search, cached search, and RAG pipeline. Use when designing Exa integrations, choosing between simple search and full RAG, or planning architecture for different traffic volumes. Trigger with phrases like "exa architecture", "exa blueprint", "how to structure exa", "exa RAG design", "exa at scale".

evernote-reference-architecture

25
from ComeOnOliver/skillshub

Reference architecture for Evernote integrations. Use when designing system architecture, planning integrations, or building scalable Evernote applications. Trigger with phrases like "evernote architecture", "design evernote system", "evernote integration pattern", "evernote scale".

elevenlabs-reference-architecture

25
from ComeOnOliver/skillshub

Implement ElevenLabs reference architecture for production TTS/voice applications. Use when designing new ElevenLabs integrations, reviewing project structure, or building a scalable audio generation service. Trigger: "elevenlabs architecture", "elevenlabs project structure", "how to organize elevenlabs", "TTS service architecture", "elevenlabs design patterns", "voice API architecture".

documenso-reference-architecture

25
from ComeOnOliver/skillshub

Implement Documenso reference architecture with best-practice project layout. Use when designing new Documenso integrations, reviewing project structure, or establishing architecture standards for document signing applications. Trigger with phrases like "documenso architecture", "documenso best practices", "documenso project structure", "how to organize documenso".

deepgram-reference-architecture

25
from ComeOnOliver/skillshub

Implement Deepgram reference architecture for scalable transcription systems. Use when designing transcription pipelines, building production architectures, or planning Deepgram integration at scale. Trigger: "deepgram architecture", "transcription pipeline", "deepgram system design", "deepgram at scale", "enterprise deepgram", "deepgram queue".

databricks-reference-architecture

25
from ComeOnOliver/skillshub

Implement Databricks reference architecture with best-practice project layout. Use when designing new Databricks projects, reviewing architecture, or establishing standards for Databricks applications. Trigger with phrases like "databricks architecture", "databricks best practices", "databricks project structure", "how to organize databricks", "databricks layout".

customerio-reference-architecture

25
from ComeOnOliver/skillshub

Implement Customer.io enterprise reference architecture. Use when designing integration layers, event-driven architectures, or enterprise-grade Customer.io setups. Trigger: "customer.io architecture", "customer.io design", "customer.io enterprise", "customer.io integration pattern".

cursor-reference-architecture

25
from ComeOnOliver/skillshub

Reference architecture for Cursor IDE projects: directory structure, rules organization, indexing strategy, and team configuration patterns. Triggers on "cursor architecture", "cursor project structure", "cursor best practices", "cursor file structure".

coreweave-reference-architecture

25
from ComeOnOliver/skillshub

Reference architecture for CoreWeave GPU cloud deployments. Use when designing ML infrastructure, planning multi-model serving, or establishing CoreWeave deployment standards. Trigger with phrases like "coreweave architecture", "coreweave design", "coreweave infrastructure", "coreweave best practices".

configuration-reference-generator

25
from ComeOnOliver/skillshub

Configuration Reference Generator - Auto-activating skill for Technical Documentation. Triggers on: configuration reference generator, configuration reference generator Part of the Technical Documentation skill category.

cohere-reference-architecture

25
from ComeOnOliver/skillshub

Implement Cohere reference architecture with layered project layout for RAG and agents. Use when designing new Cohere integrations, reviewing project structure, or establishing architecture standards for Cohere API v2 applications. Trigger with phrases like "cohere architecture", "cohere project structure", "cohere layout", "organize cohere app", "cohere design pattern".