apollo-webhooks-events
Implement Apollo.io webhook handling. Use when receiving Apollo webhooks, processing event notifications, or building event-driven integrations. Trigger with phrases like "apollo webhooks", "apollo events", "apollo notifications", "apollo webhook handler", "apollo triggers".
Install
mkdir -p .claude/skills/apollo-webhooks-events && curl -L -o skill.zip "https://mcp.directory/api/skills/download/9371" && unzip -o skill.zip -d .claude/skills/apollo-webhooks-events && rm skill.zipInstalls to .claude/skills/apollo-webhooks-events
About this skill
Apollo Webhooks & Events
Overview
Build event-driven integrations with Apollo.io. Apollo does not have a native webhook system like Stripe — instead, you build real-time sync by polling the API for changes or using third-party webhook platforms (Zapier, Pipedream, Make) that trigger on Apollo events. This skill covers both approaches plus the Contact Stages and Tasks APIs for workflow automation.
Prerequisites
- Apollo account with master API key
- Node.js 18+ with Express
- For polling: a scheduler (cron, Cloud Scheduler, BullMQ)
- For webhook platforms: Zapier/Pipedream/Make account
Instructions
Step 1: Poll for Contact Changes
Since Apollo lacks native webhooks, poll the Contacts Search API for recently updated records.
// src/sync/contact-poller.ts
import axios from 'axios';
const client = axios.create({
baseURL: 'https://api.apollo.io/api/v1',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.APOLLO_API_KEY! },
});
interface SyncState {
lastSyncAt: string; // ISO timestamp
}
export async function pollContactChanges(state: SyncState) {
const { data } = await client.post('/contacts/search', {
sort_by_field: 'contact_updated_at',
sort_ascending: false,
per_page: 100,
});
const lastSync = new Date(state.lastSyncAt);
const newChanges = data.contacts.filter(
(c: any) => new Date(c.updated_at) > lastSync,
);
if (newChanges.length > 0) {
console.log(`Found ${newChanges.length} contact changes since ${state.lastSyncAt}`);
for (const contact of newChanges) {
await handleContactChange(contact);
}
state.lastSyncAt = new Date().toISOString();
}
return newChanges;
}
async function handleContactChange(contact: any) {
console.log(`Contact updated: ${contact.name} (${contact.email}) — stage: ${contact.contact_stage_id}`);
// Sync to your CRM, database, or notification system
}
Step 2: Track Contact Stage Changes
Apollo has a Contact Stages system. Use the List Contact Stages endpoint to get stage IDs, then filter contacts by stage.
// src/sync/stage-tracker.ts
export async function getContactStages() {
const { data } = await client.get('/contact_stages');
return data.contact_stages.map((s: any) => ({
id: s.id,
name: s.name,
order: s.display_order,
}));
}
// Find contacts that moved to a specific stage
export async function getContactsByStage(stageId: string) {
const { data } = await client.post('/contacts/search', {
contact_stage_ids: [stageId],
sort_by_field: 'contact_updated_at',
sort_ascending: false,
per_page: 50,
});
return data.contacts;
}
// Update a contact's stage
export async function updateContactStage(contactId: string, stageId: string) {
await client.put(`/contacts/${contactId}`, {
contact_stage_id: stageId,
});
}
Step 3: Monitor Sequence Engagement
Poll sequence data for replies, bounces, and engagement events.
// src/sync/sequence-monitor.ts
export async function checkSequenceEngagement(sequenceId: string) {
const { data } = await client.post('/emailer_campaigns/search', {
ids: [sequenceId],
per_page: 1,
});
const seq = data.emailer_campaigns?.[0];
if (!seq) return null;
return {
name: seq.name,
active: seq.active,
metrics: {
scheduled: seq.unique_scheduled ?? 0,
delivered: seq.unique_delivered ?? 0,
opened: seq.unique_opened ?? 0,
replied: seq.unique_replied ?? 0,
bounced: seq.unique_bounced ?? 0,
unsubscribed: seq.unique_unsubscribed ?? 0,
},
rates: {
openRate: pct(seq.unique_opened, seq.unique_delivered),
replyRate: pct(seq.unique_replied, seq.unique_delivered),
bounceRate: pct(seq.unique_bounced, seq.unique_scheduled),
},
};
}
function pct(num?: number, denom?: number): string {
if (!denom) return '0%';
return `${(((num ?? 0) / denom) * 100).toFixed(1)}%`;
}
Step 4: Create Tasks for Follow-Up Actions
Use Apollo's Tasks API to create actionable follow-ups triggered by your polling logic.
// src/sync/task-creator.ts
export async function createFollowUpTask(params: {
contactId: string;
type: 'call' | 'action_item' | 'email';
dueDate: string;
note: string;
assigneeId?: string;
}) {
const { data } = await client.post('/tasks', {
contact_id: params.contactId,
type: params.type,
due_date: params.dueDate,
note: params.note,
user_id: params.assigneeId, // Apollo user ID to assign to
priority: 'high',
status: 'open',
});
return { taskId: data.task.id, type: data.task.type, dueDate: data.task.due_date };
}
// Example: auto-create call task when a sequence reply is detected
async function onSequenceReply(contactId: string, sequenceName: string) {
await createFollowUpTask({
contactId,
type: 'call',
dueDate: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().split('T')[0],
note: `Reply detected on sequence "${sequenceName}". Follow up immediately.`,
});
}
Step 5: Set Up a Cron-Based Sync Service
// src/sync/scheduler.ts
import cron from 'node-cron';
import { pollContactChanges } from './contact-poller';
import { checkSequenceEngagement } from './sequence-monitor';
const syncState = { lastSyncAt: new Date(Date.now() - 60 * 60 * 1000).toISOString() };
// Poll every 5 minutes for contact changes
cron.schedule('*/5 * * * *', async () => {
try {
const changes = await pollContactChanges(syncState);
console.log(`Sync complete: ${changes.length} changes`);
} catch (err) {
console.error('Contact sync failed:', err);
}
});
// Check sequence engagement every 15 minutes
cron.schedule('*/15 * * * *', async () => {
const activeSequenceIds = ['seq-1', 'seq-2']; // from your config
for (const id of activeSequenceIds) {
const stats = await checkSequenceEngagement(id);
if (stats && parseInt(stats.rates.replyRate) > 10) {
console.log(`High reply rate on "${stats.name}": ${stats.rates.replyRate}`);
}
}
});
console.log('Apollo sync scheduler started');
Output
- Contact change poller with timestamp-based incremental sync
- Contact stage tracking and updates via the Stages API
- Sequence engagement monitoring with reply/bounce/open rates
- Task creation API for automated follow-ups
- Cron-based scheduler for periodic sync
Error Handling
| Issue | Resolution |
|---|---|
| Missed changes between polls | Reduce poll interval or use sort_by_field: contact_updated_at |
| Rate limited during polling | Add backoff, reduce per_page, increase poll interval |
| Duplicate task creation | Track processed contact IDs in a Set or database |
| Third-party webhook delays | Zapier/Pipedream polling intervals vary (1-15 min on free tiers) |
Resources
- List Contact Stages
- Search for Contacts
- Create a Task
- Search for Sequences
- Apollo + Zapier Integration
Next Steps
Proceed to apollo-performance-tuning for optimization.
More by jeremylongshore
View all skills by jeremylongshore →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversPowerful MCP server for Slack with advanced API, message fetching, webhooks, and enterprise features. Robust Slack data
Telnyx MCP Server: manage phone numbers, calls, SMS and AI assistants with Telnyx API. Real-time webhooks, voice & messa
Nekzus Utility Server offers modular TypeScript tools for datetime, cards, and schema conversion with stdio transport co
Advanced Discord bot for moderation, automation, and server management with bulk tools, privacy, and webhook support.
Integrate with GitLab for seamless repository management, file operations, CI/CD, issue tracking, and merge requests via
Break down complex problems with Sequential Thinking, a structured tool and step by step math solver for dynamic, reflec
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.