Why does my Won event fire twice, and how do I stop duplicate sends?
It fires twice because your setup treats every move into Won as a new sale, and deals move in and out of stages more than people admit. You stop it by giving every sale one permanent ID and refusing to send the same ID again.
Picture a deal on a busy sales team. A rep drags it to Won on Tuesday. On Wednesday the buyer asks for a change, so the rep drags it back to Negotiation to keep the paperwork tidy. On Thursday it goes to Won again. If your automation says send an event whenever a deal enters Won, you have just told the ad platform about two sales. Multiply that by a team that likes to tidy up, and your reported revenue quietly inflates.
The ad platform is not the one making the mistake. It only knows what it is told. The mistake lives in the trigger, and the fix belongs there too.
There are three common ways duplicates sneak in. The first is the stage bounce I just described. The second is retries: an automation tool times out, tries again, and the receiving side sees two requests. The third is two systems both sending the same sale, for example a browser pixel and a server feed, or two workflows built by two different people six months apart.
For the last case, the standard tool is an event ID. Meta lets you send the same ID from the browser and from the server, and it keeps one. That is designed for the pixel plus server pairing. It does not automatically help with a stage bounce, because you have to make sure the two sends carry the same ID in the first place.
So build the ID from the deal, not from the moment. Use the deal or opportunity ID from your CRM, plus the event name. Won on deal 4471 always becomes the same string, no matter how many times the rep drags it around. That way a second send is recognisably the same sale.
Second, add a guard in the workflow itself. A simple version is a field on the deal called something like Won sent, set to yes the first time the event goes out. The workflow checks the field before sending. If it already says yes, stop. It is not glamorous, but it is the most reliable duplicate stopper I know because it does not depend on any downstream system being clever.
Third, decide what should happen when a deal genuinely changes. If the value goes from 4,000 to 3,500 after a renegotiation, do you want a second event? Usually not, and most ad platforms would rather have one clean number. Pick a rule, write it down, and apply it everywhere.
Here is a made-up example, invented for illustration. A three-person renovation company sends Won events to Meta and Google from their CRM. Their reported revenue for March was 212,000, but the bank showed 148,000. The gap turned out to be 16 deals moved back and forth during a pipeline clean-up, each counted twice or three times. After they added the Won sent flag, the next month matched to within a few percent, and the remaining gap was refunds.
On refunds, be aware that reversing a sale is a separate problem. Some platforms do not let you take a reported conversion back, so prevention matters far more than cleanup. Check the current documentation for each platform you send to.
For webhook setups, look for an order or deal ID field and always send one. On DataCops sales webhooks, the same order_id sent twice counts once, which is exactly the safety net you want when a retry happens. It does not replace a good trigger, though. It just means a retry cannot hurt you.
Do your deals bounce between stages a lot? If so, tell me how your automation is triggered and I will suggest where to put the guard.
More on this: CRM webhook offline conversions, and the complete guide to offline conversion tracking.