> ## Documentation Index
> Fetch the complete documentation index at: https://www.docs.wazap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook System Overview

> Framework-agnostic webhook processing for receiving and handling WhatsApp messages

## Key Features

<CardGroup cols={2}>
  <Card title="Framework Agnostic" icon="layers">
    Works with Express, Next.js, NestJS, Fastify, AWS Lambda, and any Node.js framework
  </Card>

  <Card title="Auto Message Parsing" icon="message">
    Automatically parses WhatsApp webhook payloads into structured message objects
  </Card>

  <Card title="Type-Safe Handlers" icon="shield">
    Strongly typed handler functions for each message type with full IntelliSense
  </Card>

  <Card title="Built-in Verification" icon="check-circle">
    Automatic webhook verification for security and authenticity
  </Card>

  <Card title="Message Buffer System" icon="inbox">
    Optional message grouping for better context and performance optimization
  </Card>

  <Card title="Auto Storage" icon="database">
    Automatically persist messages to database when storage is enabled
  </Card>
</CardGroup>

## Quick Start

### 1. Create Webhook Processor

```typescript theme={null}
import { WhatsAppClient } from 'whatsapp-client-sdk';

const client = new WhatsAppClient({
  accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
  phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,
  webhookVerifyToken: process.env.WHATSAPP_WEBHOOK_TOKEN!,
});

const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    console.log(`Text from ${message.from}: ${message.text}`);
    await client.sendText(message.from, `Echo: ${message.text}`);
  },
  
  onButtonClick: async (message) => {
    console.log(`Button clicked: ${message.interactive.button_id}`);
    await client.sendText(message.from, 'Button received!');
  },
  
  onError: async (error, message) => {
    console.error('Webhook error:', error.message);
    // For enhanced error handling, see: /guides/error-handling
  }
});
```

### 2. Universal Webhook Endpoint

Works with any framework:

```typescript theme={null}
// Express, Fastify, Next.js, etc.
app.all('/webhook', async (req, res) => {
  try {
    const result = await webhookProcessor.processWebhook(req.body, req.query);
    res.status(result.status).send(result.response);
  } catch (error) {
    res.status(500).send('Internal Server Error');
  }
});
```

## Webhook Handler Types

### Message Handlers

```typescript theme={null}
interface WebhookHandlers {
  onTextMessage?: (message: ProcessedIncomingMessage & { text: string }) => Promise<void>;
  onImageMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
  onVideoMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
  onAudioMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
  onDocumentMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
  onLocationMessage?: (message: ProcessedIncomingMessage & { location: LocationInfo }) => Promise<void>;
  onContactMessage?: (message: ProcessedIncomingMessage & { contacts: Contact[] }) => Promise<void>;
  onStickerMessage?: (message: ProcessedIncomingMessage & { media: MediaInfo }) => Promise<void>;
  onButtonClick?: (message: ProcessedIncomingMessage & { interactive: InteractiveInfo }) => Promise<void>;
  onListSelect?: (message: ProcessedIncomingMessage & { interactive: InteractiveInfo }) => Promise<void>;
  onMessageStatusUpdate?: (statusUpdate: MessageStatusUpdate) => Promise<void>;
  onUnknownMessage?: (message: ProcessedIncomingMessage) => Promise<void>;
  onError?: (error: Error, message?: ProcessedIncomingMessage) => Promise<void>;
}
```

## Complete Example

### Smart Auto-Responder

```typescript theme={null}
const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    const text = message.text.toLowerCase();
    
    if (text.includes('hello') || text.includes('hi')) {
      await client.sendText(
        message.from,
        '👋 Hello! How can I help you today?'
      );
    } else if (text.includes('help')) {
      await client.sendButtons(
        message.from,
        'How can I assist you?',
        [
          { id: 'support', title: '🛟 Get Support' },
          { id: 'info', title: 'ℹ️ Information' },
          { id: 'contact', title: '📞 Contact Us' }
        ]
      );
    } else if (text.includes('price') || text.includes('cost')) {
      await client.sendList(
        message.from,
        'Here are our pricing options:',
        'View Pricing',
        [
          {
            title: 'Plans',
            rows: [
              { id: 'basic', title: 'Basic Plan', description: '$9.99/month' },
              { id: 'pro', title: 'Pro Plan', description: '$19.99/month' },
              { id: 'enterprise', title: 'Enterprise', description: 'Custom pricing' }
            ]
          }
        ]
      );
    } else {
      await client.sendText(
        message.from,
        '🤖 Thanks for your message! A human will respond shortly.'
      );
    }
  },
  
  onImageMessage: async (message) => {
    await client.sendText(
      message.from,
      '📸 Great image! Thanks for sharing.'
    );
    
    // Optional: Download and process the image
    // const buffer = await client.downloadMedia(message.media.id);
    // await processImage(buffer);
  },
  
  onButtonClick: async (message) => {
    const buttonId = message.interactive.button_id;
    
    switch (buttonId) {
      case 'support':
        await client.sendText(
          message.from,
          '🛟 Our support team will contact you within 24 hours.\n\n' +
          'For urgent issues, call: +1-800-555-0123'
        );
        break;
        
      case 'info':
        await client.sendText(
          message.from,
          'ℹ️ We\'re a tech company focused on innovative solutions.\n\n' +
          'Learn more: https://company.example.com'
        );
        break;
        
      case 'contact':
        await client.sendContacts(message.from, [
          {
            name: { formatted_name: 'Customer Support' },
            phones: [{ phone: '+1-800-555-0123', type: 'WORK' }],
            emails: [{ email: 'support@company.com', type: 'WORK' }]
          }
        ]);
        break;
    }
  },
  
  onListSelect: async (message) => {
    const listId = message.interactive.list_id;
    
    switch (listId) {
      case 'basic':
        await client.sendText(
          message.from,
          '📦 Basic Plan - $9.99/month\n\n' +
          '✅ 10 GB Storage\n' +
          '✅ Email Support\n' +
          '✅ Basic Features\n\n' +
          'Ready to upgrade? https://signup.example.com/basic'
        );
        break;
        
      case 'pro':
        await client.sendText(
          message.from,
          '🚀 Pro Plan - $19.99/month\n\n' +
          '✅ 100 GB Storage\n' +
          '✅ Priority Support\n' +
          '✅ Advanced Features\n' +
          '✅ API Access\n\n' +
          'Start free trial: https://signup.example.com/pro'
        );
        break;
        
      case 'enterprise':
        await client.sendText(
          message.from,
          '🏢 Enterprise Plan\n\n' +
          '✅ Unlimited Storage\n' +
          '✅ 24/7 Phone Support\n' +
          '✅ Custom Features\n' +
          '✅ Dedicated Account Manager\n\n' +
          'Contact sales: sales@company.com'
        );
        break;
    }
  },
  
  onLocationMessage: async (message) => {
    const { latitude, longitude, name } = message.location;
    
    await client.sendText(
      message.from,
      `📍 Thanks for sharing your location!\n\n` +
      `${name || 'Location'}: ${latitude}, ${longitude}\n\n` +
      `We'll find the nearest service center for you.`
    );
  },
  
  onDocumentMessage: async (message) => {
    await client.sendText(
      message.from,
      `📄 Document "${message.media.filename}" received.\n\n` +
      `Our team will review it and get back to you within 2 business days.`
    );
  },
  
  onContactMessage: async (message) => {
    await client.sendText(
      message.from,
      '📇 Thank you for sharing the contact information!\n\n' +
      'We\'ve saved it to our system.'
    );
  },
  
  onStickerMessage: async (message) => {
    await client.sendText(
      message.from,
      '😄 Nice sticker! We love creative communication.'
    );
  },
  
  onError: async (error, message) => {
    console.error('Webhook processing error:', {
      error: error.message,
      messageId: message?.id,
      from: message?.from
    });
    
    if (message) {
      try {
        await client.sendText(
          message.from,
          '⚠️ Sorry, we encountered a technical issue. Please try again or contact support.'
        );
      } catch (sendError) {
        console.error('Failed to send error message:', sendError);
      }
    }
  }
});
```

## Framework Integration

The webhook processor works universally with any Node.js framework:

```typescript theme={null}
// Universal pattern for any framework
app.all('/webhook', async (req, res) => {
  const result = await webhookProcessor.processWebhook(req.body, req.query);
  res.status(result.status).send(result.response);
});
```

<Tip>
  For complete framework-specific examples including NestJS, Express, Next.js, Fastify, and AWS Lambda, see our [Framework Integration Examples](/examples/framework-integration).

  For message grouping and performance optimization, check out the [Message Buffer System](/webhooks/message-buffer).

  To automatically persist messages to a database, see [Storage & Persistence](/storage/overview).
</Tip>

## Advanced Features

### Message Context Tracking

```typescript theme={null}
// Track conversation context
const conversationContext = new Map();

const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    const context = conversationContext.get(message.from) || { step: 'greeting' };
    
    switch (context.step) {
      case 'greeting':
        await client.sendText(message.from, 'What\'s your name?');
        conversationContext.set(message.from, { step: 'name' });
        break;
        
      case 'name':
        context.name = message.text;
        await client.sendText(message.from, `Nice to meet you, ${context.name}! What can I help you with?`);
        conversationContext.set(message.from, { ...context, step: 'help' });
        break;
        
      case 'help':
        await client.sendText(message.from, `Thanks for your question, ${context.name}. Let me help you with that.`);
        conversationContext.delete(message.from); // Reset context
        break;
    }
  }
});
```

### Webhook Security

```typescript theme={null}
// Enhanced webhook verification
const webhookProcessor = client.createWebhookProcessor({
  verifyToken: process.env.WHATSAPP_WEBHOOK_TOKEN,
  
  onTextMessage: async (message) => {
    // Message is already verified by processor
    await handleMessage(message);
  },
  
  onError: async (error) => {
    if (error.name === 'WebhookVerificationError') {
      console.error('Webhook verification failed:', error.message);
      // Handle security breach
    }
  }
});
```

## Best Practices

### 1. Response Time

Keep webhook responses under 20 seconds:

```typescript theme={null}
const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    // Quick acknowledgment
    await client.sendText(message.from, 'Message received! Processing...');
    
    // Process asynchronously
    processMessageAsync(message); // Don't await
  }
});

async function processMessageAsync(message: any) {
  // Long-running processing
  const result = await complexProcessing(message.text);
  await client.sendText(message.from, `Result: ${result}`);
}
```

### 2. Error Handling

```typescript theme={null}
const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    try {
      await processMessage(message);
    } catch (error) {
      console.error('Message processing failed:', error);
      await client.sendText(
        message.from,
        'Sorry, something went wrong. Please try again.'
      );
    }
  },
  
  onError: async (error, message) => {
    // Log error for monitoring
    console.error('Webhook error:', { error: error.message, messageId: message?.id });

    // For comprehensive error handling and monitoring patterns
    // see our Enhanced Error Handling Guide: /guides/error-handling
    await notifyErrorService(error, message);
  }
});
```

### 3. Rate Limiting

```typescript theme={null}
const messageRateLimit = new Map();

const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    const now = Date.now();
    const userMessages = messageRateLimit.get(message.from) || [];
    
    // Remove messages older than 1 minute
    const recentMessages = userMessages.filter(time => now - time < 60000);
    
    if (recentMessages.length >= 10) {
      await client.sendText(
        message.from,
        'You\'re sending messages too quickly. Please slow down.'
      );
      return;
    }
    
    recentMessages.push(now);
    messageRateLimit.set(message.from, recentMessages);
    
    // Process message normally
    await handleMessage(message);
  }
});
```

## Message Status Updates

Track the delivery status of sent messages through webhook notifications.

### onMessageStatusUpdate Handler

```typescript theme={null}
const webhookProcessor = client.createWebhookProcessor({
  onMessageStatusUpdate: async (statusUpdate) => {
    console.log(`Message ${statusUpdate.id}: ${statusUpdate.status}`);

    switch (statusUpdate.status) {
      case 'sent':
        console.log('✅ Message sent successfully');
        break;
      case 'delivered':
        console.log('📱 Message delivered to device');
        break;
      case 'read':
        console.log('👀 Message read by user');
        break;
      case 'failed':
        console.log('❌ Message delivery failed');
        if (statusUpdate.errors) {
          console.error('Error details:', statusUpdate.errors);
        }
        break;
    }
  }
});
```

### MessageStatusUpdate Interface

```typescript theme={null}
interface MessageStatusUpdate {
  id: string;                    // Message ID
  status: MessageStatus;         // Current status
  timestamp: string;             // Status timestamp
  recipient_id: string;          // Recipient WhatsApp ID
  phoneNumberId: string;         // Business phone number ID
  businessId: string;            // Business account ID
  conversation?: {
    id: string;
    origin?: { type: string; };
  };
  pricing?: {
    pricing_model: string;
    billable: boolean;
    category: string;
  };
  errors?: Array<{
    code: number;
    title: string;
    message?: string;
    error_data?: { details: string; };
  }>;
}

enum MessageStatus {
  SENT = 'sent',
  DELIVERED = 'delivered',
  READ = 'read',
  FAILED = 'failed'
}
```

### Webhook Payload Format

WhatsApp sends status updates in this format:

```json theme={null}
{
  "object": "whatsapp_business_account",
  "entry": [{
    "changes": [{
      "value": {
        "messaging_product": "whatsapp",
        "metadata": {
          "phone_number_id": "BUSINESS_PHONE_ID"
        },
        "statuses": [{
          "id": "MESSAGE_ID",
          "status": "read",
          "timestamp": "EPOCH_TIMESTAMP",
          "recipient_id": "USER_WHATSAPP_ID",
          "conversation": {
            "id": "CONVERSATION_ID",
            "origin": { "type": "business_initiated" }
          },
          "pricing": {
            "pricing_model": "CBP",
            "billable": true,
            "category": "business_initiated"
          }
        }]
      }
    }]
  }]
}
```
