Skip to content

fix(baileys): preserve Meta Ads webhook messages - #2673

Draft
cardosolucass96 wants to merge 3 commits into
evolution-foundation:mainfrom
cardosolucass96:agent/campaign-webhook
Draft

fix(baileys): preserve Meta Ads webhook messages#2673
cardosolucass96 wants to merge 3 commits into
evolution-foundation:mainfrom
cardosolucass96:agent/campaign-webhook

Conversation

@cardosolucass96

@cardosolucass96 cardosolucass96 commented Aug 3, 2026

Copy link
Copy Markdown

What changed

  • Keep incoming Meta Ads/campaign messages flowing through messages.upsert even when the phone number cannot be resolved.
  • Preserve the original LID/alternate JID information in the webhook payload.
  • Add an explicit campaign object with Meta/CTWA identifiers and ad reply metadata.
  • Isolate per-message processing failures so one message cannot abort the rest of an upsert batch.

Why

Meta Ads conversations can arrive through Baileys with an @lid JID and without a phone number or remoteJidAlt. The previous processing path could discard the message before the webhook reached the CRM workflow.

Validation

  • npm run lint:check
  • npm run build
  • git diff --check

This PR does not add payload persistence or a retry queue.

Summary by Sourcery

Improve WhatsApp Baileys message upsert handling to reliably surface Meta Ads conversations and harden per-message processing against failures.

New Features:

  • Expose a structured campaign object on incoming WhatsApp messages, including Meta/CTWA identifiers and ad reply metadata.
  • Include original LID-based JIDs alongside phone-number JIDs in webhook payloads for better downstream identification.

Bug Fixes:

  • Ensure Meta Ads messages using LID JIDs are delivered via messages.upsert even when the phone number cannot be immediately resolved.
  • Prevent a single failing message in an upsert batch from aborting processing of subsequent messages.
  • Attempt placeholder resend when decryption-related stub errors are detected, with proper error logging if the resend request fails.

Enhancements:

  • Add a phone-number resolution strategy that leverages alt JIDs, LID mappings, and cached WhatsApp lookups to enrich webhook payloads.
  • Adjust webhook emission ordering so Meta Ads messages are dispatched before database, media, and chatbot processing that might otherwise fail.

@sourcery-ai

sourcery-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors Baileys WhatsApp message upsert handling to preserve Meta Ads/CTWA webhook messages (even without resolved phone numbers), enrich webhook payloads with campaign metadata and LID info, and isolate per-message failures within batches while adding better error handling and media/webhook processing logic.

Sequence diagram for Meta Ads message upsert and webhook preservation

sequenceDiagram
  participant BaileysStartupService
  participant PhoneResolver as resolvePhoneNumber
  participant Webhook as sendDataWebhook
  participant DB as prismaRepository
  participant S3 as s3Service
  participant Chatbot as chatbotController

  BaileysStartupService->>BaileysStartupService: prepareMessage(received)
  BaileysStartupService->>BaileysStartupService: getCampaignMetadata(messageRaw.contextInfo)
  BaileysStartupService->>PhoneResolver: resolvePhoneNumber(messageRaw.key)
  PhoneResolver-->>BaileysStartupService: phoneNumber or null
  BaileysStartupService->>BaileysStartupService: createWebhookMessage()
  BaileysStartupService->>Webhook: sendDataWebhook(Events.MESSAGES_UPSERT, webhookMessage)

  alt isMetaAdsMessage
    Note over BaileysStartupService,Webhook: Webhook sent early, before DB/media/chatbot
  else nonMetaAdsMessage
    BaileysStartupService->>Webhook: sendDataWebhook(Events.MESSAGES_UPSERT, webhookMessage)
  end

  BaileysStartupService->>DB: message.create(messageData)
  BaileysStartupService->>S3: uploadFile(fullName, buffer, size)
  BaileysStartupService->>DB: media.create({ messageId, instanceId, type, fileName })
  BaileysStartupService->>Chatbot: emit({ instance, remoteJid, msg, pushName })
  BaileysStartupService->>Webhook: sendDataWebhook(Events.CONTACTS_UPDATE | CONTACTS_UPSERT, contactRaw)
Loading

Sequence diagram for per-message error isolation and placeholder resend

sequenceDiagram
  participant BaileysStartupService
  participant Client as client
  participant Logger as logger

  loop for each received in messages
    BaileysStartupService->>BaileysStartupService: [per-message try]
    alt messageStubParameters indicate decryption error
      BaileysStartupService->>Client: requestPlaceholderResend(received.key)
      Client-->>BaileysStartupService: resendId
      BaileysStartupService->>Logger: warn("requested resend", keyId, resendId)
      BaileysStartupService-->>BaileysStartupService: continue to next message
    else normal processing
      BaileysStartupService->>BaileysStartupService: full message handling (DB, media, webhook, chatbot)
    end
    BaileysStartupService->>Logger: error on per-message failure
  end

  BaileysStartupService->>Logger: error on outer handler failure
Loading

File-Level Changes

Change Details Files
Wrap per-message processing in its own try/catch and improve error handling so one failing message does not abort the entire messages.upsert batch, while enhancing decryption and placeholder resend behavior.
  • Introduce an inner try/catch around each received message in the messages loop and log structured errors with keyId and remoteJid
  • On messageStubParameters indicating decryption/session errors, attempt requestPlaceholderResend and log success/failure instead of just ignoring the message
  • Ensure edited-message handling, poll processing, DB writes, media upload, webhooks, chatbot emission, and contact upsert are all protected by the per-message try/catch
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Ensure Meta Ads (CTWA) messages are preserved and properly represented in webhooks, including LID JIDs, campaign metadata, and optional phone-number resolution from multiple sources.
  • Add prepareMessage-derived originalRemoteJid capture and detect Meta Ads messages via new getCampaignMetadata using contextInfo/externalAdReply/ctwa signals
  • Introduce resolvePhoneNumber that tries remoteJidAlt/participantAlt/remoteJid/participant, LID mappings via signalRepository, and cached on-whatsapp mappings, using normalizePhoneNumberFromJid
  • Build webhook payload via createWebhookMessage that preserves remoteJidLid, sets phoneNumber/phoneJid when resolvable, optionally remaps remoteJid to s.whatsapp.net, and attaches campaign plus deserialized message/contextInfo
  • Send webhook early for Meta Ads messages before DB/media/chatbot work, and avoid mutating remoteJid from @lid to alt before emitting webhooks
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Refine poll update processing and JID candidate generation to better handle LID/alternate JID scenarios when decrypting poll votes and computing poll updates.
  • Preserve and reuse messageRaw for pollUpdateMessage processing and keep decryption flow intact while adjusting candidate JID lists to include participantAlt and remoteJidAlt
  • Maintain computation of selectedOptionNames, pollUpdates, and assignment back into messageRaw while fitting into the new per-message processing structure
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Adjust media handling, S3 upload, webhook base64 conversion, and database updates to work within the refactored message processing flow and ensure Meta Ads messages are not blocked by downstream failures.
  • Keep media detection (image/video/sticker/document/ptv/audio) and S3 upload logic but ensure early returns or failures don’t stop other messages in the batch
  • Maintain webhook base64 download/retry logic for media while running inside the per-message try/catch and logging conversion errors
  • Preserve unread/read status updates, cache usage, and message/media DB records while integrating with the new webhook sending order
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Enhance contact and WhatsApp cache upsert logic to support LID-based addressing and ensure contacts/webhooks are still emitted correctly under the new flow.
  • Keep contact lookup and contactRaw construction but add saveOnWhatsappCache calls using addressingMode and remoteJidAlt to store LID mappings
  • Preserve distinction between CONTACTS_UPDATE vs CONTACTS_UPSERT webhooks and database upsert logic while fitting them into the refactored loop
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant