Sales webhook for offline conversions: the fields that matter and how to test it safely
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 "[email protected]", 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?
More on this: Any CRM by webhook, and the complete guide to offline conversion tracking.