Docs menu · Confirming orders

Docs / COD Order Protection

COD Order Protection

Confirming and rejecting orders

Held orders wait until you say they are real. Approve them by hand, or let your store, CRM or courier do it.

By hand

Open COD Held Orders in the dashboard and choose Approve & Fire or Reject on each order.

Best when a person calls to verify each order.

Automatically

Your store, CRM or courier posts the order's new status to PixelFly, and the order is confirmed or rejected to match.

Best at volume. WooCommerce stores get this from the plugin — no code needed.

Confirm within 7 days where you can

Inside that window Meta records the purchase at the original order time, so the ad that produced the sale gets the credit (GA4's window is 72 hours). Later confirmations are still sent — they are simply dated when you confirmed, not when the order was placed.

Which statuses confirm, and which reject

When a status arrives by webhook, PixelFly acts on it like this:

ResultStatuses
Confirms
the purchase is sent
confirmed processing completed paid shipped order-shipped dispatched partially-shipped delivered out-for-delivery in-transit intransit picked-up ready-for-pickup
Rejects
nothing is sent
cancelled canceled failed refunded returned trash
Ignored
the order keeps waiting
Anything else. Deliberate: firing a purchase for an order your store never confirmed is the exact harm this feature prevents.

Matching is forgiving about spelling: the status is lowercased, a leading wc- is stripped and underscores become hyphens. So wc-out_for_delivery, Out For Delivery and out-for-delivery are the same status. Courier plugins that add their own statuses work without extra setup.

A confirmation that arrives before the hold does is kept, and applied as soon as the hold lands. Order of arrival never loses a sale.

What cannot go wrong

  • An order is never counted twice. Holds are keyed on the order ID, so a second hold for the same order updates the first instead of creating another — which is why running the browser hold and the WooCommerce server-side backup together is safe.
  • A sent order stays sent. Once an order has fired or been rejected, a later hold for it is ignored outright. A replayed webhook or a customer refreshing the thank-you page cannot re-send a conversion.
  • A second hold fills in gaps. If the first hold was missing a field and a later one has it, the order keeps the richer version.
  • Only value and currency are required to send. Everything else — email, phone, click IDs — improves matching, but their absence never blocks the conversion.

Your monthly limit

Confirming an order sends a conversion, and that is what your plan counts. Holding is never limited — only sending.

PlanConfirmed COD orders sent per month
Free50
Starter500
Pro2,000
Enterprise10,000
What happens if you run out: the order is marked approved but the conversion is not sent, and its fire response reads quota_exceeded. Nothing is lost and no order is rejected — but those orders sit there, so if you see approved orders that never fired, check your usage first.

The count resets each billing period. Rejected orders cost nothing.

Orders held without an order ID

If a purchase reaches PixelFly with no order ID, it is still held, under a stand-in name beginning NOID-. The sale is not lost — but your webhook cannot confirm it, because your store does not know that name. Approve these from the dashboard.

Sending an event_id with the purchase makes that name stable, so a repeated hold finds the same order rather than creating a second one. If you see NOID- orders regularly, the real fix is to send transaction_id — see the purchase event.

Setting up the webhook yourself

WooCommerce stores should use the plugin's auto-confirm instead — it does this for you. For any other system, POST to your Webhook URL with the Webhook Secret:

curl -X POST https://<your-tracking-domain>/cod/webhook \
  -H "Content-Type: application/json" -H "X-Webhook-Secret: pfwh_…" \
  -d '{"type":"order.status","order_id":"1042","status":"completed"}'

Send this whenever an order's status changes. order_id must match the one sent at checkout. Full field reference: API reference.

Use the Webhook Secret (pfwh_…) here, not your API Key (pf_live_…). The API Key holds orders; the secret confirms them. Sending the wrong one returns 401.

We use cookies to enhance your experience, analyze site traffic, and for marketing purposes. By continuing to use our site, you consent to our use of cookies. Learn more