Skip to content

Message log

The package can store every message you send and receive in your database, and keep each message's status up to date from webhooks. It's a starting point for conversation history, support tools and delivery reports.

Setup

Publish and run the migration, then turn the log on:

bash
php artisan vendor:publish --tag=whatsapp-migrations
php artisan migrate
dotenv
WHATSAPP_MESSAGE_LOG=true

To store messages on a different database connection, set WHATSAPP_MESSAGE_LOG_CONNECTION before migrating.

What's stored

Messages are stored in the whatsapp_messages table and available through the Crenspire\Whatsapp\Models\Message model.

ColumnContains
wamidThe WhatsApp message ID (empty for sends the API rejected)
directioninbound or outbound
phoneThe customer's number, digits only
phone_number_id, tenantWhich of your numbers sent or received it
typetext, template, image, interactive and so on
bodyThe text, caption, button title or template name
payloadThe full message
statusreceived, accepted, sent, delivered, read or failed
errorsError details for failed messages
reply_toThe ID of the message it replies to
sent_at, delivered_at, read_at, failed_atWhen each status was reported

Statuses only move forward, so a delivery receipt that arrives after a read receipt doesn't undo it. Duplicate webhook deliveries don't create duplicate rows.

Querying

php
use Crenspire\Whatsapp\Models\Message;

// A conversation with one customer, oldest first
$conversation = Message::query()->conversation('15551234567')->get();

$lines = $conversation->map(fn (Message $message) => sprintf(
    '%s: %s (%s)',
    $message->isInbound() ? 'Customer' : 'You',
    $message->body,
    $message->status,
));

// Messages that failed to deliver today
$failed = Message::query()->outbound()
    ->where('status', 'failed')
    ->whereDate('created_at', today())
    ->get();

// Messages the customer hasn't read after a day
$unread = Message::query()->outbound()
    ->whereNotNull('delivered_at')
    ->whereNull('read_at')
    ->where('created_at', '<', now()->subDay())
    ->get();

Reliability

Writing to the log never interrupts sending. If a database write fails, the error is reported to your exception handler and the message is still sent, because an exception at that point could make your code retry a message that was already delivered.

Status updates for messages that weren't logged, such as messages sent before the log was turned on, are ignored.

Released under the MIT License. Not affiliated with Meta or WhatsApp.