Welcome!
What did you do?
Environment
- Evolution API: v2.3.7 (Docker,
evoapicloud/evolution-api:latest)
- Connection type: WhatsApp Cloud API (Business, not Baileys/QR)
- Consumer: webhook → n8n
Summary
When a user starts a conversation by tapping "Send message" on a
Click-to-WhatsApp (CTWA) ad, Meta includes a referral object (with
ctwa_clid, source_id, etc.) inside messages[0] of the incoming
webhook payload. Evolution receives this correctly, but the object it
emits on messages.upsert drops the referral entirely: contextInfo
comes out undefined and there is no field carrying the ad attribution.
As a result, CTWA click IDs cannot be captured, breaking ad-conversion
attribution.
What Meta sends (inbound to Evolution — confirmed in logs)
"messages": [{
"referral": {
"source_url": "https://fb.me/XXXX",
"source_id": "1202XXXXXXXXXXXXX",
"source_type": "ad",
"headline": "...",
"body": "...",
"media_type": "image",
"image_url": "...",
"ctwa_clid": ""
},
"from": "",
"id": "wamid...",
"timestamp": "...",
"text": { "body": "Hola 👋 ..." },
"type": "text"
}]
What Evolution emits on messages.upsert (the bug)
{
"key": { "id": "wamid...", "remoteJid": "@s.whatsapp.net", "fromMe": false },
"pushName": "JL",
"message": { "conversation": "Hola 👋 ..." },
"contextInfo": undefined, // <-- referral lost
"messageType": "conversation",
...
}
What did you expect?
Expected
The referral data (at minimum ctwa_clid and source_id) should be
propagated to the emitted message object — e.g. inside contextInfo
and/or a dedicated adReferral field — so downstream consumers can
capture ad attribution.
Steps to reproduce
- Configure a WhatsApp Cloud API instance.
- Create a Click-to-WhatsApp ad in Meta Ads Manager.
- From a phone number that has never messaged this WABA before, tap
"Send message" on the ad and send the first message.
- Observe the inbound payload contains
messages[0].referral with
ctwa_clid, but the messages.upsert webhook has contextInfo: undefined and no referral data.
Note: ctwa_clid is only present on the first message from a given
user; subsequent messages don't carry it. Use a fresh number to test.
What did you observe instead of what you expected?
Looking at the section "What Evolution emits on messages.upsert (the bug)" — that JSON block is the observed behavior. But you're right to flag it: in a GitHub bug report the reviewer scans for a crisp "Expected vs Observed" pair, and mine buried the observed part inside a code block without a plain-language sentence stating it. Let me give you that sentence so the report reads cleanly.
Observed: The messages.upsert event fires with contextInfo: undefined and no adReferral (or any other) field carrying the referral. The ctwa_clid, source_id, and the rest of messages[0].referral that Meta sent are silently dropped — they never reach the webhook consumer, so ad-conversion attribution is impossible.
Expected: The referral data (at minimum ctwa_clid and source_id) should be propagated to the emitted object, so downstream consumers can capture the CTWA attribution.
You can drop those two lines in verbatim, right above the two JSON blocks, so a reviewer gets the contrast in one read before diving into the payloads.
One caveat worth repeating, since it affects whether "observed" is even still true: this was observed on v2.3.7. A later release changelog mentions work on the Cloud API sender-identification path in the same handler, so before you post, confirm the drop still happens on the current version — otherwise the observed behavior may already be fixed and the report would close as resolved.
Screenshots/Videos
No response
Which version of the API are you using?
2.3.7
Other environment specifications
No response
If applicable, paste the log output
No response
Additional Notes
Additional context
There is a second, related crash on the same handler: WhatsApp status
notifications (sent/read) enter messageHandle with a contacts
array that has no profile, causing
TypeError: Cannot read properties of undefined (reading 'name') at
e.contacts[0].profile.name. Guarding that access with optional
chaining (e.contacts[0]?.profile?.name) resolves it.
I've validated a local patch that (a) reads messages[0].referral and
attaches it as adReferral + merges into contextInfo, and (b) guards
the profile access above. Happy to open a PR against the current
main if the maintainers find it useful — I'd just want to confirm the
issue isn't already addressed in a version newer than 2.3.7 first.
Temporary workaround (until fixed upstream)
Since the bug is in the compiled dist/main.js bundle, I patched it
directly and bind-mounted the patched file over the container's copy,
so it survives container recreation (I'm on Docker Swarm, where
docker restart recreates the replica from the base image and loses
any in-container change).
What the patch does, right before the Cloud API's
sendDataWebhook("messages.upsert", ...) inside messageHandle:
- Reads
messages[0].referral and attaches it as an adReferral
object, also merging it into contextInfo:
if (referral) {
msg.adReferral = {
ctwaClid: referral.ctwa_clid ?? null,
sourceId: referral.source_id ?? null,
sourceUrl: referral.source_url ?? null,
sourceType:referral.source_type?? null,
headline: referral.headline ?? null,
body: referral.body ?? null,
mediaType: referral.media_type ?? null,
imageUrl: referral.image_url ?? null
};
msg.contextInfo = { ...(msg.contextInfo || {}), ...msg.adReferral };
}
- Guards the
profile access that crashes on status notifications:
e.contacts[0].profile.name → e.contacts[0]?.profile?.name
(same guard applied to the .profile.phone accesses further down).
Bind-mount (docker-compose / stack):
volumes:
- /opt/evolution-patch/main.js:/evolution/dist/main.js:ro
After redeploying, the messages.upsert payload now carries
data.adReferral.ctwaClid and data.contextInfo.ctwaClid, and the
status-notification TypeError is gone. Verified end-to-end against a
real CTWA ad click.
Note: this is a bundle-level patch, so it must be re-applied whenever
the image is updated. A proper fix in the TypeScript source
(whatsapp.business.service.ts) would be preferable — happy to open a
PR if useful.
Welcome!
What did you do?
Environment
evoapicloud/evolution-api:latest)Summary
When a user starts a conversation by tapping "Send message" on a
Click-to-WhatsApp (CTWA) ad, Meta includes a
referralobject (withctwa_clid,source_id, etc.) insidemessages[0]of the incomingwebhook payload. Evolution receives this correctly, but the object it
emits on
messages.upsertdrops the referral entirely:contextInfocomes out
undefinedand there is no field carrying the ad attribution.As a result, CTWA click IDs cannot be captured, breaking ad-conversion
attribution.
What Meta sends (inbound to Evolution — confirmed in logs)
What Evolution emits on messages.upsert (the bug)
{ "key": { "id": "wamid...", "remoteJid": "@s.whatsapp.net", "fromMe": false }, "pushName": "JL", "message": { "conversation": "Hola 👋 ..." }, "contextInfo": undefined, // <-- referral lost "messageType": "conversation", ... }What did you expect?
Expected
The referral data (at minimum
ctwa_clidandsource_id) should bepropagated to the emitted message object — e.g. inside
contextInfoand/or a dedicated
adReferralfield — so downstream consumers cancapture ad attribution.
Steps to reproduce
"Send message" on the ad and send the first message.
messages[0].referralwithctwa_clid, but themessages.upsertwebhook hascontextInfo: undefinedand no referral data.Note:
ctwa_clidis only present on the first message from a givenuser; subsequent messages don't carry it. Use a fresh number to test.
What did you observe instead of what you expected?
Looking at the section "What Evolution emits on messages.upsert (the bug)" — that JSON block is the observed behavior. But you're right to flag it: in a GitHub bug report the reviewer scans for a crisp "Expected vs Observed" pair, and mine buried the observed part inside a code block without a plain-language sentence stating it. Let me give you that sentence so the report reads cleanly.
Observed: The messages.upsert event fires with contextInfo: undefined and no adReferral (or any other) field carrying the referral. The ctwa_clid, source_id, and the rest of messages[0].referral that Meta sent are silently dropped — they never reach the webhook consumer, so ad-conversion attribution is impossible.
Expected: The referral data (at minimum ctwa_clid and source_id) should be propagated to the emitted object, so downstream consumers can capture the CTWA attribution.
You can drop those two lines in verbatim, right above the two JSON blocks, so a reviewer gets the contrast in one read before diving into the payloads.
One caveat worth repeating, since it affects whether "observed" is even still true: this was observed on v2.3.7. A later release changelog mentions work on the Cloud API sender-identification path in the same handler, so before you post, confirm the drop still happens on the current version — otherwise the observed behavior may already be fixed and the report would close as resolved.
Screenshots/Videos
No response
Which version of the API are you using?
2.3.7
Other environment specifications
No response
If applicable, paste the log output
No response
Additional Notes
Additional context
There is a second, related crash on the same handler: WhatsApp status
notifications (
sent/read) entermessageHandlewith acontactsarray that has no
profile, causingTypeError: Cannot read properties of undefined (reading 'name')ate.contacts[0].profile.name. Guarding that access with optionalchaining (
e.contacts[0]?.profile?.name) resolves it.I've validated a local patch that (a) reads
messages[0].referralandattaches it as
adReferral+ merges intocontextInfo, and (b) guardsthe
profileaccess above. Happy to open a PR against the currentmainif the maintainers find it useful — I'd just want to confirm theissue isn't already addressed in a version newer than 2.3.7 first.
Temporary workaround (until fixed upstream)
Since the bug is in the compiled
dist/main.jsbundle, I patched itdirectly and bind-mounted the patched file over the container's copy,
so it survives container recreation (I'm on Docker Swarm, where
docker restartrecreates the replica from the base image and losesany in-container change).
What the patch does, right before the Cloud API's
sendDataWebhook("messages.upsert", ...)insidemessageHandle:messages[0].referraland attaches it as anadReferralobject, also merging it into
contextInfo:profileaccess that crashes on status notifications:e.contacts[0].profile.name→e.contacts[0]?.profile?.name(same guard applied to the
.profile.phoneaccesses further down).Bind-mount (docker-compose / stack):
After redeploying, the
messages.upsertpayload now carriesdata.adReferral.ctwaClidanddata.contextInfo.ctwaClid, and thestatus-notification
TypeErroris gone. Verified end-to-end against areal CTWA ad click.
Note: this is a bundle-level patch, so it must be re-applied whenever
the image is updated. A proper fix in the TypeScript source
(
whatsapp.business.service.ts) would be preferable — happy to open aPR if useful.