Webhook Examples
Real-world integration examples for AlonChat webhooks with popular services
Webhook Examples#
Copy-paste these integration examples to quickly connect AlonChat webhooks with popular services.
All examples include HMAC-SHA256 signature verification. See the Security guide for details.
Signature Verification Helper#
Every example below uses this helper function. Define it once and reuse it:
// lib/verify-webhook.ts
import crypto from 'crypto';
export function verifyAlonChatSignature(
rawBody: string,
signature: string | null,
secret: string
): boolean {
if (!signature) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
} catch {
return false;
}
}
HubSpot CRM Integration#
Auto-create contacts in HubSpot when leads are submitted.
Setup#
npm install @hubspot/api-client
Implementation#
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@hubspot/api-client';
import { verifyAlonChatSignature } from '@/lib/verify-webhook';
const hubspot = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.data;
try {
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'
}
});
console.log('Created HubSpot contact:', response.id);
return NextResponse.json({ success: true, hubspotContactId: response.id });
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
if (message.includes('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 a Google Sheet automatically.
Setup#
npm install googleapis
1. Create a Google Service Account:
- Go to the Google Cloud Console
- Create a service account and download the JSON key file
- Share your Google Sheet with the service account email
2. Implementation:
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { google } from 'googleapis';
import { verifyAlonChatSignature } from '@/lib/verify-webhook';
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) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'leads.submit') {
const { customerEmail, customerName, customerPhone, conversationId } = event.data;
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 lead to Google Sheets');
return NextResponse.json({ success: true });
}
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 Sheet columns:
| Timestamp | Name | Phone | Conversation ID | |
|---|---|---|---|---|
| 2026-03-15T10: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 a Slack App at api.slack.com/apps and install it to your workspace.
2. Implementation:
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { WebClient } from '@slack/web-api';
import { verifyAlonChatSignature } from '@/lib/verify-webhook';
const slack = new WebClient(process.env.SLACK_BOT_TOKEN);
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.data;
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.data.conversationId}`
}
]
}
]
});
}
if (event.event === 'conversation.ended') {
const { duration, messageCount, satisfaction } = event.data;
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: ${satisfaction?.rating || 'N/A'}/5`
}
}
]
});
}
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 { verifyAlonChatSignature } from '@/lib/verify-webhook';
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'leads.submit') {
const { customerEmail, customerName, customerPhone, conversationId } = event.data;
// Notify your sales team
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 the 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>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
Discord Notifications#
Send Discord messages for leads and events.
// app/api/webhooks/alonchat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyAlonChatSignature } from '@/lib/verify-webhook';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.event === 'leads.submit') {
const { customerEmail, customerName, customerPhone } = event.data;
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 a Discord Webhook URL:
- In your Discord server, go to Edit Channel > Integrations > Webhooks
- Create a webhook and copy the URL
Zapier Integration#
Use Zapier's webhook trigger for no-code integrations.
- Create a Zap with trigger: Webhooks by Zapier > Catch Hook
- Copy the webhook URL Zapier provides
- Add to AlonChat: Go to Settings > Webhooks > Add Webhook, paste the Zapier URL, and select your events
- Test: Use the AlonChat test button, then check Zapier for received data
- Build your Zap: Add actions for your target service
Popular Zap ideas:
- Lead > Airtable (project management)
- Lead > Mailchimp (email marketing)
- Conversation ended > Trello (support tickets)
Make.com (Integromat) Integration#
Similar to Zapier with more advanced workflow capabilities.
- Create a Scenario with trigger: Webhooks > Custom Webhook
- Copy the webhook URL
- Add to AlonChat (same as Zapier setup)
- Make.com auto-parses the JSON payload into fields for subsequent modules
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 { verifyAlonChatSignature } from '@/lib/verify-webhook';
import { handleHubSpotLead } from './services/hubspot';
import { handleSlackNotification } from './services/slack';
import { handleGoogleSheets } from './services/google-sheets';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signature = req.headers.get('x-alonchat-signature');
if (!verifyAlonChatSignature(rawBody, signature, process.env.ALONCHAT_WEBHOOK_SECRET!)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
try {
switch (event.event) {
case 'leads.submit':
await Promise.all([
handleHubSpotLead(event.data),
handleGoogleSheets(event.data),
handleSlackNotification(event)
]);
break;
case 'conversation.ended':
await handleSlackNotification(event);
break;
case 'tool.failed':
await handleSlackNotification(event);
break;
}
return NextResponse.json({ success: true });
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
console.error('Webhook processing error:', message);
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
}
}
Error Handling#
Retry with Backoff#
async function handleWithRetry(
fn: () => Promise<unknown>,
maxRetries = 3
): Promise<unknown> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
console.error(`Attempt ${attempt} failed:`, message);
if (attempt === maxRetries) {
throw error;
}
await new Promise(resolve =>
setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
);
}
}
}
Local Testing with ngrok#
# 1. Start your local server
npm run dev
# 2. Expose to the internet
npx ngrok http 3000
# 3. Copy the ngrok URL (e.g., https://abc123.ngrok.io)
# 4. Add to AlonChat: https://abc123.ngrok.io/api/webhooks/alonchat
Test events from AlonChat include a "test": true field so you can distinguish them from real events in your processing logic.
Next Steps#
- Webhooks Overview — Setup and configuration
- Webhook Events — All event types and payloads
- Webhook Security — HMAC-SHA256 verification details