Tracking & Attribution

How Do You Track Pinterest Checkout Events Server-Side?

Browser-fired Pinterest checkout events die in the payment redirect. Fire them from the order record instead — value, order_id and dedup done right.

Quick answer

Fire Pinterest checkout events from your order record — the webhook your store emits when payment succeeds — instead of from a browser script on the thank-you page. The order always exists; the browser session often does not, because it has to survive a payment redirect, a closed tab and an ad blocker first. Read value, currency and order ID from the order itself, give the server event the same deterministic event ID the tag would use so the two paths deduplicate, and keep the browser tag installed for the click identifier it captures.

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Track Pinterest checkout events server-side by firing them from your order record — the webhook your store emits when payment succeeds — instead of from a browser script on the thank-you page. The order always exists. The browser session often doesn’t. That single change decides whether Pinterest sees most of your checkout revenue or a fraction of it.

This is not a Pinterest-specific idea. It’s the same argument behind the Conversions API on every platform: the most reliable witness to a sale is the system that took the money, not the page that happened to load afterwards. Pinterest just makes the gap easy to see, because Pinterest traffic skews mobile and mobile is where browser-fired checkout events die.

Why do Pinterest checkout events go missing?

Because a browser-fired checkout event has to survive the payment redirect, and frequently doesn’t. The event fires from a script, on a page the buyer may never reach, in a tab they may close, in a browser that may have stripped the script. Each of those failures is silent — nothing errors, the event simply never exists.

Walk a real path. A buyer taps a Pin on their phone. They add to cart on your store, hit checkout, and get handed off to a hosted checkout on a different domain. Their bank throws a 3-D Secure step. They approve it, see “payment successful” from the bank, and close the tab — because as far as they’re concerned, they’re done. Your thank-you page never loads. Your checkout event never fires.

Then there are the quieter versions. A mobile browser reclaims memory and kills the backgrounded tab before the redirect completes. A content blocker removes the tag entirely. A sandboxed pixel environment — Shopify’s Customer Events being the well-known case — hands your script a stripped-down view of the page. On desktop with a same-domain checkout, none of this bites. On mobile with a redirect, it bites constantly.

Here’s the part worth internalising: your revenue is fine. The order is in your admin, the money is in your account. What’s broken is only the report. Pinterest is optimising against a sample of your sales and you have no way to tell which ones it’s missing, because a missing event leaves no trace.

What does firing the event server-side actually change?

It moves the trigger from a page load to a database write. Your server already knows payment succeeded — that’s what created the order. Emitting the conversion event at that moment removes the browser from the critical path entirely.

That buys you something specific, and it’s worth being precise about what it is. Server-side dispatch gives you delivery completeness: every order in your store admin can produce a conversion event, and you can verify it by reconciling your order count against your dispatched event count. Any order that didn’t produce an event is a bug you can find and fix.

What it does not give you is guaranteed attribution matching. Whether Pinterest ties that event back to a Pin click depends on the identifiers you attach and on Pinterest’s own ability to resolve the person behind them. You control dispatch. The platform controls matching. Anyone selling you the second half is overselling.

That distinction matters because it tells you where to spend effort. Missing events are your problem and are fixable. Unmatched events are a shared problem and are only improvable.

Which checkout-path events are worth sending server-side?

Send the events tied to an order or a deliberate action — the checkout event above all, and the funnel steps before it if you have a clean, non-duplicated trigger for each. Skip anything that fires on every pageview.

The failure mode almost everyone hits on their first server-side build is noise. It’s tempting to mirror your whole client-side event stream from the server “for completeness”, including a page-visit event on every request. Don’t. You end up paying an API call to tell Pinterest something its tag already reported, you double-count the top of your funnel, and — worst of the three — you bury the events that matter. When your activity log is 95% page visits, a genuinely failed checkout event is invisible in it.

A workable rule: one event per order, keyed on the order ID. If a second event arrives for order #1042, it is either a retry (deduplicate it) or a different status phase such as a refund (a separate row, not a replacement). Anything that can’t be keyed to an order or a specific user action is probably noise.

One practical constraint before you build: Pinterest accepts a fixed list of event names and rejects anything outside it, and its payload shapes differ from Meta’s in ways that produce unhelpful errors rather than clear ones. Those specifics belong in the build guide — if you haven’t wired the integration yet, set up the Pinterest Conversions API first and check names and field shapes against Pinterest’s own API reference, which is the only version that’s current.

How do you get value, currency and order_id right?

Read all three from the order record, never from the cart or the front end. The cart is what the buyer assembled; the order is what they actually paid. Those differ more often than you’d expect — discount codes, shipping calculated at the last step, a declined card, a partial-stock adjustment.

Some specifics that catch people:

  • Currency: send the order’s own currency, in major units. A store selling in three currencies should send three currencies and let the reporting layer normalise, rather than converting at send time with whatever rate the server happened to have.
  • Shipping and tax: pick a convention — usually ex-shipping, ex-tax, matching how you judge margin — and apply it everywhere. The number being consistent matters more than which convention you chose.
  • order_id: this is your dedup handle and your audit handle. Without it you cannot answer “did this specific order reach Pinterest?”, which is the only question you’ll actually want to ask when the numbers disagree.
  • Refunds: a refund is a new status phase, not an edit to the original. Keep the original paid row and net the refund against it. Overwriting the original destroys your audit trail and makes reported revenue drift silently upward over time.

Diagram of the server-side checkout event path in the PartialLeads dashboard style: order record with orders/create, a dedup key of order_id plus status phase, identity match on visitor id or email and phone, then the Pinterest CAPI checkout event carrying value and order_id, with a deterministic event ID strip below

How do you stop the tag and the server double-counting?

Give both paths the same deterministic event ID, derived from stable inputs rather than generated fresh on each send. If the browser tag and your server both report the same checkout, the platform can only collapse them into one conversion when both carry an identical ID.

The mistake is generating a random UUID per attempt. It looks correct and passes a single test, then breaks the first time a webhook is redelivered: your retry arrives with a new ID, the platform can’t tell it’s the same sale, and one order becomes two conversions. Under-reporting at least looks like a problem. Over-reporting looks like a good week, which is why it survives so long before anyone catches it.

Derive the ID instead from values that don’t change between attempts — the destination config, the event name, and the record it describes. Hash them. The same order now produces the same ID on the first send and the fifth, so a redelivery is harmless. Meta documents this pixel-versus-server pattern in detail (Meta, Deduplicate Pixel and Conversions API Events), and the shape of the idea carries across platforms even though each has its own matching rules.

Then add the belt-and-braces version: store dispatched event IDs yourself with a uniqueness constraint on the destination and the ID. Now a duplicate can’t even leave your server, which means you’re not relying on the platform’s dedup window to save you. If you want to see how many ways this goes wrong in practice, the same failure taxonomy is worked through in detail for Meta in our guide on why deduplication breaks.

Why does the browser tag still matter if the event comes from your server?

Because match quality depends on identifiers that only the browser can capture. Pinterest’s click identifier arrives as the _epik cookie on the visit, and the visitor’s IP address and user agent belong to the session, not to your order table. Your server knows what was bought. Only the browser knows which Pin click preceded it.

So the honest version is: server-side is a completeness fix, not an identity fix. Remove the base tag and your checkout events keep arriving, but they arrive as anonymous server events with a thin identifier set, and match quality drops. Run both — tag for identity, server for delivery — and deduplicate between them.

This is the same arithmetic behind Event Match Quality on Meta: the ceiling is set by what you collect, not by which transport you use. If you want a sense of where your numbers should land before you start tuning, we’ve written up what a good match rate looks like across platforms. The general principle — that the reliable trigger is the order record and the reliable identity is the session — is exactly how you’d fire purchase events server-side on any other platform too.

How does PartialLeads track Pinterest checkout events server-side?

PartialLeads ingests the order from the store rather than the browser. Shopify (webhook plus pixel), WooCommerce, Stripe, GoHighLevel and a universal inbound webhook all feed the same purchase pipeline, so the trigger is the payment, not the page.

Every inbound purchase runs the same path. Identity is normalised — email lowercased, phone converted to E.164 with a country-code fallback from the session’s geography. The purchase is deduplicated on client, source, order ID and status phase, which is what makes a refund its own row that nets against the paid one instead of overwriting it. Then the purchase is matched to a session through a tiered join: a visitor-ID echo carries the highest confidence, then email, then phone, then IP. Matching flips the lead to Completed with revenue attached, writes both a first-touch and a last-touch attribution row, and fans the conversion out server-side — Pinterest’s Conversions API v5 alongside Meta, TikTok and Google Ads, plus your own CRM webhooks.

The Pinterest path captures the _epik click identifier on the visit and sends ecommerce events, including checkout with value and order ID. Dispatch uses a deterministic event ID — a SHA-256 of the config, event name and record ID — against a dedup table with a uniqueness constraint per config, so a redelivered webhook physically cannot fire twice. Failures are typed rather than retried blindly: transient errors retry on the next worker cycle, authentication errors flag the configuration as needing reconnection, and permanent payload errors stop instead of looping.

Pinterest CAPI activity log in the PartialLeads dashboard style: a 14-day delivery sparkline with delivery rate, failed and queued counts, above a table of checkout event rows showing status dots, order numbers, which identifier matched, order value and timestamp

You verify it in three places. The CAPI activity log shows one checkout row per order with its status and the error text when a send fails. The Purchases ledger shows matched against unmatched orders, so a drop in match rate is visible as a shape rather than a vibe. The Attribution report’s Revenue by Match Quality view splits net revenue into partial-captured, submit-completed and unmatched — which answers the question that actually matters, namely how much of your revenue is tied to a session at all.

Two honest constraints. Match quality still depends on the _epik cookie captured client-side, so keeping your Pinterest base tag installed is not optional — PartialLeads improves delivery, not Pinterest’s identity graph. And purchases that can’t be matched to a session still land; they sit as unmatched and become attributable later if identity arrives, with a manual re-match available. That’s a real gap, not a rounding error, and the report shows it to you rather than hiding it in an average.

What breaks The mechanism Where you see it in the dashboard
Checkout event dies in the payment redirect Purchase ingested from the store’s order webhook, not the browser CAPI activity log — one checkout row per order
A redelivered webhook fires the same sale twice Deterministic event ID plus a uniqueness constraint per config CAPI activity log — no duplicate row
Refunds keep inflating reported revenue Refund lands as its own status-phase row and nets against paid Purchases ledger
Pinterest sees the sale but not the Pin click Purchase matched to the session by visitor ID, then email, then phone Attribution report — Revenue by Match Quality
Value taken from the cart, or in the wrong currency Value and currency read from the order record in its own currency Purchases ledger, per-order line items
A send fails and nobody notices for a month Typed retries; auth failures flag the config for reconnection CAPI activity log status and delivery rate

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Sources

https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters https://developers.facebook.com/docs/marketing-api/conversions-api


Frequently asked questions

QDo I still need the Pinterest tag if I send checkout events server-side?
Yes. The tag captures the `_epik` click identifier, the user agent and the IP address that belong to the visit — your order record has none of those. Without them, server events arrive with a thin identifier set and match quality drops. Run both paths and deduplicate between them: the tag handles identity, the server handles delivery.
QWill sending checkout events from my server double-count my conversions?
Only if the two paths use different event IDs. Give the browser event and the server event the same deterministic ID, derived from stable values such as the configuration, event name and order ID, and the platform collapses them into one conversion. Generating a random ID per send is what causes double-counting, because a webhook retry then looks like a second sale.
QWhat value should I send — cart total, order total, or net of shipping?
Send the order total from the order record, in the order's own currency and in major units, and pick one convention for shipping and tax that you apply everywhere. Most teams use ex-shipping and ex-tax because it matches how they judge margin. Consistency matters more than the specific choice — a number that changes definition between reports is worse than one that's slightly conservative.
QShould I send a page-visit event from the server too?
No. Page visits are already reported by the tag, they duplicate the top of your funnel, and at volume they bury the events you care about. When the activity log is mostly page visits, a failed checkout event is invisible in it. Keep server-side dispatch to events that key to an order or a deliberate user action.
QHow do I handle refunds after the checkout event has already been sent?
Treat the refund as a new status phase rather than an edit to the original. Keep the paid row, add the refund row, and net one against the other in reporting. Overwriting or deleting the original destroys the audit trail, and it makes reported revenue drift upward over time because partial refunds quietly stop being subtracted.
QCan I backfill checkout events for orders from last month?
Usually not in full. Conversion APIs enforce an event-time window and will reject or clamp events older than it, so a backfill can deliver with an adjusted timestamp rather than the original one. Check the current window in Pinterest's own API reference before planning a backfill, and treat historical sends as a repair for a recent gap rather than a way to rebuild a quarter.
QHow do I know whether a specific order reached Pinterest?
Look it up by order ID in your dispatch log — which is the main reason to attach the order ID to every event. A reliable setup lets you reconcile store order count against dispatched event count for a period and account for every difference. If your tooling can't answer "did order #1042 send, and if not why", you don't have delivery visibility yet.

Find the qualified leads your forms are currently throwing away.

Install PartialLeads on one landing page, send traffic, and compare what your CRM captured against what PartialLeads recovered and qualified.