WasenderAPI

API Documentation

WasenderApi WhatsApp API

How To Receive Messages and Media From Wasenderapi

Getting Started

A developer's guide to receiving and processing real-time message events. This documentation details the flattened JSON payload, unified messageBody field, and handling for both Private and Group chats.

How to Handle Incoming WhatsApp Messages

When you get a new WhatsApp message, we send a POST request to your server (webhook). Inside is a JSON payload with all the message details.

The Message Payload

The JSON structure has been updated. The data.messages field is now a single object (not an array) containing the normalized key, the unified messageBody, and the raw message content.

{
  "event": "messages.received",
  "timestamp": 1633456789,
  "data": {
    "messages": {
      "key": {
        "id": "3EB0X123456789",
        "fromMe": false,
        "remoteJid": "123456789@lid", 
        "cleanedSenderPn": "5551234567",
        "senderLid": "123456789@lid"
      },
      "messageBody": "Hello! This is a test.",
      "message": {
        "conversation": "Hello! This is a test."
      }
    }
  }
}

Key Fields Explained:

  • key.cleanedSenderPn: (Recommended) The sender's phone number in private chats. Use this for your database or logic.
  • key.cleanedParticipantPn: (Recommended) The sender's phone number in group chats.
  • key.remoteJid: The unique ID of the chat.
    ⚠️ Important: Do not rely on remoteJid to be a phone number. It can often be a LID (Linked ID, ending in @lid). Always use the "cleaned" fields if you need the specific phone number.
  • messageBody: The unified text content of the message. Whether it's a text message, an image caption, or a reply, the text will always be here.

Reading the Message Content

1. The Easy Way (Text)

You no longer need to check multiple fields (like conversation vs extendedTextMessage). Just use data.messages.messageBody.

2. Media Messages

For media, look inside the raw data.messages.message object for keys like imageMessage, videoMessage, or audioMessage.

How to Decrypt Media Files

Important Update: You no longer have to decrypt the media yourself if you don’t want to. We now provide a secure API endpoint that does it for you automatically: Decrypt Media File API.

If you choose to decrypt manually, use the code examples below.

Code Examples

<?php

declare(strict_types=1);

// The directory to save downloaded media files.
// Make sure this directory exists and your web server can write to it.
define('DOWNLOAD_DIR', __DIR__ . '/downloads');

/**
 * A simple logging function for demonstration.
 * In a real application, you would use a proper logger like Monolog.
 */
function logMessage(string $message): void
{
    $timestamp = date('Y-m-d H:i:s');
    file_put_contents('webhook.log', "[$timestamp] $message\n", FILE_APPEND);
}

/**
 * Finds the first available media object and its type from the message.
 */
function findMediaInfo(array $messageObject): ?array
{
    $mediaKeys = [
        'imageMessage'    => 'image',
        'videoMessage'    => 'video',
        'audioMessage'    => 'audio',
        'documentMessage' => 'document',
        'stickerMessage'  => 'sticker',
    ];

    foreach ($mediaKeys as $key => $type) {
        if (isset($messageObject[$key])) {
            return [$messageObject[$key], $type];
        }
    }
    return null;
}

/**
 * Downloads a file from a URL.
 */
function downloadFile(string $url)
{
    $context = stream_context_create(['http' => ['follow_location' => true]]);
    return file_get_contents($url, false, $context);
}

/**
 * Derives the decryption keys using HKDF.
 */
function getDecryptionKeys(string $mediaKey, string $mediaType): string
{
    $info = match ($mediaType) {
        'image', 'sticker' => 'WhatsApp Image Keys',
        'video'           => 'WhatsApp Video Keys',
        'audio'           => 'WhatsApp Audio Keys',
        'document'        => 'WhatsApp Document Keys',
        default           => throw new Exception("Invalid media type: {$mediaType}"),
    };
    
    return hash_hkdf('sha256', base64_decode($mediaKey), 112, $info, '');
}

/**
 * Main function to decrypt and save a media file.
 */
function handleMediaDecryption(array $mediaInfo, string $mediaType, string $messageId): void
{
    $url = $mediaInfo['url'] ?? null;
    $mediaKey = $mediaInfo['mediaKey'] ?? null;
    
    if (!$url || !$mediaKey) {
        throw new Exception("Media object is missing url or mediaKey.");
    }

    $encryptedData = downloadFile($url);
    if (!$encryptedData) {
        throw new Exception("Failed to download media from URL: {$url}");
    }

    $keys = getDecryptionKeys($mediaKey, $mediaType);
    $iv = substr($keys, 0, 16);
    $cipherKey = substr($keys, 16, 32);
    $ciphertext = substr($encryptedData, 0, -10);

    $decryptedData = openssl_decrypt($ciphertext, 'aes-256-cbc', $cipherKey, OPENSSL_RAW_DATA, $iv);
    if ($decryptedData === false) {
        throw new Exception('Failed to decrypt media.');
    }

    if (!is_dir(DOWNLOAD_DIR)) {
        mkdir(DOWNLOAD_DIR, 0755, true);
    }
    $mimeType = $mediaInfo['mimetype'] ?? 'application/octet-stream';
    $extension = explode('/', $mimeType)[1] ?? 'bin';
    $filename = $mediaInfo['fileName'] ?? "{$messageId}.{$extension}";
    $outputPath = DOWNLOAD_DIR . '/' . basename($filename);
    
    file_put_contents($outputPath, $decryptedData);
    logMessage("Successfully decrypted and saved media to: {$outputPath}");
}

// --- MAIN WEBHOOK PROCESSING LOGIC ---

$jsonPayload = file_get_contents('php://input');
$payload = json_decode($jsonPayload, true);

// 1. Access data.messages (Direct Object Access)
$messageData = $payload['data']['messages'] ?? null;

if (!$messageData) {
    logMessage('Webhook received but no message data found.');
    http_response_code(200);
    exit();
}

$key = $messageData['key'] ?? [];
$messageId = $key['id'] ?? 'unknown_id';

// 2. Identify Sender (Group vs Private)
$sender = $key['cleanedParticipantPn'] ?? $key['cleanedSenderPn'] ?? $key['remoteJid'];

// 3. Get Unified Text Body
$messageContent = $messageData['messageBody'] ?? '';

logMessage("Processing message from {$sender}. ID: {$messageId}");

if (!empty($messageContent)) {
    logMessage("Text: {$messageContent}");
    // TODO: Save text message to your database here.
}

// 4. Handle Media
$mediaInfo = findMediaInfo($messageData['message'] ?? []);
if ($mediaInfo) {
    try {
        logMessage("Media found. Type: {$mediaInfo[1]}. Attempting to decrypt...");
        handleMediaDecryption($mediaInfo[0], $mediaInfo[1], $messageId);
    } catch (Exception $e) {
        logMessage("ERROR processing media: " . $e->getMessage());
    }
}

http_response_code(200);
logMessage("--- Finished processing webhook ---");