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#

bash
npm install @hubspot/api-client
typescript
// 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:

env
HUBSPOT_ACCESS_TOKEN=pat-na1-xxx
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret

Google Sheets Integration#

Append new leads to Google Sheets automatically.

Setup#

bash
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:

typescript
// 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:

env
GOOGLE_SERVICE_ACCOUNT_JSON={"type":"service_account",...}
GOOGLE_SHEET_ID=1abc123xyz...
ALONCHAT_WEBHOOK_SECRET=your-webhook-secret

Google Sheets Format:

TimestampNameEmailPhoneConversation ID
2025-11-26T10:30:00ZJohn Doejohn@example.com+639171234567conv-123

Slack Notifications#

Send Slack messages when leads are submitted or conversations end.

Setup#

bash
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:

typescript
// 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:

env
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#

bash
npm install @sendgrid/mail
typescript
// 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:

env
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:

Code
https://hooks.zapier.com/hooks/catch/123456/abcdef/

3. Add to AlonChat:

  1. Go to Agent Settings → Webhooks
  2. Click "Add Webhook"
  3. Enter Zapier webhook URL
  4. Select events (e.g., "leads.submit")
  5. 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.

typescript
// 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:

  1. Discord server → Edit Channel → Integrations → Webhooks
  2. Create webhook → Copy URL

Multi-Service Router#

Route events to multiple services based on event type.

typescript
// 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#

typescript
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#

typescript
// 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#

bash
# 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:

typescript
if (event.test === true) {
  console.log('Test event received, processing normally');
}

// Process normally (test events behave like real events)

Next Steps#

examples | AlonChat Docs