How to distinguish "fromMe" messages sent via API vs. WhatsApp Web in Evolution API v2.x?

Hi everyone,

I’m building an automation in n8n using Evolution API v2.3.7 to handle lead qualification for a law firm. I have a logic that silences the AI Agent for 30 minutes if the lawyer intervenes manually (sending a message from her phone or WhatsApp Web).

The Setup:

  • Trigger: Webhook listening to messages.upsert.

  • Database: Google Sheets/Postgres tracks the last_human_interaction timestamp.

  • Logic: If fromMe is true, I update the timestamp. If a new message arrives and the last interaction was < 30 mins ago, the bot remains silent.

The Issue: The Evolution API sends a webhook for every message, including the ones sent by the n8n bot itself via the API.

  • Bot messages (via API) arrive as: fromMe: true and source: web.

  • Manual messages (Lawyer via WhatsApp Web) arrive as: fromMe: true and source: web.

Because both have the exact same source, the bot records its own responses as “Human Interactions,” triggering its own silence logic and effectively “suiciding” after the first reply.

The Question: Is there a reliable way in the messages.upsert payload to distinguish between a message sent specifically via the API and a message sent manually from the WhatsApp Web interface when the source field is identical?

I’ve tried filtering by source, but since the lawyer uses WhatsApp Web, it’s not a unique identifier.

Environment:

  • Evolution API Version: 2.3.7

  • n8n Version: 2.14.2

  • Instance Type: Typebot/n8n integration.

I have disabled the nodes related to the solution because they were creating problems.

Has anyone found a specific metadata field or a header that identifies API-originated messages?

Thanks in advance!

Hi Since you can’t rely on the source field, the best workaround is to tag your bot’s outgoing messages with a custom identifier before sending them. You can add a unique prefix or invisible character to the message body, or store the message ID of every message your bot sends in your database. Then when the webhook fires with fromMe: true, check if that message ID already exists in your records… if it does, it’s a bot message and you skip the timestamp update. If it doesn’t exist, it’s the lawyer and you trigger the silence logic.

2 Likes

thank you, this really worked, i thought about that possibily, but your comment made me implement something similar, following that logic. thank you, have a nice day.

1 Like

Glad it worked out, good luck with the rest of the build :+1:

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.