examples
Webhook Examples#
Real-world integration examples for common use cases. Copy-paste these code snippets to quickly implement webhooks with popular services.
HubSpot CRM Integration#
Auto-create contacts in HubSpot when leads are submitted.
Setup#
npm install @hubspot/api-client
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@hubspot/api-client';
import crypto from 'crypto';
const hubspot = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });
export async function POST(req: NextRequest) {
// 1. Verify signature (see Security guide)
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Handle leads.submit event
if (event.eventType === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.payload;
try {
// Create contact in HubSpot
const response = await hubspot.crm.contacts.basicApi.create({
properties: {
email: customerEmail,
firstname: customerName.split(' ')[0],
lastname: customerName.split(' ').slice(1).join(' '),
phone: customerPhone,
lifecyclestage: 'lead',
lead_source: 'AlonChat Chatbot'
}
});
console.log('Created HubSpot contact:', response.id);
return NextResponse.json({
success: true,
hubspotContactId: response.id
});
} catch (error: any) {
// Handle duplicate email error
if (error.code === 'CONTACT_EXISTS') {
console.log('Contact already exists:', customerEmail);
return NextResponse.json({ success: true, duplicate: true });
}
throw error;
}
}
return NextResponse.json({ success: true });
}
Environment variables:
HUBSPOT_ACCESS_TOKEN=pat-na1-xxx
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret
Google Sheets Integration#
Append new leads to Google Sheets automatically.
Setup#
npm install googleapis
1. Create Google Service Account:
- Go to Google Cloud Console
- Create new service account
- Download JSON key file
- Share your Google Sheet with service account email
2. Implementation:
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { google } from 'googleapis';
import crypto from 'crypto';
// Load service account credentials
const auth = new google.auth.GoogleAuth({
credentials: JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_JSON!),
scopes: ['https://www.googleapis.com/auth/spreadsheets']
});
const sheets = google.sheets({ version: 'v4', auth });
export async function POST(req: NextRequest) {
// 1. Verify signature (see Security guide)
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Handle leads.submit event
if (event.eventType === 'leads.submit') {
const { customerEmail, customerName, customerPhone, conversationId } = event.payload;
try {
// Append row to Google Sheet
await sheets.spreadsheets.values.append({
spreadsheetId: process.env.GOOGLE_SHEET_ID!,
range: 'Leads!A:E',
valueInputOption: 'USER_ENTERED',
requestBody: {
values: [[
new Date().toISOString(),
customerName,
customerEmail,
customerPhone || '',
conversationId
]]
}
});
console.log('Appended to Google Sheets');
return NextResponse.json({ success: true });
} catch (error) {
console.error('Google Sheets error:', error);
throw error;
}
}
return NextResponse.json({ success: true });
}
Environment variables:
GOOGLE_SERVICE_ACCOUNT_JSON={"type":"service_account",...}
GOOGLE_SHEET_ID=1abc123xyz...
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret
Google Sheets Format:
| Timestamp | Name | Phone | Conversation ID | |
|---|---|---|---|---|
| 2025-11-26T10:30:00Z | John Doe | john@example.com | +639171234567 | conv-123 |
Slack Notifications#
Send Slack messages when leads are submitted or conversations end.
Setup#
npm install @slack/web-api
1. Create Slack App:
- Go to Slack API
- Create new app
- Add "Incoming Webhooks" or use Web API with bot token
- Install to workspace
2. Implementation:
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { WebClient } from '@slack/web-api';
import crypto from 'crypto';
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
export async function POST(req: NextRequest) {
// 1. Verify signature
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Handle events
if (event.eventType === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.payload;
await slack.chat.postMessage({
channel: process.env.SLACK_CHANNEL_ID!,
text: '🎉 New lead from AlonChat!',
blocks: [
{
type: 'header',
text: {
type: 'plain_text',
text: '🎉 New Lead Submitted'
}
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Name:*\n${customerName}`
},
{
type: 'mrkdwn',
text: `*Email:*\n${customerEmail}`
},
{
type: 'mrkdwn',
text: `*Phone:*\n${customerPhone || 'N/A'}`
},
{
type: 'mrkdwn',
text: `*Time:*\n${new Date(event.timestamp).toLocaleString()}`
}
]
},
{
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: 'View Conversation'
},
url: `https://alonchat.com/conversations/${event.payload.conversationId}`
}
]
}
]
});
}
if (event.eventType === 'conversation.ended') {
const { duration, messageCount, satisfaction } = event.payload;
await slack.chat.postMessage({
channel: process.env.SLACK_CHANNEL_ID!,
text: `Conversation ended (${duration}s, ${messageCount} messages)`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `📝 Conversation ended\n` +
`*Duration:* ${Math.round(duration / 60)} minutes\n` +
`*Messages:* ${messageCount}\n` +
`*Rating:* ${'⭐'.repeat(satisfaction?.rating || 0)}`
}
}
]
});
}
return NextResponse.json({ success: true });
}
Environment variables:
SLACK_BOT_TOKEN=xoxb-xxx
SLACK_CHANNEL_ID=C01234567
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret
Email Notifications (SendGrid)#
Send email alerts when leads are submitted.
Setup#
npm install @sendgrid/mail
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import sgMail from '@sendgrid/mail';
import crypto from 'crypto';
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
export async function POST(req: NextRequest) {
// 1. Verify signature
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Handle leads.submit event
if (event.eventType === 'leads.submit') {
const { customerEmail, customerName, customerPhone, conversationId } = event.payload;
await sgMail.send({
to: process.env.SALES_EMAIL!,
from: process.env.FROM_EMAIL!,
subject: `🎉 New Lead: ${customerName}`,
html: `
<h2>New Lead from AlonChat</h2>
<p><strong>Name:</strong> ${customerName}</p>
<p><strong>Email:</strong> ${customerEmail}</p>
<p><strong>Phone:</strong> ${customerPhone || 'N/A'}</p>
<p><strong>Time:</strong> ${new Date(event.timestamp).toLocaleString()}</p>
<p>
<a href="https://alonchat.com/conversations/${conversationId}">
View Conversation
</a>
</p>
`
});
// Send welcome email to lead
await sgMail.send({
to: customerEmail,
from: process.env.FROM_EMAIL!,
subject: 'Thanks for reaching out!',
html: `
<h2>Hi ${customerName}!</h2>
<p>Thank you for contacting us. We'll get back to you shortly.</p>
<p>In the meantime, feel free to explore our website or reply to this email with any questions.</p>
<p>Best regards,<br>Your Team</p>
`
});
}
return NextResponse.json({ success: true });
}
Environment variables:
SENDGRID_API_KEY=SG.xxx
SALES_EMAIL=sales@yourcompany.com
FROM_EMAIL=noreply@yourcompany.com
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret
Zapier Integration#
Use Zapier's Webhook trigger for no-code integrations.
Setup#
1. Create Zap:
- Trigger: Webhooks by Zapier → Catch Hook
- Action: Choose your app (Airtable, Mailchimp, etc.)
2. Get Webhook URL: Zapier provides a webhook URL:
https://hooks.zapier.com/hooks/catch/123456/abcdef/
3. Add to AlonChat:
- Go to Agent Settings → Webhooks
- Click "Add Webhook"
- Enter Zapier webhook URL
- Select events (e.g., "leads.submit")
- Save
4. Test:
- Click "Test" button in AlonChat
- Check Zapier for received data
- Continue building your Zap
Popular Zap Examples:
- Lead → Airtable (project management)
- Lead → Mailchimp (email marketing)
- Lead → Typeform (survey trigger)
- Conversation → Trello (support tickets)
Make.com (Integromat) Integration#
Similar to Zapier, but with more advanced features.
Setup#
1. Create Scenario:
- Trigger: Webhooks → Custom Webhook
- Copy webhook URL
2. Add to AlonChat: Same as Zapier setup above.
3. Parse Data: Make.com auto-parses JSON payload into fields you can use in subsequent modules.
Popular Make Scenarios:
- Lead → Google Calendar (schedule follow-up)
- Lead → WhatsApp (instant notification)
- Lead → Notion (database entry)
- Conversation → Discord (community alert)
n8n (Self-Hosted Automation)#
Open-source alternative to Zapier/Make.
Setup#
1. Create Workflow:
- Trigger: Webhook
- Copy webhook URL
2. Add to AlonChat: Same as Zapier setup.
3. Build Workflow: n8n provides visual workflow builder with 200+ integrations.
Advantages:
- ✅ Self-hosted (full control)
- ✅ Unlimited executions
- ✅ Free forever
Discord Notifications#
Send Discord messages for leads and events.
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
export async function POST(req: NextRequest) {
// 1. Verify signature
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Handle leads.submit event
if (event.eventType === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.payload;
await fetch(process.env.DISCORD_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
embeds: [{
title: '🎉 New Lead Submitted',
color: 0x0070f3,
fields: [
{ name: 'Name', value: customerName, inline: true },
{ name: 'Email', value: customerEmail, inline: true },
{ name: 'Phone', value: customerPhone || 'N/A', inline: true }
],
timestamp: event.timestamp,
footer: { text: 'AlonChat' }
}]
})
});
}
return NextResponse.json({ success: true });
}
Get Discord Webhook URL:
- Discord server → Edit Channel → Integrations → Webhooks
- Create webhook → Copy URL
Multi-Service Router#
Route events to multiple services based on event type.
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
// Service handlers
import { handleHubSpotLead } from './services/hubspot';
import { handleSlackNotification } from './services/slack';
import { handleGoogleSheets } from './services/google-sheets';
import { handleSendGridEmail } from './services/sendgrid';
export async function POST(req: NextRequest) {
// 1. Verify signature
const rawBody = await req.text();
const receivedSig = req.headers.get('x-alonchat-signature')!;
const expectedSig = crypto
.createHmac('sha1', process.env.ALONCHAT_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expectedSig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
// 2. Parse event
const event = JSON.parse(rawBody);
// 3. Route to appropriate handlers
const results = [];
try {
switch (event.eventType) {
case 'leads.submit':
results.push(
await Promise.all([
handleHubSpotLead(event.payload),
handleGoogleSheets(event.payload),
handleSlackNotification(event),
handleSendGridEmail(event.payload)
])
);
break;
case 'conversation.ended':
results.push(
await handleSlackNotification(event)
);
break;
case 'tool.failed':
results.push(
await handleSlackNotification(event)
);
break;
}
return NextResponse.json({
success: true,
results: results.flat()
});
} catch (error) {
console.error('Webhook processing error:', error);
return NextResponse.json(
{ error: 'Internal error' },
{ status: 500 }
);
}
}
Error Handling Best Practices#
Retry Failed Services#
async function handleWithRetry(fn: () => Promise<any>, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error);
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff: 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));
}
}
}
// Usage
if (event.eventType === 'leads.submit') {
await handleWithRetry(async () => {
await hubspot.crm.contacts.basicApi.create({ ... });
});
}
Log Failures to Database#
// Log failed webhook processing
await supabase.from('webhook_failures').insert({
event_type: event.eventType,
event_payload: event,
error_message: error.message,
error_stack: error.stack,
service: 'hubspot',
retry_count: 0
});
// Later: Manual retry or automatic retry job
Testing Integrations#
Local Testing with ngrok#
# 1. Install ngrok
npm install -g ngrok
# 2. Start your local server
npm run dev
# 3. Expose to internet
ngrok http 3000
# 4. Copy ngrok URL (e.g., https://abc123.ngrok.io)
# 5. Add to AlonChat webhook: https://abc123.ngrok.io/api/webhooks/alonchat
Use Test Events#
AlonChat provides test events with "test": true field:
if (event.test === true) {
console.log('Test event received, processing normally');
}
// Process normally (test events behave like real events)
Next Steps#
- Webhook Overview - Introduction to webhooks
- Webhook Events - All available event types
- Webhook Security - HMAC verification
- API Reference - Full API documentation