Skip to content

WhatsApp Webhook Forwarding Feature - Complete Documentation

Table of Contents


Overview

The WhatsApp Webhook Forwarding feature allows your application to automatically forward incoming WhatsApp webhook events to external endpoints (primarily N8N automation workflows) in a standardized, structured format.

What It Does

  • ✅ Receives WhatsApp webhook events (messages, status updates, etc.)
  • ✅ Transforms raw WhatsApp payloads into N8N-compatible structured format
  • ✅ Forwards selected events to configured external webhook URLs
  • ✅ Includes signature verification for security
  • ✅ Supports multi-tenancy (tenant-specific configurations)
  • ✅ Filters events by field types (messages, statuses, templates, etc.)

Use Cases

  • Integrate with N8N for automation workflows
  • Send WhatsApp events to third-party systems
  • Build custom integrations without modifying core code
  • Create multi-step automation based on WhatsApp events
  • Analytics and monitoring of WhatsApp interactions

Architecture & Flow

┌─────────────────┐
│  WhatsApp API   │ (Meta/Facebook)
│   (Webhooks)    │
└────────┬────────┘
         │ 1. Webhook Event

┌─────────────────────────────────────────┐
│  WhatsAppWebhookController              │
│  (/webhook/whatsapp)                    │
├─────────────────────────────────────────┤
│ • Receives raw WhatsApp payload         │
│ • Verifies webhook authenticity         │
│ • Processes messages/statuses           │
│ • Checks duplicate messages             │
└────────┬────────────────────────────────┘
         │ 2. Process Webhook

┌─────────────────────────────────────────┐
│  forwardWebhookData()                   │
├─────────────────────────────────────────┤
│ • Check if forwarding enabled           │
│ • Filter by enabled event fields        │
│ • Check webhook URL validity            │
└────────┬────────────────────────────────┘
         │ 3. Transform Data

┌─────────────────────────────────────────┐
│  WhatsAppWebhookN8NTransformer          │
├─────────────────────────────────────────┤
│ • Detect event type                     │
│ • Extract resource data                 │
│ • Structure payload for N8N             │
│ • Add tenant context                    │
│ • Include original payload              │
└────────┬────────────────────────────────┘
         │ 4. N8N Formatted Payload

┌─────────────────────────────────────────┐
│  HTTP Request to External Endpoint      │
├─────────────────────────────────────────┤
│ • Add signature header (HMAC-SHA256)    │
│ • Add event type header                 │
│ • Add timestamp header                  │
│ • POST/GET/Custom method                │
└────────┬────────────────────────────────┘
         │ 5. Forward to N8N

┌─────────────────┐
│  N8N Workflow   │ (or any external endpoint)
│  (Automation)   │
└─────────────────┘

Key Components

1. WhatsAppWebhookController

File: app/Http/Controllers/Whatsapp/WhatsAppWebhookController.php

Main Method: forwardWebhookData(string $feedData, array $payload)

Responsibilities:

  • Retrieve tenant-specific webhook settings
  • Filter events based on configured field types
  • Call transformer for payload conversion
  • Send HTTP request to external endpoint
  • Log forwarding success/failure

Settings Used:

  • enable_webhook_resend - Enable/disable forwarding
  • whatsapp_data_resend_to - Target webhook URL
  • webhook_resend_method - HTTP method (POST/GET)
  • webhook_selected_fields - Event types to forward

2. WhatsAppWebhookN8NTransformer

File: app/Transformers/WhatsAppWebhookN8NTransformer.php

Main Method: transform(array $whatsappPayload, int $tenantId): array

Responsibilities:

  • Detect WhatsApp event type from payload structure
  • Extract message/status/account data
  • Structure data in N8N-compatible format
  • Add tenant information
  • Include metadata for tracing

Key Methods:

  • detectEventType() - Identifies the webhook event type
  • extractResourceData() - Extracts relevant data based on event
  • getResourceType() - Determines resource type (message, status, etc.)
  • getTenantData() - Fetches tenant context
  • generateEventId() - Creates unique event identifier

3. Webhook Configuration UI

File: resources/views/livewire/tenant/settings/whats-mark/web-hooks-settings.blade.php

Features:

  • Toggle to enable/disable webhook forwarding
  • URL input for webhook endpoint
  • HTTP method selector (GET/POST)
  • Event field selector with:
    • Multi-select dropdown
    • Search functionality
    • Select all/Clear all options
    • Visual tag display for selected events

Configuration Guide

Step 1: Enable Webhook Forwarding

  1. Navigate to Settings

    • Go to: Tenant Settings → WhatsApp → Webhooks
  2. Enable Webhook Resend

    • Toggle "Enable Webhooks Resend" to ON
  3. Configure Webhook URL

    • Enter your N8N webhook URL or external endpoint
    • Example: https://your-n8n-instance.com/webhook/whatsapp-events
    • Must be a valid HTTPS URL (recommended for production)
  4. Select HTTP Method

    • POST (Recommended) - Sends data in request body
    • GET - Sends data as query parameters
    • Other methods available for custom integrations
  5. Select Event Fields

    • Choose which WhatsApp event types to forward:
      • messages - Incoming messages
      • message_echoes - Sent messages
      • message_status - Status updates (sent, delivered, read, failed)
      • account_alerts - Account alerts
      • account_review_update - Account review updates
      • business_capability_update - Business capability changes
      • message_template_status_update - Template status changes
      • message_template_quality_update - Template quality updates
      • phone_number_quality_update - Phone quality updates
      • And more...
  6. Save Configuration

Step 2: Set Up N8N Webhook (If using N8N)

  1. Create N8N Workflow

    • Add a Webhook node as the trigger
  2. Configure Webhook Node

    • HTTP Method: POST
    • Path: /webhook/whatsapp-events (or custom path)
    • Authentication: None (or configure based on your needs)
    • Response: Return immediately
  3. Copy Webhook URL

    • Get the production webhook URL from N8N
    • Paste it in your WhatsApp webhook settings
  4. Activate Workflow

    • Ensure the workflow is active to receive events

N8N Format Structure

Complete Payload Example

Based on the sample JSON you provided, here's the standardized structure:

json
{
  "event": {
    "id": "evt_1767263928_7KGQvXqbFF",
    "type": "whatsapp.message.received",
    "timestamp": "2026-01-01T10:38:48+00:00",
    "version": "1.0"
  },
  "tenant": {
    "id": 3,
    "name": "vatsal"
  },
  "data": {
    "resource": {
      "type": "message",
      "id": "wamid.HBgMOTE5OTI1MTE5Mjg0FQIAEhgWM0VCMDE1NDRCMTFBMDQ1RTlFM0NFMwA=",
      "attributes": {
        "message_id": "wamid.HBgMOTE5OTI1MTE5Mjg0FQIAEhgWM0VCMDE1NDRCMTFBMDQ1RTlFM0NFMwA=",
        "from": "919925119284",
        "timestamp": "1767263926",
        "type": "text",
        "text": "Hello",
        "context": null
      }
    },
    "relationships": {
      "contact": {
        "wa_id": "919925119284",
        "name": "Team Corbital Technologies"
      },
      "metadata": {
        "phone_number_id": "339141689277134",
        "display_phone_number": "919428735270"
      }
    }
  },
  "whatsapp": {
    "original_payload": {
      "object": "whatsapp_business_account",
      "entry": [
        {
          "id": "315064115026195",
          "changes": [
            {
              "value": {
                "messaging_product": "whatsapp",
                "metadata": {
                  "display_phone_number": "919428735270",
                  "phone_number_id": "339141689277134"
                },
                "contacts": [
                  {
                    "profile": {
                      "name": "Team Corbital Technologies"
                    },
                    "wa_id": "919925119284"
                  }
                ],
                "messages": [
                  {
                    "from": "919925119284",
                    "id": "wamid.HBgMOTE5OTI1MTE5Mjg0FQIAEhgWM0VCMDE1NDRCMTFBMDQ1RTlFM0NFMwA=",
                    "timestamp": "1767263926",
                    "text": {
                      "body": "Hello"
                    },
                    "type": "text"
                  }
                ]
              },
              "field": "messages"
            }
          ]
        }
      ]
    }
  },
  "metadata": {
    "source": "whatsapp_webhook_forward",
    "environment": "production",
    "request_id": "595bbcf8-93bf-418b-8209-3343c76ad852"
  }
}

Payload Structure Breakdown

1. Event Object

Contains event metadata and identification:

json
{
  "id": "evt_1767263928_7KGQvXqbFF", // Unique event ID
  "type": "whatsapp.message.received", // Event type
  "timestamp": "2026-01-01T10:38:48+00:00", // ISO 8601 timestamp
  "version": "1.0" // Format version
}

2. Tenant Object

Multi-tenancy context:

json
{
  "id": 3, // Tenant ID
  "name": "vatsal" // Tenant name/company name
}

3. Data Object

Contains the actual event data:

Resource

The main entity (message, status, etc.):

json
{
  "type": "message", // Resource type
  "id": "wamid...", // Resource ID (message ID)
  "attributes": {
    // Resource-specific data
    "message_id": "...",
    "from": "919925119284",
    "timestamp": "1767263926",
    "type": "text",
    "text": "Hello",
    "context": null
  }
}
Relationships

Related entities:

json
{
  "contact": {
    "wa_id": "919925119284",
    "name": "Team Corbital Technologies"
  },
  "metadata": {
    "phone_number_id": "339141689277134",
    "display_phone_number": "919428735270"
  }
}

4. WhatsApp Object

Original webhook payload from Meta:

json
{
  "original_payload": {
    // Complete raw WhatsApp webhook data
  }
}

5. Metadata Object

Tracking and debugging information:

json
{
  "source": "whatsapp_webhook_forward",
  "environment": "production",
  "request_id": "595bbcf8-93bf-418b-8209-3343c76ad852"
}

Event Types

Supported Event Types

The transformer automatically detects and categorizes events:

Message Events

  • whatsapp.message.received - Incoming message from user
  • whatsapp.message.sent - Message sent by business (echo)
  • whatsapp.message.delivered - Message delivered to recipient
  • whatsapp.message.read - Message read by recipient
  • whatsapp.message.failed - Message delivery failed

Status Events

  • whatsapp.status.updated - Generic status update

Account Events

  • whatsapp.account.alert - Account alert notification
  • whatsapp.account.review_updated - Account review status changed
  • whatsapp.account.updated - Account information updated

Business Events

  • whatsapp.business.capability_updated - Business capabilities changed
  • whatsapp.business.status_updated - Business status changed

Template Events

  • whatsapp.template.status_updated - Template approval status
  • whatsapp.template.quality_updated - Template quality score changed

Phone Events

  • whatsapp.phone.quality_updated - Phone number quality changed
  • whatsapp.phone.name_updated - Display name updated

Other Events

  • whatsapp.call.received - Incoming WhatsApp call
  • whatsapp.flow.event - Flow-related event

Event Detection Logic

The transformer uses the following logic to detect event types:

php
// 1. Check the 'field' from webhook changes
$field = $payload['entry'][0]['changes'][0]['field'];

// 2. Based on field and data structure:
if ($field === 'messages') {
    // Check if it's a received message
    return 'whatsapp.message.received';
}

if ($field === 'message_echoes') {
    return 'whatsapp.message.sent';
}

// 3. Check for status updates
if (isset($payload['entry'][0]['changes'][0]['value']['statuses'])) {
    $status = $statuses[0]['status'];
    // Map to specific event type
    return 'whatsapp.message.delivered|read|sent|failed';
}

// 4. Map other fields directly
return match($field) {
    'account_alerts' => 'whatsapp.account.alert',
    'calls' => 'whatsapp.call.received',
    // ... etc
};

Implementation Details

Core Logic Flow

1. Webhook Reception (WhatsAppWebhookController)

php
protected function processWebhookPayload()
{
    $feedData = file_get_contents('php://input');
    $payload = json_decode($feedData, true);

    // Extract business & phone number IDs
    $business_id = $payload['entry'][0]['id'];
    $phoneNumberId = $payload['entry'][0]['changes'][0]['value']['metadata']['phone_number_id'];

    // Get tenant ID from WhatsApp details
    $this->tenant_id = getTenantIdFromWhatsappDetails($business_id, $phoneNumberId);

    // Process webhook...

    // Forward webhook data if enabled
    $this->forwardWebhookData($feedData, $payload);
}

2. Forward Decision Making

php
protected function forwardWebhookData(string $feedData, array $payload)
{
    // 1. Get tenant settings
    $enabled = get_tenant_setting_by_tenant_id('whats-mark', 'enable_webhook_resend', '', $this->tenant_id);
    $url = get_tenant_setting_by_tenant_id('whats-mark', 'whatsapp_data_resend_to', '', $this->tenant_id);
    $method = get_tenant_setting_by_tenant_id('whats-mark', 'webhook_resend_method', 'POST', $this->tenant_id);

    // 2. Extract fields from webhook
    $fields = collect($payload['entry'] ?? [])
        ->flatMap(fn($entry) => collect($entry['changes'] ?? []))
        ->pluck('field')
        ->filter()
        ->unique();

    // 3. Get enabled fields from settings
    $enabled_fields = collect(get_tenant_setting_by_tenant_id('whats-mark', 'webhook_selected_fields', [], $this->tenant_id));

    // 4. Check if field is allowed
    $isFieldAllowed = $fields->intersect($enabled_fields)->isNotEmpty();

    if (!$isFieldAllowed) {
        whatsapp_log('Webhook Forward Skipped', 'info', [
            'reason' => 'No enabled fields found for resend',
            'fields' => $fields,
        ]);
        return;
    }

    // 5. Proceed with forwarding...
}

3. Data Transformation

php
// Transform to N8N format
$n8nPayload = \App\Transformers\WhatsAppWebhookN8NTransformer::transform(
    $payload,
    $this->tenant_id
);

4. Signature Generation

php
// Generate HMAC-SHA256 signature
$secret = config('ApiWebhookManager.signing_secret', config('app.key'));
$signature = hash_hmac('sha256', json_encode($n8nPayload), $secret);

5. HTTP Request

php
$headers = [
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
    'User-Agent' => 'whatsmark-Webhook/1.0',
    'X-Webhook-Signature' => $signature,
    'X-Webhook-Event' => $n8nPayload['event']['type'] ?? 'unknown',
    'X-Webhook-Timestamp' => now()->toIso8601String(),
    'X-Webhook-Format' => 'n8n',
];

switch ($method) {
    case 'POST':
        $response = \Http::withHeaders($headers)->post($url, $n8nPayload);
        break;
    case 'GET':
        $response = \Http::withHeaders($headers)->get($url, $n8nPayload);
        break;
    default:
        $response = \Http::withHeaders($headers)
            ->withBody(json_encode($n8nPayload), 'application/json')
            ->send($method, $url);
        break;
}

6. Logging

php
whatsapp_log('Webhook Forwarded (N8N Format)', 'info', [
    'url' => $url,
    'method' => $method,
    'event_type' => $n8nPayload['event']['type'] ?? 'unknown',
    'status_code' => $response->status(),
    'success' => $response->successful(),
]);

Security Features

1. Signature Verification

Every forwarded webhook includes a signature for verification:

php
// Generation (in application)
$secret = config('ApiWebhookManager.signing_secret', config('app.key'));
$signature = hash_hmac('sha256', json_encode($payload), $secret);

Verification Example (in N8N or receiving endpoint):

javascript
// Node.js example
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
const signature = req.headers['x-webhook-signature'];
const isValid = verifySignature(
  req.body,
  signature,
  process.env.WEBHOOK_SECRET
);

if (!isValid) {
  return res.status(403).send('Invalid signature');
}

2. Custom Headers

Every request includes identifying headers:

X-Webhook-Signature: <hmac-sha256-signature>
X-Webhook-Event: whatsapp.message.received
X-Webhook-Timestamp: 2026-01-01T10:38:48+00:00
X-Webhook-Format: n8n
User-Agent: whatsmark-Webhook/1.0

3. URL Validation

php
if ($enabled && filter_var($url, FILTER_VALIDATE_URL)) {
    // Only proceed if URL is valid
}

4. Duplicate Prevention

The controller checks for duplicate message processing:

php
$message_id = $payload['entry'][0]['changes'][0]['value']['messages'][0]['id'] ?? '';
if (!empty($message_id)) {
    $found = $this->checkMessageProcessed($message_id);
    if ($found) {
        whatsapp_log('Duplicate Message Detected', 'warning', ['message_id' => $message_id]);
        return;
    }
}

5. Tenant Isolation

All settings and data are tenant-specific:

php
$enabled = get_tenant_setting_by_tenant_id('whats-mark', 'enable_webhook_resend', '', $this->tenant_id);

Troubleshooting

Webhooks Not Being Forwarded

Check 1: Verify Settings

php
// Check if forwarding is enabled
get_tenant_setting_by_tenant_id('whats-mark', 'enable_webhook_resend', '', $tenant_id);

// Verify URL is configured
get_tenant_setting_by_tenant_id('whats-mark', 'whatsapp_data_resend_to', '', $tenant_id);

Check 2: Field Filtering

  • Ensure the event field is in the selected fields list
  • Check logs: "Webhook Forward Skipped - No enabled fields found"

Check 3: URL Validity

  • Must be a valid URL format
  • HTTPS recommended for production
  • Check for typos in configuration

N8N Not Receiving Data

Check 1: N8N Workflow Status

  • Ensure workflow is activated
  • Check webhook node configuration
  • Verify path matches configuration

Check 2: Firewall/Network

  • Check if N8N endpoint is accessible
  • Test with curl:
bash
curl -X POST https://your-n8n.com/webhook/test \
  -H "Content-Type: application/json" \
  -d '{"test": "data"}'

Check 3: Response Status

  • Check application logs for response status
  • Look for "Webhook Forwarded" log entries

Signature Verification Failing

Issue: Signature mismatch in receiving endpoint

Solution:

  1. Ensure secret matches on both sides
  2. Verify you're computing signature on the same payload
  3. Check that payload hasn't been modified in transit
  4. Use json_encode($payload) exactly as sent

Logs to Check

bash
# Check webhook forward logs
php artisan tinker
>>> \DB::table('whatsapp_logs')->where('title', 'like', '%Webhook Forward%')->latest()->get();

# Check for skipped webhooks
>>> \DB::table('whatsapp_logs')->where('title', 'Webhook Forward Skipped')->latest()->get();

# Check for errors
>>> \DB::table('whatsapp_logs')->where('title', 'Webhook Forward Error')->latest()->get();

Code References

Primary Files

  1. Controller Logic

    • File: app/Http/Controllers/Whatsapp/WhatsAppWebhookController.php
    • Method: forwardWebhookData() (lines 820-900)
    • Handles: Settings retrieval, field filtering, HTTP forwarding
  2. Transformer Logic

    • File: app/Transformers/WhatsAppWebhookN8NTransformer.php
    • Method: transform() (lines 1-315)
    • Handles: Event detection, data extraction, N8N formatting
  3. Configuration UI

    • File: resources/views/livewire/tenant/settings/whats-mark/web-hooks-settings.blade.php
    • Handles: User interface for webhook configuration
  4. Admin Configuration

    • File: resources/views/livewire/admin/whats-app/webhook-configuration.blade.php
    • Handles: Admin-level webhook settings
  • Modules/ApiWebhookManager/Config/config.php - Webhook config
  • Modules/ApiWebhookManager/Enums/WebhookEventType.php - Event type definitions
  • Modules/ApiWebhookManager/Services/WebhookService.php - Webhook service utilities

Configuration Keys

php
// Settings keys used:
'whats-mark.enable_webhook_resend'      // Boolean: Enable forwarding
'whats-mark.whatsapp_data_resend_to'    // String: Webhook URL
'whats-mark.webhook_resend_method'      // String: HTTP method (POST/GET)
'whats-mark.webhook_selected_fields'    // Array: Enabled event fields

Database Tables

sql
-- Settings stored in:
settings (tenant-specific settings)

-- Logs stored in:
whatsapp_logs (webhook forward logs)

Example N8N Workflow

Basic Workflow Structure

┌─────────────────┐
│  Webhook Node   │ (Trigger)
│  (POST)         │
└────────┬────────┘


┌─────────────────┐
│ Function Node   │ (Verify Signature)
│                 │
└────────┬────────┘


┌─────────────────┐
│  Switch Node    │ (Route by event.type)
│                 │
└────────┬────────┘

         ├─────► whatsapp.message.received
         │       └─► Save to DB / Send Notification

         ├─────► whatsapp.message.delivered
         │       └─► Update message status

         └─────► Other event types...

Signature Verification (Function Node)

javascript
// items[0].json contains the webhook payload
const payload = items[0].json;
const signature = $node['Webhook'].context['headers']['x-webhook-signature'];
const secret = 'your-secret-key-here'; // Or from environment

const crypto = require('crypto');
const expectedSignature = crypto
  .createHmac('sha256', secret)
  .update(JSON.stringify(payload))
  .digest('hex');

if (signature !== expectedSignature) {
  throw new Error('Invalid signature');
}

return items;

Event Routing (Switch Node)

javascript
// Route based on event type
return [
  payload.event.type === 'whatsapp.message.received' ? 0 : -1,
  payload.event.type === 'whatsapp.message.delivered' ? 0 : -1,
  payload.event.type === 'whatsapp.message.read' ? 0 : -1,
  // ... other event types
];

Best Practices

1. Selective Event Forwarding

  • Only forward events you actually need
  • Reduces bandwidth and processing overhead
  • Easier to debug and maintain

2. Error Handling

  • Implement retry logic in N8N for failed webhooks
  • Monitor webhook forward logs regularly
  • Set up alerts for high error rates

3. Security

  • Always use HTTPS endpoints
  • Implement signature verification
  • Rotate secrets periodically
  • Restrict webhook endpoint access

4. Performance

  • Use asynchronous processing in N8N when possible
  • Avoid long-running operations in webhook handlers
  • Consider queuing for heavy processing

5. Testing

  • Test webhook forwarding in development first
  • Use N8N's test webhook feature
  • Verify all event types you need
  • Check signature verification works

6. Monitoring

  • Log all webhook forwards
  • Track success/failure rates
  • Monitor response times
  • Set up alerts for issues

Migration Notes

From Previous Version (If Applicable)

If migrating from an older webhook system:

  1. Settings Migration: Old settings may need to be converted
  2. Event Field Mapping: Ensure field names match new structure
  3. Signature Method: Update signature verification if changed
  4. Testing: Thoroughly test all event types after migration

Support & References

Additional Documentation

Commits Reference

  • Main implementation: 74a9907c - "feat: Implement WhatsApp Webhook Transformer for N8N integration"
  • Enhancements: 984b8440 - "Enhance multi-tenancy support and improve webhook processing"
  • ApiWebhookManager Module - Generic webhook management
  • Multi-tenancy - Tenant-specific webhook configurations
  • WhatsApp Integration - Core WhatsApp Business API integration

Changelog

Version 1.6.0 (Current)

  • ✅ N8N format transformer implemented
  • ✅ Multi-tenant webhook forwarding
  • ✅ Signature verification
  • ✅ Selective event field filtering
  • ✅ Comprehensive logging
  • ✅ Admin and tenant configuration UI

Future Enhancements (Planned)

  • [ ] Webhook retry mechanism
  • [ ] Webhook forward queue for reliability
  • [ ] Webhook analytics dashboard
  • [ ] Custom transformation templates
  • [ ] Webhook event batching
  • [ ] Webhook rate limiting

Summary

The WhatsApp Webhook Forwarding feature provides a robust, secure, and flexible way to integrate WhatsApp events with external automation systems like N8N. With its standardized format, comprehensive event coverage, and tenant-specific configuration, it enables powerful automation workflows while maintaining security and multi-tenancy support.

Key Benefits:

  • 🚀 Easy integration with N8N and other webhook consumers
  • 🔒 Secure with signature verification
  • 🎯 Selective event forwarding for efficiency
  • 🏢 Multi-tenant support
  • 📊 Comprehensive logging and debugging
  • 🔧 Flexible configuration per tenant

For questions or issues, refer to the application logs or contact the development team.

© 2024 - Corbital Technologies. All rights reserved.