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:

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

bash
npm install @hubspot/api-client

Implementation#

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

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

Google Sheets Integration#

Append new leads to a Google Sheet automatically.

Setup#

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

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

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

Google Sheet columns:

TimestampNameEmailPhoneConversation ID
2026-03-15T10: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 a Slack App at api.slack.com/apps and install it to your workspace.

2. Implementation:

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

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

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

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

  1. In your Discord server, go to Edit Channel > Integrations > Webhooks
  2. Create a webhook and copy the URL

Zapier Integration#

Use Zapier's webhook trigger for no-code integrations.

  1. Create a Zap with trigger: Webhooks by Zapier > Catch Hook
  2. Copy the webhook URL Zapier provides
  3. Add to AlonChat: Go to Settings > Webhooks > Add Webhook, paste the Zapier URL, and select your events
  4. Test: Use the AlonChat test button, then check Zapier for received data
  5. 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.

  1. Create a Scenario with trigger: Webhooks > Custom Webhook
  2. Copy the webhook URL
  3. Add to AlonChat (same as Zapier setup)
  4. Make.com auto-parses the JSON payload into fields for subsequent modules

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

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

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