Why send webhooks to server GTM?
The Meta Pixel only sees browser activity. Phone orders, COD confirmations, PayPal/Stripe callbacks, and CRM status changes happen outside the browser — but they still started from your ads. Webhooks let you send those conversions server-side with hashed email, phone, and match keys.
Common use cases (same as Stape describes):
- COD / manual payment — fire Purchase when WooCommerce status becomes Processing or Completed.
- Phone sales — CRM marks lead as won; webhook sends Purchase with stored click IDs.
- Payment gateway callbacks — Stripe/PayPal confirm on their domain; your backend forwards JSON to sGTM.
Two PixelFly patterns
| Pattern | Best for | Webhook URL |
|---|---|---|
| A — COD Order Protection (recommended for Woo COD) | Hold at checkout, confirm later, fire with original event_time |
/cod/webhook |
| B — Direct to sGTM /data (Stape-style CRM) | Custom CRM, no PixelFly hold; JSON straight to Data Client | /data on your server domain |
fbp, fbc, gclid at checkout in PixelFly
held_events, then replays them on confirm. Pattern B requires your CRM webhook to include all match fields itself.
Pattern A — WooCommerce / CRM → PixelFly /cod/webhook
This is the PixelFly-native path for COD and order-status confirmations.
Step 1 — Enable COD Order Protection
- PixelFly dashboard → your container → Power-ups → enable COD Order Protection.
- Set fire path to sGTM /data (required for Google Ads + Meta via your server tags).
- Copy Webhook URL and Webhook secret from the Power-up card.
Example webhook URL: https://server.yourstore.com/cod/webhook
Step 2 — Hold at checkout (capture match data)
Before the webhook can fire with high EMQ, the order must be held at checkout with user data:
- WooCommerce plugin v1.2+ → GTM / sGTM mode (dataLayer
purchase+payment_method). - sGTM → PixelFly COD Protection tag →
POST /cod/hold.
See the COD offline conversions guide for full GTM wiring.
Step 3 — Configure the confirm webhook (WooCommerce plugin)
- WordPress → PixelFly → COD Order Protection.
- Enable Auto-confirm webhook.
- Paste the Webhook secret from PixelFly.
- Select statuses: Processing, Completed (and Cancelled to reject fakes).
The plugin sends:
POST /cod/webhook
Header: X-Webhook-Secret: <your-secret>
Body: {
"type": "order.status",
"order_id": "12345",
"status": "processing"
}
PixelFly maps processing / completed → confirm (fire), cancelled → reject.
Step 4 — Custom CRM / backend webhook (no WooCommerce plugin)
From your CRM or Laravel/Node backend when an order is confirmed:
curl -X POST "https://server.yourstore.com/cod/webhook" \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: YOUR_SECRET" \
-d '{"type":"order.status","order_id":"12345","status":"completed"}'
If no prior hold exists, PixelFly creates a thin pending row then fires (best practice: always hold at checkout first).
Step 5 — What PixelFly sends to sGTM on confirm
After webhook confirm (or manual Approve & Fire in the dashboard), PixelFly POSTs to:
POST https://server.yourstore.com/data
Payload includes event: purchase, original event_time, user_data (email, phone), fbp, fbc, gclid, is_delayed: true.
Pattern B — CRM JSON directly to sGTM /data (Stape-style)
Use this when you do not need PixelFly hold/quota — e.g. phone CRM, Zapier, custom backend.
Step 1 — Server GTM + Data Client
- Install Data Client template in server GTM (Stape GitHub or compatible).
- Create Client → type Data Client → Accepted path:
/data(or custom path like/order_created). - Publish the server container after first import (avoids CORS errors).
Step 2 — Create the CRM webhook
Point your CRM delivery URL to your tagging server + path:
Delivery URL: https://server.yourstore.com/data
Content-Type: application/json
Minimum JSON (include as much user data as possible):
{
"event": "purchase",
"event_id": "crm_order_12345",
"event_time": 1699012345,
"value": 1500.00,
"currency": "BDT",
"transaction_id": "12345",
"user_data": {
"em": "customer@example.com",
"ph": "01712345678",
"fbp": "fb.1....",
"fbc": "fb.1...."
}
}
Step 3 — sGTM Preview header (test webhooks in Preview)
- Server GTM Preview → ⋮ → Send requests manually → copy
X-Gtm-Server-Preview. - PixelFly dashboard → Power-ups → sGTM Preview header config → paste header → Save.
- Trigger your CRM test webhook — requests appear in GTM Preview.
- Remove the preview header after testing (do not leave on in production).
Step 4 — Meta Conversions API tag
- Server GTM → Tags → New → Facebook Conversions API (Gallery).
- Setup method: Override → Event type: Purchase (or Lead).
- Map Event Data variables for email, phone, value, currency, event_id.
- Trigger: Custom Event
purchase+ Client Name equals Data Client.
Step 5 — Test & publish
- Send a test webhook from CRM or WooCommerce.
- Verify in GTM Preview → Event Data tab.
- Check Meta Events Manager → Test Events / live Purchase.
- Publish server container; disable Preview header in PixelFly.
PixelFly vs Stape webhook paths
| Stape | PixelFly equivalent |
|---|---|
/webhook or custom path | /cod/webhook (confirm) or /data (direct CRM) |
| Stape WP plugin webhooks tab | PixelFly plugin → Auto-confirm webhook |
| sGTM Preview header power-up | Same — PixelFly Power-up |
| Data Client + Meta CAPI tag | Same — your server GTM tags |
| Attribution at checkout | /cod/hold + held_events (Pattern A only) |
Improve Event Match Quality
- Send hashed-ready email and phone in every webhook.
- For Pattern A, capture
fbp,fbc,gclidat hold — not only at confirm. - Use a stable
event_id(e.g.purchase_12345) for deduplication against browser Pixel. - Keep
action_source: websitefor web-originated COD (not offline CSV).
FAQs
Should I use Pattern A or B for WooCommerce COD?
Pattern A. Hold at checkout, confirm via plugin webhook or dashboard. You get attribution storage and fake-order rejection.
Can I send webhooks without COD Order Protection enabled?
Pattern B (/data) works without the Power-up. Pattern A requires COD Order Protection enabled on the container.
Does the webhook auto-fire to Meta immediately?
Yes — on confirm status, PixelFly dispatches FireHeldEvent → POST /data → your Meta CAPI tag runs in sGTM.