How to count a sale that happens in a webhook
When the payment or booking completes inside another tool, your website never sees it. Here is how that sale gets counted, and exactly what to ask your web person for.

On this page
The short version
- If people pay, book or join inside another tool, your website never sees the moment that matters, so your analytics cannot count it on its own
- Counting a "thank you" page instead is wrong in both directions: refreshes count twice, closed tabs never count, renewals never appear
- The fix is a webhook: the payment tool sends a message when a payment completes, and that message is passed to your analytics as a sale
- It only helps if the sale is matched to the visit that led to it, so ask for the visitor's code to travel with the payment
- It is a small job for a web developer. The checklist below is what to send them
A webhook is a message one tool sends to another the moment something happens, such as "this payment just went through". To count a sale that happens in a webhook, whoever looks after your website passes that message on to your analytics as a named event, attached to the visitor who made the purchase. That is the whole mechanism.
It matters because the last step is usually the one that pays, and it is often the one your analytics cannot see. If people join, pay or book on a page run by your payment or booking tool, your report shows plenty of interest and no sales, and every piece of marketing looks like it failed at the final hurdle. Here is why that happens, what the fix is, and what to ask for.
Why the sale is invisible to your analytics
Take a professional association for independent bookkeepers. Its website explains the membership, the events and the resources. When somebody clicks "Join", they go to a payment page run by the payment provider, pay the annual fee, and get an email confirming their membership. Next year, the renewal is taken automatically.
The association's analytics runs in the visitor's browser, on the association's own pages. The payment page is not one of them, and the renewal does not happen in a browser at all: nobody is looking at a screen when the card is charged. So the two moments the association cares most about, a new member and a renewal, never reach its analytics.
That is the gap. Your payment or booking tool knows exactly what happened. Your analytics knows exactly where people came from. Nothing joins the two.
Why the thank-you page is not the sale
The usual workaround is to send people back to a "thank you for joining" page and count arrivals there as sales. It is better than nothing for a week. Then it starts to mislead.
| What happens | What the report says |
|---|---|
| The new member closes the tab before the redirect | A payment you received never happened |
| The page is refreshed, bookmarked or shared | One payment counted three times |
| A payment fails after the redirect | A sale that never happened, counted |
| The membership renews a year later | Nothing at all |
The refresh row alone makes the number wrong in the flattering direction, which is the worst way for a number to be wrong. And the renewal row means the association can never see which marketing brings members who stay.
The fix, in plain words
Most payment and booking tools can send a webhook when a payment completes. The fix has three parts:
- The payment tool sends its message to a small piece of code on your website's side when a payment succeeds
- That code passes it to your analytics as a named event, such as "member joined"
- Your analytics counts the event as a step in the journey, like any page
In Funnel Analytics the second part is one request to its server events address, and the third is adding a step of type "Server event" with the same name. If the name matches no step yet, the event still appears in that visitor's journey, which is a useful way to check it is arriving before you rely on it.
Payment tools sometimes send the same message twice. Stripe's own documentation says webhook endpoints "might occasionally receive the same event more than once", and it retries failed deliveries for up to three days. So each event should carry the payment's own reference as its unique key, and then a repeat is counted once.
Matching the sale to the visit
Sending the sale is the easy half. The useful half is attaching it to the right visitor, because a sale with no history cannot tell you which marketing worked.
Your analytics tries the strongest match first:
| What is sent with the sale | What it matches |
|---|---|
| Your own member or customer number | The person, on whichever device they use |
| The visitor's code, carried through the payment | Exactly one browser |
| The visitor's internet address | A household or office, which is a guess |
The member number is the strongest. If members sign in on your website, your pages can tell the analytics who is signed in, and the payment message can carry the same number. Renewals then land on the right person a year later.
The visitor's code is the practical one for a first purchase. Your web person reads the code from the page and puts it into the payment link as a reference, which the payment tool hands back in its message. Stripe's checkout has a field for exactly this, client_reference_id. (How cookie-less tracking works explains what that code is.)
A sale is never the first sight of somebody. Funnel Analytics attaches a server event only to a visitor it has already seen on your website. An event with no visit behind it has no source, so counting it would add a sale to the bottom of the journey while the top stayed still.
When nothing matches
Sometimes a sale matches nobody, and that failure is silent: the payment tool says the message was delivered, and the report says nothing happened.
Funnel Analytics records every event that matched no visitor and lists them on the Install panel, with which matching details each one carried. "We only sent an internet address and it matched nobody" and "we sent nothing to match on" are different problems, and that list is what tells them apart. Look at it a week after the setup goes live. A long list of unmatched sales usually means the visitor's code is not making it into the payment link.
Once it works, the association can finally answer the question it set out with: did the members who joined from the webinar series renew at the same rate as the ones who came from the accountancy podcast? Knowing which marketing worked is how you decide what to try next, and it is why Mole Marketer gives every initiative its own UTM tags, so that answer arrives sorted by the work that produced it.
What to send whoever looks after your website
Copy this, fill in the brackets, and send it.
- When a payment completes in [payment or booking tool], please send it to our analytics as a server event named [member joined]
- Please send renewals too, named [membership renewed]
- Please use the payment's own reference as the event's unique key, so a repeated message is counted once
- Please carry the visitor's code into the payment link as a reference, and send it back with the event
- If members sign in on our site, please send our member number with each event as well
- When it is live, please check the Install panel's server tab for events that matched nobody
Frequently asked questions
What is a webhook, in plain words?
A webhook is a message one tool sends to another the moment something happens. Your payment tool can send one that says "this payment just went through", and your website can pass it on to your analytics so the sale is counted.
Can I just count my thank-you page instead?
For a first week, yes. After that it misleads: refreshes count twice, closed tabs never count, failed payments count anyway, and renewals never appear. Replace it before you make a decision on the number.
Do I need a developer to track a conversion in a webhook?
Yes, for a small job. Somebody has to receive the payment tool's message and pass it on, and carry the visitor's code into the payment link. It is a small job for somebody who already looks after your website, and the checklist above is what to hand them.
What if somebody browsed on their phone and paid on a laptop?
Then the visitor's code from the phone will not match the laptop. Your own member or customer number solves it: if your pages tell the analytics who is signed in, and the payment carries the same number, the sale lands on the person rather than the browser.
Will a repeated webhook count the sale twice?
Not if each event carries the payment's own reference as its unique key. A repeat with the same key is counted once. Stripe retries failed deliveries for up to three days, so for very late repeats, keep a note on your side of what has already been sent.
Count the step that pays
The sale your business runs on probably finishes somewhere your website cannot see: a payment page, a booking tool, a renewal on a schedule. Your payment tool already knows when it happened.
So have it say so. Send the sale to your analytics, match it to the visit, and the last number in the report becomes the one your bank agrees with. Then the rest of the report is worth reading, because it tells you which marketing brought the people who paid.
Written by Oli
Founder of Mole Marketer. Writes about running marketing as a steady weekly process with a small team, and checks every third-party price against its own page before publishing.
