Cash-on-delivery conversions
Why a cash-on-delivery order reaches Meta, TikTok, Google or Microsoft as a Purchase only once the courier's cash is settled, and how to read each status.
What it is
When a shopper places a cash-on-delivery (COD) order, nobody has paid yet. Many such parcels are refused at the door, or the phone number is fake. If your shop tells Meta "Purchase" at checkout, Meta believes every order is a sale. It then finds you more people who order and refuse.
The app does it differently. Its server sends a Purchase to the ad platform only when the order is really paid:
- Cash on delivery: when the courier's cash for the parcel is settled against the invoice, or when a person approves the parcel in the cash-on-delivery review.
- Prepaid orders: when the order is paid.
Refused, returned, cancelled or fake orders are never sent as Purchase.
Note Example: in one week a Bangladesh shop gets 200 COD orders from Meta ads, worth BDT 260,000. 46 are refused or returned. The shop's old set-up told Meta "200 purchases, BDT 260,000". With the app, Meta hears "154 purchases, BDT 198,000", one by one as the courier's cash settles. Meta now learns from people who actually pay.
Who it is for
- Shops that sell with cash on delivery through a courier (Steadfast, Pathao, RedX and others)
- Shops that sell prepaid and want refunds and cancellations reflected
- Anyone who runs conversion campaigns that optimise for Purchase
Before you start#
- Order attribution is set up and the plugin captures orders. See Set up order attribution.
- The ad account is connected on the Ad platforms tab, and the platform is set up under Server conversions (see the Meta, Google and TikTok and Microsoft guides).
- Your COD parcels are booked with a courier connection, so the courier's report can settle the invoice. See Cash-on-delivery settlement.
- You need the Edit right on Integrations to change the settings.
Step by step#
- Open Integrations › CFI Order Attribution and the Ad platforms tab.
- Under Server conversions, choose Set up on the platform, for example Meta.
- Tick Send conversions to Meta.
- Under Events, keep Purchase once paid (cash on delivery: when the courier’s cash is settled) ticked. Keep Checkout started (InitiateCheckout, the same event id as the browser) ticked if you use the Meta Pixel. For Google, also keep Corrections (refunds restated, returns and cancellations retracted).
- Choose Value sent: Net revenue (ex tax, after refunds) (the usual choice) or Cash collected. Profit (after cost of goods, shipping and fees) is offered only to people who may see margins, and needs the box I understand the platform will see my profit per order (my margins).
- Leave Limited Data Use (US state privacy laws) on Automatic (US buyers in covered states).
- Enter the platform's test code, choose Save, and follow one test order to settlement.
- Clear the test code and choose Save.
How it works#
| What happens to the order | What the platform receives |
|---|---|
| Shopper starts checkout (with consent) | InitiateCheckout with the same event id as the browser pixel, so it is counted once |
| Order placed on the thank-you page (Meta, with the pixel from the plugin) | OrderPlaced, a custom event, never Purchase |
| Courier delivers; cash settled to the invoice | Purchase (TikTok: CompletePayment), dated at the order time |
| Paid more than 7 days after the order | Meta: LateDelivery custom event instead of Purchase (Meta and TikTok accept events at most 7 days old) |
| Partial refund or return after Purchase | Google: a restatement with the new value |
| Refused, returned, cancelled, voided or written off after upload | Google: a retraction. Meta and TikTok have no "undo": the order is kept for the exclusion audience |
| Refused before anything was sent | Nothing is sent |
The job runs every 10 minutes. Only web orders count: an order with no attribution record (a counter sale or a typed-in order) is not a website conversion.
Only orders whose buyer gave marketing consent are sent. Denied, unknown, Global Privacy Control, orders captured only by WooCommerce's own attribution, and orders from the old plugin 1.x are never sent. Contact details are normalised and hashed before they leave (a Bangladesh number 01711… becomes 8801711… and is then hashed).
Daily use#
A COD shop in Bangladesh
Monday: 40 Meta orders. Tuesday to Thursday: the courier delivers 31, two are refused, seven are still out. Each delivered parcel settles when the courier confirms the cash. Within 10 minutes of each settlement, a Purchase goes to Meta. Open Recent conversions on Friday: 31 Sent, and the two refusals listed as Kept for the exclusion audience (this platform has no negative event). The seven still out show nothing yet.
A prepaid store in the EU
Orders are paid at checkout, so Purchase goes out when the order is paid. Shoppers who refused cookies are listed as Skipped: no marketing consent. A customer returns one item: Google receives a restatement with the lower value.
Slow deliveries
A parcel to a remote area takes nine days. It is paid on day nine, after Meta's 7-day limit. Meta receives LateDelivery (counted, but not used for optimising), never a stale Purchase.
What you see#
Recent conversions (example):
| When | Order | Event | Value | Status |
|---|---|---|---|---|
| 12 Nov 10:20 | SO-1043 | Purchase | BDT 1,450 | Sent |
| 12 Nov 10:20 | SO-1047 | Purchase | BDT 990 | Skipped · Skipped: no marketing consent |
| 12 Nov 09:50 | SO-1031 | Purchase | BDT 2,100 | Waiting · 2 tries so far |
| 11 Nov 18:00 | SO-1012 | — | BDT 1,200 | Skipped · Kept for the exclusion audience (this platform has no negative event) |
| 11 Nov 17:30 | SO-1001 | LateDelivery | BDT 1,800 | Sent · Late delivery (paid after the 7-day limit: sent as LateDelivery, never as Purchase) |
Above the list: Last 7 days: 31 sent, 0 failed, 12 skipped.
On the platform's side, Purchases appear on the day of the order (the event time is the order time), a few days after the order was placed. Expect a lag in the ad platform's reports for COD campaigns.
Good practice#
- Optimise ad sets on Purchase. If you need more signal while a campaign learns, Meta's OrderPlaced can be used as a custom conversion for early learning.
- Switch off any browser Purchase from another plugin, or every order counts twice.
- Settle COD parcels promptly. The sooner cash is settled, the sooner the platform learns.
- Use Net revenue as the value. Send profit only if you accept that the platform sees your margins.
- Make phone numbers reliable (the plugin's Require a valid phone number): it cuts refusals and improves matching.
Troubleshooting#
| What you see | Why | What to do |
|---|---|---|
| Connect the account above first — conversions use its sign-in. | No ad account connected for this platform | Connect the account on the same tab |
| Orders don’t carry a consent decision yet (database update pending), so nothing can be sent. | Your business is waiting for an update | Nothing to do; ask support if it stays |
| Mostly Skipped: no marketing consent | Shoppers refused cookies, or the banner does not tell the plugin | Check the banner works with the plugin; refusals are correct |
| Skipped: older than the platform accepts | Paid after the platform's time limit | Expected for very late payments |
| No ad click or customer contact on the order | Nothing to match the buyer with | Expected for some orders |
| Failed with the platform's message | Token, ids or data refused | Read the message; check ids and token; send a test |
| Purchases sent but the platform shows double | Another plugin sends a browser Purchase | Switch Purchase off in that plugin |
| No Purchase for a delivered parcel | The invoice is not settled yet, or the parcel is in review | Settle it, or decide it in Approvals › Delivered parcels (COD) |
Limits and what is not there yet#
- Built and contract-tested against each vendor's documentation; not yet proven on a real ad account.
- Meta and TikTok have no negative event, so refused orders cannot be "taken back" there. They are kept for the exclusion audience.
- Microsoft offline conversions need a Microsoft click id on the order, so most orders without one are not sent there.
- Today a refused token can make waiting events fail after their retries. After the 2026-11 update, a refused token pauses the platform and keeps its events waiting until you reconnect.
- Orders started in WhatsApp or Messenger chats are not yet attributed to click-to-chat ads. This is planned.
Questions#
Why not send Purchase at checkout like other plugins?
Because for cash on delivery, checkout is not a sale. Sending it teaches the platform to find people who order and refuse.
Will my ad platform show fewer purchases than before?
Usually yes, and closer to the truth. The difference is mostly refusals and returns.
Can I see what was sent for one order?
Yes. Recent conversions lists each event with its status, value and the platform's message.
What happens if I switch a platform off?
Nothing more is sent to it. Events already sent stay on the platform.
