Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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".
.claude/skills/jeremylongshore-apollo-reference-architecture/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 39% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 165% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 41% | 0% |
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.
┌───────────────────────────────────────────────┐
│ 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 │
└───────────────────────────────────────────────┘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 }, ); } }
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, }); } }
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 } });
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<string, any>; @Column({ nullable: true }) enrichedAt: Date; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }
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 };
LeadService with cached search and retried enrichmentDealService with create, list, update, and stage management| 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 |
Proceed to apollo-multi-env-setup for environment configuration.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | fail→fail | 14,024 | 7,796 | -44% | 1 | 1 | 0% | 2,627 | 3,866 | +47% | 0 | 0 | — |
case-01 | fail→fail | 23,283 | 24,077 | +3% | 1 | 1 | 0% | 5,562 | 8,228 | +48% | 0 | 0 | — |
case-12 | fail→fail | 18,849 | 11,694 | -38% | 1 | 1 | 0% | 3,332 | 4,449 | +34% | 0 | 0 | — |
case-02 | fail→pass | 25,734 | 24,732 | -4% | 1 | 1 | 0% | 6,199 | 8,589 | +39% | 0 | 0 | — |
case-03 | fail→fail | 26,781 | 26,058 | -3% | 1 | 1 | 0% | 6,201 | 8,479 | +37% | 0 | 0 | — |
case-04 | fail→fail | 12,821 | 11,206 | -13% | 1 | 1 | 0% | 2,567 | 4,458 | +74% | 0 | 0 | — |
case-05 | fail→fail | 7,107 | 3,258 | -54% | 1 | 1 | 0% | 1,392 | 2,931 | +111% | 0 | 0 | — |
case-13 | fail→fail | 14,586 | 8,308 | -43% | 1 | 1 | 0% | 2,455 | 3,849 | +57% | 0 | 0 | — |
case-06 | pass→pass | 9,301 | 5,992 | -36% | 1 | 1 | 0% | 1,921 | 3,687 | +92% | 0 | 0 | — |
case-07 | pass→pass | 7,357 | 5,586 | -24% | 1 | 1 | 0% | 1,430 | 3,424 | +139% | 0 | 0 | — |
case-08 | fail→pass | 8,506 | 7,854 | -8% | 1 | 1 | 0% | 1,739 | 3,505 | +102% | 0 | 0 | — |
case-09 | fail→pass | 7,156 | 5,443 | -24% | 1 | 1 | 0% | 1,340 | 3,291 | +146% | 0 | 0 | — |
case-10 | fail→pass | 7,445 | 6,319 | -15% | 1 | 1 | 0% | 1,332 | 3,533 | +165% | 0 | 0 | — |
case-14 | pass→pass | 14,724 | 13,493 | -8% | 1 | 1 | 0% | 2,720 | 4,814 | +77% | 0 | 0 | — |
case-15 | fail→pass | 16,555 | 7,237 | -56% | 1 | 1 | 0% | 2,757 | 3,877 | +41% | 0 | 0 | — |
case-16 | fail→pass | 9,537 | 2,219 | -77% | 1 | 1 | 0% | 1,632 | 2,707 | +66% | 0 | 0 | — |
case-17 | fail→pass | 15,148 | 10,253 | -32% | 1 | 1 | 0% | 2,766 | 4,566 | +65% | 0 | 0 | — |
case-18 | pass→pass | 12,080 | 10,651 | -12% | 1 | 1 | 0% | 2,367 | 4,665 | +97% | 0 | 0 | — |
case-19 | pass→pass | 20,780 | 18,451 | -11% | 1 | 1 | 0% | 3,692 | 5,962 | +61% | 0 | 0 | — |
case-20 | fail→fail | 21,936 | 18,995 | -13% | 1 | 1 | 0% | 3,733 | 6,219 | +67% | 0 | 0 | — |
case-21 | pass→pass | 15,389 | 16,475 | +7% | 1 | 1 | 0% | 3,173 | 5,733 | +81% | 0 | 0 | — |
case-22 | pass→pass | 12,169 | 15,441 | +27% | 1 | 1 | 0% | 2,407 | 5,637 | +134% | 0 | 0 | — |
case-23 | pass→pass | 13,046 | 11,405 | -13% | 1 | 1 | 0% | 2,541 | 4,589 | +81% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +30 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.