--- name: apollo-reference-architecture description: 'Implement Apollo.io reference architecture. Use when designing Apollo integrations, establishing patterns, or building production-grade sales intelligence systems. Trigger with phrases like "apollo architecture", "apollo system design", "apollo integration patterns", "apollo best practices architecture". ' allowed-tools: Read, Write, Edit, Bash(gh:*), Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore tags: - saas - apollo - apollo-reference compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw --- # Apollo Reference Architecture ## Overview Production-ready reference architecture for Apollo.io integrations. Layered design with API client, service layer, background jobs, database models, CRM sync, and deals pipeline — all built around Apollo's REST API with correct endpoints and `x-api-key` authentication. ## Prerequisites - Apollo master API key - Node.js 18+ with TypeScript - PostgreSQL for data layer - Redis for job queues ## Instructions ### Step 1: Architecture Diagram ``` ┌───────────────────────────────────────────────┐ │ API Layer │ Express routes │ POST /api/leads/search GET /api/org/:d │ POST /api/deals ├───────────────────────────────────────────────┤ │ Service Layer │ Business logic │ LeadService EnrichService DealService │ SequenceService ├───────────────────────────────────────────────┤ │ Client Layer │ Apollo API wrapper │ ApolloClient RateLimiter Cache │ CreditTracker ├───────────────────────────────────────────────┤ │ Background Jobs │ BullMQ queues │ EnrichJob SyncJob StageChangeJob │ TaskCreatorJob ├───────────────────────────────────────────────┤ │ Data Layer │ Prisma/TypeORM │ Contact Organization Deal AuditLog │ └───────────────────────────────────────────────┘ ``` ### Step 2: Service Layer ```typescript // src/services/lead-service.ts import { getApolloClient } from '../apollo/client'; import { withRetry } from '../apollo/retry'; import { cachedRequest } from '../apollo/cache'; export class LeadService { private client = getApolloClient(); async searchPeople(params: { domains: string[]; titles?: string[]; seniorities?: string[]; page?: number }) { return cachedRequest('/mixed_people/api_search', () => withRetry(() => this.client.post('/mixed_people/api_search', { q_organization_domains_list: params.domains, person_titles: params.titles, person_seniorities: params.seniorities, page: params.page ?? 1, per_page: 100, })), params, ); } async enrichPerson(email: string) { return withRetry(() => this.client.post('/people/match', { email })); } async enrichOrg(domain: string) { return cachedRequest('/organizations/enrich', () => withRetry(() => this.client.get('/organizations/enrich', { params: { domain } })), { domain }, ); } } ``` ### Step 3: Deals/Opportunities Service Apollo has a full Deals API for tracking revenue pipeline. ```typescript // src/services/deal-service.ts export class DealService { private client = getApolloClient(); async createDeal(params: { name: string; amount: number; ownerId: string; // Apollo user ID accountId?: string; // Apollo account ID contactIds?: string[]; // Apollo contact IDs stageId?: string; // Deal stage ID }) { const { data } = await this.client.post('/opportunities', { name: params.name, amount: params.amount, owner_id: params.ownerId, account_id: params.accountId, contact_ids: params.contactIds, opportunity_stage_id: params.stageId, }); return { dealId: data.opportunity.id, name: data.opportunity.name }; } async listDeals(page: number = 1) { const { data } = await this.client.post('/opportunities/search', { page, per_page: 50 }); return data.opportunities.map((d: any) => ({ id: d.id, name: d.name, amount: d.amount, stage: d.opportunity_stage?.name, owner: d.owner?.name, })); } async getDealStages() { const { data } = await this.client.get('/opportunity_stages'); return data.opportunity_stages.map((s: any) => ({ id: s.id, name: s.name, order: s.display_order })); } async updateDeal(dealId: string, updates: { amount?: number; stageId?: string }) { await this.client.patch(`/opportunities/${dealId}`, { amount: updates.amount, opportunity_stage_id: updates.stageId, }); } } ``` ### Step 4: Background Job Processing ```typescript // src/jobs/enrichment-job.ts import { Queue, Worker, Job } from 'bullmq'; import { LeadService } from '../services/lead-service'; const connection = { host: process.env.REDIS_HOST ?? 'localhost', port: 6379 }; export const enrichmentQueue = new Queue('apollo-enrichment', { connection, defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 }, removeOnComplete: 1000, }, }); const leadService = new LeadService(); new Worker('apollo-enrichment', async (job: Job) => { switch (job.name) { case 'enrich-person': return leadService.enrichPerson(job.data.email); case 'enrich-org': return leadService.enrichOrg(job.data.domain); case 'bulk-search': { const results: any[] = []; for (const domain of job.data.domains) { const { data } = await leadService.searchPeople({ domains: [domain] }); results.push(...data.people); await job.updateProgress(results.length); } return { total: results.length }; } } }, { connection, concurrency: 3, limiter: { max: 50, duration: 60_000 } }); ``` ### Step 5: Database Model ```typescript // src/models/contact.ts (Prisma schema excerpt) // model Contact { // id String @id @default(cuid()) // apolloId String @unique // email String @unique // name String // title String? // seniority String? // phone String? // linkedinUrl String? // organizationId String? // rawApolloData Json? // enrichedAt DateTime? // createdAt DateTime @default(now()) // updatedAt DateTime @updatedAt // } // TypeORM version import { Entity, Column, PrimaryColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('contacts') export class Contact { @PrimaryColumn() apolloId: string; @Column({ unique: true }) email: string; @Column() name: string; @Column({ nullable: true }) title: string; @Column({ nullable: true }) seniority: string; @Column({ nullable: true }) phone: string; @Column({ nullable: true }) linkedinUrl: string; @Column({ type: 'jsonb', nullable: true }) rawApolloData: Record; @Column({ nullable: true }) enrichedAt: Date; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; } ``` ### Step 6: API Routes ```typescript // src/api/routes.ts import { Router } from 'express'; import { LeadService } from '../services/lead-service'; import { DealService } from '../services/deal-service'; const router = Router(); const leads = new LeadService(); const deals = new DealService(); router.post('/api/leads/search', async (req, res) => { const { data } = await leads.searchPeople(req.body); res.json({ leads: data.people, pagination: data.pagination }); }); router.post('/api/leads/enrich', async (req, res) => { const { data } = await leads.enrichPerson(req.body.email); res.json({ contact: data.person }); }); router.get('/api/organizations/:domain', async (req, res) => { const { data } = await leads.enrichOrg(req.params.domain); res.json({ organization: data.organization }); }); router.post('/api/deals', async (req, res) => { const result = await deals.createDeal(req.body); res.json(result); }); router.get('/api/deals', async (req, res) => { const list = await deals.listDeals(parseInt(req.query.page as string) || 1); res.json({ deals: list }); }); export { router }; ``` ## Output - Layered architecture: API, Service, Client, Jobs, Data - `LeadService` with cached search and retried enrichment - `DealService` with create, list, update, and stage management - BullMQ background jobs for async enrichment - Database model (Prisma + TypeORM) - Express API routes for search, enrichment, and deals ## Error Handling | Layer | Strategy | |-------|----------| | Client | Retry with backoff, circuit breaker for prolonged outages | | Service | Cache fallback on failure, credit budget enforcement | | Jobs | 3 retries with exponential backoff, dead letter after max | | API | Structured JSON error responses with error codes | ## Resources - [Apollo API Overview](https://docs.apollo.io/docs/api-overview) - [Create Deal](https://docs.apollo.io/reference/create-deal) - [List Deals](https://docs.apollo.io/reference/list-all-deals) - [Deal Stages](https://docs.apollo.io/reference/list-deal-stages) - [BullMQ Documentation](https://docs.bullmq.io/) ## Next Steps Proceed to `apollo-multi-env-setup` for environment configuration.