From ffdf2d1c809ac59c6dc19626f687f7791c0acada Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Thu, 16 Apr 2026 21:03:08 +0300 Subject: [PATCH] fix: webhook message insert uses wrong schema field names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a WhatsApp message arrives, the webhook creates/updates the contact and conversation fine (so the conversation appears in the Inbox), but every message INSERT fails silently because the field names don't match the messages table schema: Webhook wrote Schema has ----------------- ---------- user_id (doesn't exist) whatsapp_message_id message_id direction: 'inbound' sender_type ('customer' | 'agent' | 'bot') message_type content_type media_type (doesn't exist) status: 'received' status ('sending'|'sent'|'delivered'|'read'|'failed') timestamp created_at The insert error was caught and logged with console.error but silently swallowed — the conversation update succeeded (its fields were right), so the Inbox showed the contact and last message text, but opening the thread showed zero messages. The 24-hour session timer also read "no customer messages ever" and immediately marked the session expired. Fixes in src/app/api/whatsapp/webhook/route.ts: - processMessage() — message insert now uses the correct schema columns: sender_type: 'customer' content_type: message.type message_id: message.id status: 'delivered' created_at: - handleStatusUpdate() — lookup uses message_id (not whatsapp_message_id) and no longer sets updated_at (column doesn't exist on messages). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/api/whatsapp/webhook/route.ts | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/app/api/whatsapp/webhook/route.ts b/src/app/api/whatsapp/webhook/route.ts index 55c1562..2b0071f 100644 --- a/src/app/api/whatsapp/webhook/route.ts +++ b/src/app/api/whatsapp/webhook/route.ts @@ -184,13 +184,13 @@ async function handleStatusUpdate(status: { timestamp: string recipient_id: string }) { + // The messages table schema uses `message_id` (not whatsapp_message_id) + // and has no `updated_at` column. Meta's status values (sent/delivered/ + // read/failed) already match our CHECK constraint. const { error } = await supabaseAdmin() .from('messages') - .update({ - status: status.status, - updated_at: new Date().toISOString(), - }) - .eq('whatsapp_message_id', status.id) + .update({ status: status.status }) + .eq('message_id', status.id) if (error) { console.error('Error updating message status:', error) @@ -227,18 +227,23 @@ async function processMessage( ) if (!conversation) return - // Insert message + // Insert message — field names MUST match the messages table schema + // (see supabase/migrations/001_initial_schema.sql): + // conversation_id, sender_type, content_type, content_text, + // media_url, template_name, message_id, status, created_at + // `mediaType` is intentionally unused — the schema has no media_type + // column; the MIME type is only used to construct the proxy URL during + // parseMessageContent. Silence the unused-var warning: + void mediaType const { error: msgError } = await supabaseAdmin().from('messages').insert({ conversation_id: conversation.id, - user_id: userId, - whatsapp_message_id: message.id, - direction: 'inbound', - message_type: message.type, + sender_type: 'customer', + content_type: message.type, content_text: contentText, media_url: mediaUrl, - media_type: mediaType, - status: 'received', - timestamp: new Date(parseInt(message.timestamp) * 1000).toISOString(), + message_id: message.id, + status: 'delivered', + created_at: new Date(parseInt(message.timestamp) * 1000).toISOString(), }) if (msgError) {