Sales webhook for offline conversions: the fields that matter and how to test it safely
Short answer
Send event (required) and at least one of email or phone. Add value, currency, order_id and any click IDs. Use the test flag first, and check the delivery log if nothing arrives.
A webhook is the most flexible way to send an offline conversion, and also the one people are most nervous about. So let me take the mystery out of it.
What it is: your CRM, your app, or a tool like Zapier, Make or n8n makes one web request each time a sale or stage happens. The request goes to a private URL. The receiver takes what's in it, finds the ad click, and sends the stage on to the ad platforms.
What goes in the request. Not much, and only a few fields are required.
The event name is required. Standard names like Purchase, Lead, Schedule and CompleteRegistration are recognised automatically. If you use your own name, like "Deal Won", you map it once first.
The person is required: an email, a phone with country code, or both. At least one. This is how the sale is matched to them and to their ad click.
Everything else is optional but useful: the value and currency, so the platform can learn what a sale is worth. An order ID, which is your own ID for the sale. And any click IDs you stored: gclid, wbraid, gbraid for Google, fbclid, fbc and fbp for Meta, ttclid for TikTok, li_fat_id for LinkedIn.
The order ID is worth a moment. If the same order ID arrives twice, it counts once. That makes retries safe, which matters, because tools retry when they think a request failed, and without dedupe a retry becomes a duplicate sale.
How to set it up in the common tools. In Zapier, add a Webhooks by Zapier step, choose POST, paste the URL, set the payload type to JSON, and add event, email, value, currency and order_id. In Make, use the HTTP module with method POST and a raw JSON body. In n8n, use the HTTP Request node with the same fields. From your own app, it's one POST with a JSON body.
Common mistakes with webhooks. Sending an email with a trailing space. Forgetting the country code on a phone. Using a different event name in the CRM than the one you mapped. Sending the value as text with a currency symbol. And sending stages with no click ID and no email, which leaves nothing to match on.
To make it concrete, here's what a webhook request looks like in plain terms. It's a JSON message with the fields we covered. An example: event is "Purchase", email is "jane@example.com", phone is "+15550100199", value is 4200, currency is "USD", order_id is "SO-10482", gclid is the stored Google click ID. That's it. A dozen lines at most.
If something isn't working, here's a debugging checklist in order. Did the request reach the receiver at all? Check the log for it. Was the event name recognised or mapped? Was there an email or phone? Was the value a plain number? Was there a click ID or enough identity to match? Was the order ID unique, or was it treated as a repeat? And did you leave the test flag on by accident?
Nine times out of ten, the answer is on that list.
Have you built a webhook flow before? What tripped you up?
DataCops in short
For this question: This webhook is the DataCops private webhook: event plus email or phone, optional value, order ID and click IDs, with a test flag and a delivery log to check the send.
DataCops is a tool that makes the ads learn from real sales: it sends booked, showed, won and paid stages from your CRM back to your ad platforms, gives every visit a bot verdict with a Real people only switch per platform, warms up new campaigns with your existing customers, and logs every send. It is not an attribution report.
How DataCops does it
- The sale after the form. HighLevel natively (lead, booked, showed, won with value, paid; cancelled, no-show and lost are never sent), any other CRM through a private webhook, matched to the click by click ID or hashed email and phone.
- The click is kept on the server. Click IDs are stored for up to 90 days, so a deal that closes weeks later still finds its click.
- Real people only. Every visit gets a bot verdict against 360+ billion IPs and 350+ monitoring points, with a Real people only switch per ad platform, off by default. CRM events carry no bot flag.
- Counted once, logged every time. Pixel and server events share an event ID, and a delivery log shows each send as sent, held, skipped or failed, with the reason.
- One script, one DNS record. Collection runs on your own domain; with DNS on Cloudflare, the free Worker reads the click at the edge before the page loads.
Best for: ad-funded businesses whose sales close in a CRM or on a call: clinics, home services, agencies, B2B and lead gen.
Ads Warmup: tell the ads who pays
Ads Warmup, DataCops' flagship feature, sends customers you already have to Meta, Google Ads and TikTok before a new campaign spends: upload a CSV (only email is required, up to 20,000 rows), see a 0 to 10 match score per person, pick the event, and send. Rows are dated when you send, and Google Ads credits only people who clicked a Google ad. Preview is free; sending needs a paid plan. Check your own consent basis for the list first. See Ads Warmup.
Ways to do this job
| Option | Best for |
|---|---|
| DataCops | CRM stages to Meta and Google Ads, with a bot verdict and a delivery log |
| Zapier or Make | One simple CRM-to-ad flow you build and maintain |
| Direct API upload | Teams with an engineer |
| Manual CSV upload | Occasional batches |
When not to use DataCops
- You want a reporting dashboard. DataCops cleans and sends what goes into your ads. It is not a multi-touch reporting layer.
- Your sales never leave one store checkout. If everything happens in one checkout, the platform's own pixel plus server events may be enough.
Sources and further reading
More on this: Any CRM by webhook, and the complete guide to offline conversion tracking.
Start with one stage and the test flag on. Once it reads correctly in the log, turn the flag off and add the next stage.