With cash on delivery, a purchase at checkout is not a sale yet. Some orders are fake, some are cancelled on the confirmation call, and some come back with the courier. If your purchase tags fire at checkout, the ad platforms learn from all of them, and they go looking for more people who order and never pay.
COD Order Protection holds a cash-on-delivery purchase in Taglinko when it happens. When you confirm the order, Taglinko sends it back into your server container as a purchase with the original order time and the original ad click, and your own tags send it on. Orders you reject are never sent. It works on every plan.
How it works
- Your web container sends the GA4 purchase as usual, with one extra parameter,
cod_hold, that is true for cash-on-delivery orders. - In your server container, the Taglinko COD Hold tag sends that purchase to Taglinko. Your other purchase tags skip it (an exception), so nothing reaches the ad platforms yet.
- The order waits on the COD Order Protection page of your container. You confirm or reject it there, or your shop does it for you (WooCommerce or a webhook, see below).
- A confirmed order goes back into your server container through the Taglinko COD Client, as a
purchaseevent withis_delayedset to true. Your Meta, GA4, Google Ads and TikTok tags fire on it with the order's own ID, value, items, customer details and click IDs.
If Taglinko cannot hold an order (for example while Taglinko is down, or after you switch the power-up off), the hold tag sends the purchase straight back into your container through the same client, so your tags fire at once as they did before. You do not lose a sale because of Taglinko.
Set it up
You need a server container on Taglinko with a GA4 client, and a web container that sends the GA4 purchase event to it.
1. Switch it on
Open your container, go to Power-Ups and turn on COD Order Protection. Your container restarts once.
Do this before step 2. Until the power-up has been on once, the hold tag does not know where to send an order, so it cannot hold it or pass it on. With the exceptions from step 2 already on your purchase tags, a COD purchase would then go nowhere.
2. Server container
The quickest way is the ready-made container from the power-up: in your server container open Admin, Import Container, choose taglinko-cod-server-container.json, pick your workspace and Merge (rename conflicting items). It adds:
- the two templates, Taglinko COD Hold (tag) and Taglinko COD Client (client),
- a Taglinko COD Client client, which answers on
/taglinko-cod, - a Taglinko COD Hold tag with the trigger Taglinko - COD purchase to hold (Client Name is GA4, Event Name is purchase,
cod_holdis true), - a trigger Taglinko - COD confirmed purchase (Client Name is Taglinko COD Client, Event Name is purchase).
Then, on every other purchase tag in the server container (Meta Conversions API, GA4, Google Ads, TikTok and so on):
- add Taglinko - COD purchase to hold as an exception, so the tag skips a COD order at checkout;
- add Taglinko - COD confirmed purchase as a second firing trigger, so the tag sends the order when it is confirmed.
If you would rather build it by hand: download both templates from the power-up, import them under Templates (the three-dot menu, Import), and create the client, tag and triggers as listed above.
3. Web container
Import taglinko-cod-web-container.json the same way (Merge). It adds a variable Taglinko - payment type (the data layer value ecommerce.payment_type), a variable Taglinko - cod_hold (true for cod, cash_on_delivery or cash on delivery) and a trigger Taglinko - COD purchase. Then:
- In your GA4 purchase event tag, add two event parameters:
cod_holdwith the value{{Taglinko - cod_hold}}, andpayment_typewith{{Taglinko - payment type}}. - On every browser purchase tag that goes straight to an ad platform (Meta Pixel, Google Ads, TikTok Pixel), add Taglinko - COD purchase as an exception. Otherwise the browser still reports the order at checkout.
If your data layer keeps the payment method under another name, change Taglinko - payment type to read it. If your shop sends something other than cod for cash on delivery, add it to the list in Taglinko - cod_hold and in the hold tag's Payment methods field.
4. Publish both containers
Then test, below, before you rely on it.
Confirm or reject orders
On the COD Order Protection page
Pending orders show the order ID, value, customer name, phone and city, and whether the order came with a Google click or a Meta browser ID. Approve & fire sends the order now; Reject drops it for good. You can tick several and do them at once. If other orders came from the same phone number, the row says so, which helps spot fake orders.
From WooCommerce (no plugin)
- On the power-up, under Confirm orders automatically, copy the Delivery URL and the Webhook secret.
- In WordPress open WooCommerce, Settings, Advanced, Webhooks, Add webhook. Status Active, Topic Order updated, API version WP REST API Integration v3, and paste the Delivery URL and the secret (into Secret). Save.
By default an order is sent on when its status becomes Completed, because WooCommerce marks every cash-on-delivery order Processing as soon as it is placed. If your shop only moves an order to Processing after the confirmation call, choose Processing on the power-up. Cancelled, Refunded and Failed reject the order. Taglinko matches the order by its number or its ID, so it works whichever one your data layer sends as the transaction ID.
From any other shop, n8n or a CRM
POST JSON to the Webhook URL with the secret in the X-Taglinko-Webhook-Secret header:
curl -X POST https://api.taglinko.com/taglinko-api/cod/YOUR_WEBHOOK_ID \
-H 'Content-Type: application/json' \
-H 'X-Taglinko-Webhook-Secret: fnwh_...' \
-d '{"order_id":"1234","status":"completed"}'
These statuses send the order: confirmed, processing, completed, paid, shipped, delivered, approved. These reject it: cancelled, failed, refunded, returned, rejected, fake. Anything else is ignored. Send up to 100 orders at once as {"orders":[{"order_id":"1","status":"delivered"}, ...]}. The answer says what happened to each order.
The webhook secret starts with fnwh_. It is not the key Taglinko uses inside your container; if you send that by mistake, the answer tells you. If a status arrives before the order was held (a fast shop, a slow page), Taglinko keeps it for 7 days and applies it when the order comes in.
Test it
- Turn on the Preview Header power-up with your Tag Manager preview header, so the requests Taglinko sends into your container show up in Preview.
- Place one prepaid test order. It must go out at once, as before.
- Place one cash-on-delivery test order. In Preview, the GA4 purchase fires only the Taglinko COD Hold tag, and the order appears as Pending on the power-up.
- Press Approve & fire. In Preview you see a new
purchasefrom the Taglinko COD Client withis_delayedtrue, and your purchase tags fire on it. - Check the browser side too: the Meta Pixel and Google Ads purchase tags must not fire for the COD test order.
- Turn the Preview Header off again when you are done.
Timing: send confirmations soon
- Meta accepts the original order time for 7 days. An order confirmed later is sent with the time it is sent.
- GA4 keeps the original time only for about 72 hours.
- Google Ads needs a Google click ID (gclid, wbraid or gbraid) on the order, and the click must be within about 90 days.
The page warns about pending orders that are close to these limits. A pending order that nobody confirms or rejects becomes Expired after 30 days; you can still approve it later.
How many orders
Holding orders is free and not limited. What counts is each order sent on (a fire): 50 a month on Free, 5,000 on Pro, 10,000 on Business and Enterprise. The power-up shows how many you have used this month, and Taglinko emails you at 80% and at 100%. Over the limit, confirmed orders wait and go out when the next month starts or when you upgrade.
Good to know
- Taglinko cannot see inside your tags. The page shows whether your container took the order and, when Tag Manager reports it, which tags ran. Use Preview to check a tag's own result.
- If your container does not answer, Taglinko tries again for 24 hours, then marks the order Send failed with a Retry button. The usual cause is that the Taglinko COD Client is not published.
- Held orders, including the customer details, are kept encrypted by Taglinko and shown only to you and your team. They are never shown to other Taglinko customers.
- Only requests signed by Taglinko are accepted on
/taglinko-cod, so nobody else can push purchases into your container through it. - Turning the power-up on or off restarts the container once. When it is off, cash-on-delivery purchases go out at checkout again (the fallback); you can leave the tags in place.
Still stuck?
Open a support ticket from your dashboard and include the container name.