| name | apollo-webhooks-events |
| description | 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".
|
| allowed-tools | Read, Write, Edit, Bash(gh:*), Bash(curl:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Apollo Webhooks Events
Overview
Implement webhook handlers for Apollo.io to receive real-time notifications about contact updates, sequence events, and engagement activities.
Apollo Webhook Events
| Event Type | Description | Payload Contains |
|---|
contact.created | New contact added | Contact data |
contact.updated | Contact info changed | Updated fields |
sequence.started | Contact added to sequence | Sequence & contact IDs |
sequence.completed | Sequence finished | Completion status |
email.sent | Email delivered | Email & contact info |
email.opened | Email was opened | Open timestamp |
email.clicked | Link clicked | Click details |
email.replied | Reply received | Reply content |
email.bounced | Email bounced | Bounce reason |
Webhook Handler Implementation
Express Handler
import { Router } from 'express';
import crypto from 'crypto';
import { z } from 'zod';
const router = Router();
const ContactEventSchema = z.object({
event: z.enum(['contact.created', 'contact.updated']),
timestamp: z.string(),
data: z.object({
contact: z.object({
id: z.string(),
email: z.string().optional(),
name: z.string().optional(),
title: z.string().optional(),
organization: z.object({
name: z.string(),
}).optional(),
}),
changes: z.record(z.any()).optional(),
}),
});
const SequenceEventSchema = z.object({
: z.([, , ]),
: z.(),
: z.({
: z.(),
: z.(),
: z.().(),
}),
});
= z.({
: z.([, , , , ]),
: z.(),
: z.({
: z.(),
: z.(),
: z.().(),
: z.().(),
: z.().(),
: z.().(),
}),
});
(): {
expectedSignature = crypto
.(, secret)
.(payload)
.();
crypto.(
.(signature),
.(expectedSignature)
);
}
() {
signature = req.[];
webhookSecret = process..;
(!webhookSecret) {
.();
res.().({ : });
}
(!signature) {
res.().({ : });
}
rawBody = .(req.);
(!(rawBody, signature, webhookSecret)) {
res.().({ : });
}
();
}
router.(, verifyApolloWebhook, (req, res) => {
{ event } = req.;
{
(event.()) {
(.(req.));
} (event.()) {
(.(req.));
} (event.()) {
(.(req.));
} {
.(, event);
}
res.().({ : });
} (: ) {
.(, error);
res.().({ : error. });
}
});
router;
Event Handlers
import { prisma } from '../db';
import { publishEvent } from '../events';
export async function handleContactEvent(payload: any) {
const { event, data } = payload;
switch (event) {
case 'contact.created':
await prisma.contact.upsert({
where: { apolloId: data.contact.id },
create: {
apolloId: data.contact.id,
email: data.contact.email,
name: data.contact.name,
title: data.contact.title,
company: data.contact.organization?.name,
syncedAt: new Date(),
},
update: {
email: data.contact.email,
name: data..,
: data..,
: data..?.,
: (),
},
});
(, {
: data..,
: ,
});
;
:
prisma..({
: { : data.. },
: {
...data.,
: (),
},
});
(, {
: data..,
: ,
: data.,
});
;
}
}
() {
{ event, data } = payload;
(event) {
:
prisma..({
: {
: data.,
: data.,
: ,
: (),
},
});
;
:
prisma..({
: {
: {
: data.,
: data.,
},
},
: {
: data. || ,
: (),
},
});
;
}
}
() {
{ event, data, timestamp } = payload;
prisma..({
: {
: data.,
: data.,
: data.,
: event.(, ),
: {
: data.,
: data.,
: data.,
},
: (timestamp),
},
});
(event === ) {
(, {
: data.,
: ,
});
} (event === ) {
prisma..({
: { : data. },
: { : },
});
}
}
Webhook Registration
import { apollo } from '../src/lib/apollo/client';
interface WebhookConfig {
url: string;
events: string[];
secret: string;
}
async function registerWebhook(config: WebhookConfig) {
console.log('Webhook registration:', config);
console.log(`
To register webhooks in Apollo:
1. Go to Apollo Settings > Integrations > Webhooks
2. Click "Add Webhook"
3. Enter URL: ${config.url}
4. Select events: ${config.events.join(', ')}
5. Copy the webhook secret and add to your environment:
APOLLO_WEBHOOK_SECRET=<secret>
`);
}
const webhookConfig: WebhookConfig = {
url: `${process.env.APP_URL}/webhooks/apollo`,
events: [
'contact.created',
'contact.updated',
'sequence.started',
'sequence.completed',
'email.sent',
'email.opened',
'email.clicked',
'email.replied',
,
],
: process..!,
};
(webhookConfig);
Testing Webhooks
import { describe, it, expect } from 'vitest';
import request from 'supertest';
import crypto from 'crypto';
import app from '../../src/app';
function signPayload(payload: any, secret: string): string {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
}
describe('Apollo Webhooks', () => {
const secret = 'test-webhook-secret';
beforeAll(() => {
process.env.APOLLO_WEBHOOK_SECRET = secret;
});
it('rejects requests without signature', async () => {
const response = await request(app)
.post('/webhooks/apollo')
.send({ event: 'contact.created' });
expect(response.).();
});
(, () => {
response = (app)
.()
.(, )
.({ : });
(response.).();
});
(, () => {
payload = {
: ,
: ().(),
: {
: {
: ,
: ,
: ,
},
},
};
signature = (payload, secret);
response = (app)
.()
.(, signature)
.(payload);
(response.).();
(response..).();
});
(, () => {
payload = {
: ,
: ().(),
: {
: ,
: ,
: ,
},
};
signature = (payload, secret);
response = (app)
.()
.(, signature)
.(payload);
(response.).();
});
});
Local Testing with ngrok
npm run dev
ngrok http 3000
Output
- Webhook endpoint with signature verification
- Event handlers for all Apollo event types
- Database sync for contact and engagement data
- Webhook registration instructions
- Test suite for webhook validation
Error Handling
| Issue | Resolution |
|---|
| Invalid signature | Check webhook secret |
| Unknown event | Log and acknowledge (200) |
| Processing error | Log error, return 500 |
| Duplicate events | Implement idempotency |
Resources
Next Steps
Proceed to apollo-performance-tuning for optimization.