Technical Deep-Dive · 14 min read
Server-Side Conversion API Setup: Meta CAPI + Google Enhanced Conversions in One Hour
Meta's Conversions API and Google's Enhanced Conversions both raise reported ROAS by 15-25% on a typical DTC store — the lift is real and comes from recovering signal that iOS 17 ATP, Safari ITP, and ad-blocker browsers (Brave, Firefox, DuckDuckGo) suppress on the client side. This playbook walks the 1-hour setup: paste-token, event-name mapping, deduplication via event_id, and the post-deploy parity check.
The dual-feed concept — pixel and CAPI firing the same conversion
A modern DTC conversion fires twice. First, the pixel posts the event from the browser when the thank-you page loads — Meta's edge gets a cookie-rich, fingerprinted, browser-native signal. Second, the server posts the same event to Meta's Graph API endpoint from your backend — Meta's edge gets a signal that survives ad blockers, iOS link-tracking protection, and Safari intelligent tracking prevention.
Both feeds are valuable, and together they form a redundant pair. The pixel fires with high trust (Meta likes the browser fingerprint) but loses 15–25% of conversions to client-side suppression. The server fires unconditionally (no browser, no blockers) but with lower trust per event because the server cannot supply a fresh browser fingerprint. Together they recover the suppressed conversions without sacrificing the pixel's trust signal.
The contract that holds the dual feed together is event_id. Every conversion ships from both sides with the same event_id — typically the platform's order ID. Meta dedupes events with matching event_name + event_id within a rolling 48-hour window. If the pixel arrives first, it counts. If only the CAPI arrives (pixel was blocked), the CAPI counts. If both arrive, they merge. Without a shared event_id the same conversion counts twice and your ROAS inflates 2x.
Admaxxer ships the dual feed as a single connector: paste your Meta Pixel ID and CAPI access token, point your Shopify or storefront pixel at our endpoint, and the pixel + server feed both fire with matching event_ids automatically. The same connector covers Google Enhanced Conversions, TikTok Events API, and Pinterest Conversions API — the dedup contract is identical across all four networks.
What Meta's Conversions API actually is
Meta's Conversions API (CAPI) is a server-to-server POST to the Graph API event-ingest endpoint at /<PIXEL_ID>/events. The pixel does the same conceptual thing from the browser; CAPI does it from your backend. Same event schema, same custom_data shape, same standard event vocabulary — just a different physical sender.
The shape of a Purchase event is the same as what fbq('track', 'Purchase', ...) produces on the client, with one critical addition: the user_data block. Because the server does not have a browser, it cannot pull the visitor's cookies or fingerprint automatically. You have to supply Meta with the identifiers Meta needs to match the event back to a person: hashed email, hashed phone, fbc (the fbclid-derived cookie), fbp (the first-party tracking cookie), client IP, and user-agent.
Here is the canonical Purchase event shape — every field is documented at the Meta Marketing API v25 reference:
POST https://graph.facebook.com/v25.0/<PIXEL_ID>/events
?access_token=<CAPI_ACCESS_TOKEN>
Content-Type: application/json
{
"data": [
{
"event_name": "Purchase",
"event_time": 1747526400,
"event_id": "shopify-order-1029384756",
"action_source": "website",
"event_source_url": "https://acme-skincare.com/checkout/thank_you",
"user_data": {
"em": ["e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"],
"ph": ["83a3e7cf02d4b76cd60ba89eb6c2c19acb3a1a1f7f1d8b1d1cb1c0e3f1e1f7c6"],
"fbc": "fb.1.1747526380.IwAR1234567890abcdefREDACTED_fbclid_value",
"fbp": "fb.1.1747526380.0123456789",
"client_ip_address": "203.0.113.42",
"client_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4) ...",
"fb_login_id": null
},
"custom_data": {
"currency": "USD",
"value": 54.99,
"content_ids": ["sku_serum_1oz"],
"content_type": "product",
"num_items": 1,
"order_id": "1029384756"
}
}
],
"partner_agent": "admaxxer-capi/1.0",
"test_event_code": null
}
Field-by-field: event_name is one of Meta's nine standard events plus your custom-named events. event_time is a Unix timestamp in seconds. event_id is your dedup key — make it the Shopify order ID. action_source is 'website' for browser conversions, 'app' for in-app, 'physical_store' for offline, 'email' for email-driven conversions. event_source_url is the thank-you page URL.
The user_data block is where the recovery work happens. em (email) and ph (phone) are SHA-256 hashes of the lowercased trimmed input — Meta requires this hashing because they never accept plaintext PII. fbc and fbp are first-party cookies that Admaxxer's pixel writes to your domain — the server reads them from the request when the conversion fires. client_ip_address and client_user_agent are the visitor's IP and browser UA at the time of conversion, captured by the pixel and forwarded server-side.
custom_data carries the business payload: currency, value, content_ids, num_items, order_id. These power Meta's optimization and bid algorithms — the more complete this block, the better Meta's auction targeting on your campaigns.
Why CAPI matters more now than it did pre-iOS-17
Pixels worked fine in 2020. They worked acceptably in 2022. They are systematically lossy in 2026 because three independent forces have closed in on the browser pixel from different angles.
iOS 17 Link Tracking Protection (Sept 2023) strips fbclid, gclid, and 15 other click identifiers from URLs opened in Mail, Messages, and Private Safari before the destination page loads. The pixel still fires, but it fires with no fbclid — Meta cannot match the conversion back to an ad click. The conversion is reported as organic. Spend is misallocated.
Safari ITP — Intelligent Tracking Prevention — caps the lifetime of any cookie set in third-party context to 7 days, and any cookie set via document.cookie from JS to 1 day. Repeat-visit attribution windows collapse from 28 days to 1 day on a sizeable fraction of your iOS Safari traffic.
Privacy-first browsers — Brave, Firefox with Enhanced Tracking Protection, DuckDuckGo, the latest Edge — block Meta's pixel script outright. The user clicks an ad, lands on your page, converts, and Meta never receives a single signal. The conversion is invisible to the auction.
Aggregated across a typical DTC traffic mix, these three forces suppress 15–25% of pixel-side conversions before they reach Meta. CAPI recovers most of them because it fires from your server, which has none of these constraints. The recovered ROAS is real — it represents conversions that were happening anyway but were being misattributed to organic/direct.
Google has the same problem with the gclid + GA4 client-side feed. TikTok and Pinterest have the same problem with their pixel. The server-side recovery pattern is identical across all four networks. Admaxxer ships it as a single connector module.
Meta CAPI setup — the 30-minute walkthrough
The Meta setup splits into three artefacts: a long-lived user access token (your auth into the Marketing API), the Pixel ID (the ad pixel resource), and the CAPI access token (a server-event-specific token scoped to the pixel). All three are pasted into Admaxxer's /settings/connections page and the dual feed goes live within five minutes.
The step-by-step:
- Open Meta Business Manager at business.facebook.com and switch to the Business that owns your ad account.
- Go to Business Settings → System Users (under Users in the left rail). Create a system user named 'admaxxer-capi' with the 'Admin' role.
- Click 'Generate New Token' on that system user. Select your business app from the dropdown (create one named 'Admaxxer CAPI' if you do not already have a business app). Token expiry: 'Never expires'. Scopes: 'ads_management', 'business_management'. Click Generate, copy the token to a secure spot — you will not see it again.
- Back in Business Settings → Data Sources → Pixels, click the pixel that fires on your storefront. Copy the Pixel ID (a 15-digit number in the URL or under Settings → Pixel ID).
- Click the pixel → Settings → Set up the Conversions API → Set up manually → Generate Access Token. Copy this CAPI access token (different from the user token; this one is server-event-specific).
- Open Admaxxer's /settings/connections → Meta → click 'Connect'. Paste the user token, the Pixel ID, and the CAPI access token. Click 'Verify' — Admaxxer pings the Marketing API with each token and confirms permissions.
- Once verified, Admaxxer's pixel begins firing both client-side fbq('track', ...) events AND server-side CAPI POSTs with matching event_ids automatically. No additional code needed on Shopify themes — the Shopify app handles theme injection.
Verify with Meta's Test Events tool before considering setup complete. Go to Events Manager → your pixel → Test Events tab. Browse to your storefront, add a product to cart, complete a $1 test purchase (or use Shopify Bogus Gateway). Within 30 seconds the event should appear in the Test Events tab with both 'Browser' and 'Server' badges — that confirms the dual feed is dedupping correctly.
# Verify CAPI before going live. Visit Events Manager → Test Events,
# copy the TEST<...> code, then run this curl against your pixel.
curl -X POST "https://graph.facebook.com/v25.0/<PIXEL_ID>/events?access_token=<CAPI_ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"data": [{
"event_name": "Purchase",
"event_time": '"$(date +%s)"',
"event_id": "smoke-test-001",
"action_source": "website",
"user_data": { "em": ["test-hash-here"] },
"custom_data": { "currency": "USD", "value": 1.00 }
}],
"test_event_code": "TEST12345"
}'
# Successful response: {"events_received":1,"messages":[],"fbtrace_id":"..."}
# If you see "messages": [...], the payload was rejected — read the message
# carefully (typically a missing user_data field or unhashed email).
Google Enhanced Conversions setup
Google's equivalent of Meta CAPI is Enhanced Conversions. The server POSTs hashed email, hashed phone, and the gclid to the Google Ads API's uploadClickConversions endpoint. The recovered ROAS lift on Google Performance Max campaigns is comparable to Meta CAPI — typically 15–25% — because PMax leans heavily on first-party signals for bid optimization.
The artefacts you need: a Google Ads developer token (a 1-time generation per Google Ads account), an OAuth refresh token (a 1-time generation, never expires), the customer ID of your Google Ads account, and the conversion action IDs you want to send conversions for.
The step-by-step:
- Open Google Ads → Tools → Setup → API Center. Apply for a developer token (Basic Access is sufficient for Admaxxer; see developers.google.com/google-ads/api/docs/api-policy/access-levels for the tier matrix).
- Once the developer token is approved (usually <24h for Basic Access), copy it.
- Open Admaxxer's /settings/connections → Google → click 'Connect'. Admaxxer redirects you through Google's OAuth consent screen. Approve the requested scopes (adwords read+write). Google returns a refresh token to Admaxxer's backend.
- Back in Admaxxer, paste the developer token. Admaxxer auto-fetches your customer ID and the list of conversion actions in your account.
- Map your conversion actions: each Google Ads conversion action (Purchase, Lead, Signup, etc.) is mapped to the equivalent Admaxxer event. Save.
- Within 5 minutes, completed orders begin firing Enhanced Conversions to the uploadClickConversions endpoint with hashed email + hashed phone + gclid in the user_identifiers block.
Here is the shape of the POST that Admaxxer fires on each completed order:
# Google Enhanced Conversions for Leads — server-side POST.
POST https://googleads.googleapis.com/v17/customers/<CUSTOMER_ID>:uploadClickConversions
Authorization: Bearer <OAUTH_ACCESS_TOKEN>
developer-token: <DEVELOPER_TOKEN>
Content-Type: application/json
{
"conversions": [
{
"gclid": "EAIaIQobChMI1234567890_gclid_value",
"conversion_action": "customers/<CUSTOMER_ID>/conversionActions/<ACTION_ID>",
"conversion_date_time": "2026-05-18 14:30:00+00:00",
"conversion_value": 54.99,
"currency_code": "USD",
"order_id": "1029384756",
"user_identifiers": [
{
"hashed_email": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"user_identifier_source": "FIRST_PARTY"
},
{
"hashed_phone_number": "83a3e7cf02d4b76cd60ba89eb6c2c19acb3a1a1f7f1d8b1d1cb1c0e3f1e1f7c6",
"user_identifier_source": "FIRST_PARTY"
}
]
}
],
"partial_failure": true
}
user_identifier_source='FIRST_PARTY' tells Google these identifiers came from your own customer data, not an in-Google form fill — required for the conversion to be eligible for Enhanced Conversion matching.
TikTok Events API and Pinterest Conversions API
TikTok and Pinterest both ship their own server-side CAPI equivalents, and Admaxxer's connector covers both. Same paste-token pattern, same dedup contract via event_id, same /attribution/health visibility downstream.
TikTok Events API: open TikTok Business Center → Events Manager → click your pixel → Settings → Set up Events API → Generate Access Token. Copy the access token and the Pixel Code. Paste both in Admaxxer's /settings/connections → TikTok. The same nine standard events ship over — TikTok renames a few (ContentView instead of ViewContent) but Admaxxer's mapping layer handles the difference.
Pinterest Conversions API: open Pinterest Business → Ads → Conversions → Get Started → Generate Conversion Token. Copy the conversion token and the Ad Account ID. Paste both in Admaxxer's /settings/connections → Pinterest. Pinterest's standard event vocabulary is identical to Meta's nine — no mapping translation needed.
TikTok and Pinterest CAPI both run on the same dual-feed contract as Meta. The pixel fires client-side; the server fires server-side; both ship a matching event_id; the platform dedupes on event_name + event_id. The recovered ROAS lift on TikTok and Pinterest is typically 10–20%, slightly less than Meta's 15–25% because their client-side pixels were less aggressively cookie-dependent to begin with.
TikTok-shop attribution gets a special handling layer: TikTok has two attribution surfaces — the in-feed pixel for off-TikTok conversions and the TikTok-shop API for in-app conversions. Admaxxer's connector ensures a single conversion is not double-counted across both surfaces by routing TikTok-shop events through a separate event_name (so the dedup contract still works without ambiguity).
event_id deduplication — the contract that makes the dual feed work
Without deduplication, the dual feed double-counts every conversion. With deduplication, the dual feed recovers suppressed conversions at no double-count cost. The mechanism is simple, and the implementation discipline is non-negotiable.
Meta deduplicates events on the tuple (event_name, event_id) within a rolling 48-hour window per pixel. If your pixel fires a 'Purchase' event with event_id='1029384756' at 10:00 UTC, and your CAPI fires a 'Purchase' event with event_id='1029384756' at 10:00:02 UTC, Meta keeps one. If your CAPI fires the same Purchase 24 hours later because the pixel was blocked and the server retry queue kicked in, Meta still keeps one.
The canonical event_id for an ecommerce conversion is the Shopify order ID. Every other identifier (Stripe payment intent, Recharge charge ID, custom-checkout order number) works equally well as long as it is stable and unique per conversion. The pixel and the server must both supply the same id, in the same case, with no decoration.
// Pixel side (browser) — Shopify thank-you page snippet.
fbq('track', 'Purchase', {
value: 54.99,
currency: 'USD',
content_ids: ['sku_serum_1oz']
}, {
eventID: '1029384756' // <-- Shopify order ID, lowercased to "eventID" per fbq spec
});
// Server side (CAPI) — fired by Admaxxer from the order webhook handler.
{
"event_name": "Purchase",
"event_id": "1029384756", // <-- SAME id as the pixel-side eventID
"event_time": 1747526400,
"event_source_url": "https://acme-skincare.com/checkout/thank_you",
...
}
// Meta dedupes events with matching event_name + event_id within a 48-hour
// rolling window. Without a shared event_id, the same purchase counts twice
// in your Ads Manager reports — inflating ROAS by 2x and corrupting the
// bidding algorithm's learned conversion rate.
Two ways the contract breaks. First, mismatched casing: 'A1B2C3' on the pixel side, 'a1b2c3' on the server side — Meta treats those as different ids. Admaxxer normalizes both sides to a consistent shape. Second, event_id reuse across event_names: using order ID '1029384756' as the event_id for both a Purchase event AND a CompleteRegistration event is fine (Meta dedupes on the tuple), but using order ID '1029384756' for two separate Purchase events is a bug — Admaxxer's CAPI module asserts uniqueness per (event_name, event_id) at write time and surfaces violations in /attribution/health.
The same contract applies to Google Enhanced Conversions (dedup key: order_id), TikTok Events API (event_id), and Pinterest Conversions API (event_id). Admaxxer assigns event_ids automatically, ensures pixel and server agree, and runs daily integrity checks that flag any drift.
The nine Meta standard events — what fires when
Meta's nine standard events (per the v25 reference) are the canonical event vocabulary across pixel + CAPI. Admaxxer ships sensible defaults for Shopify; custom checkouts can map events explicitly in /settings/events.
| Standard event | When it fires |
|---|---|
PageView | Any page load. Fires automatically on every Admaxxer pixel install. |
ViewContent | Product detail page view. Fires when /products/* loads on Shopify. |
AddToCart | Cart line added. Fires from the Shopify cart event. |
InitiateCheckout | Checkout started. Fires when /checkouts/* loads. |
AddPaymentInfo | Payment step reached. Available on Shopify Plus / custom checkouts. |
Purchase | Order completed. Fires on the thank-you page + server-side CAPI POST. |
Lead | Lead form submission (newsletter, contact, demo). Custom-mapped per workspace. |
Subscribe | Recurring subscription started. Fires from subscription platform webhook. |
CompleteRegistration | Account created. Fires on Shopify customer.created. |
Standard events benefit from Meta's optimization layer in a way that custom events do not — Meta's bid algorithms have prior beliefs about how each standard event correlates with downstream value, and they exploit those priors during the learning phase. Custom events work, but they put the optimization layer into a longer learning phase. Use standard events whenever the conceptual mapping is clean.
The mapping that catches the most operators out: AddToCart fires whenever a line is added, including when the customer is browsing pre-purchase. Some merchants want AddToCart to fire only when the cart has crossed a value threshold; that is a custom event, not AddToCart. Keep the standard event semantics aligned with Meta's documented definitions or the bid algorithm will mismatch your data.
Post-deploy parity check — the 24-hour rolling integrity test
Once both pixel and CAPI feeds are live, Admaxxer runs a rolling 24-hour parity check that compares three numbers: pixel events received, CAPI events received, and Shopify orders. The three should agree within tight tolerances.
Tolerances:
- Within 2% of each other across all three: green. The dual feed is healthy.
- Between 2% and 5% drift on any pair: yellow. Investigate within 24 hours — likely a payload field is missing or a CMP consent rule is gating events.
- Above 5% drift on any pair: red. Auto-alert fires; dashboard banner surfaces on the workspace overview page; an admin email is sent.
The pixel-vs-Shopify gap is normal — pixel events are suppressed by ad blockers / iOS link tracking / Safari ITP and will run ~15-25% below Shopify orders. The CAPI-vs-Shopify gap should be near-zero (the server has no suppression issues). The pixel-vs-CAPI gap is the interesting number: it measures the suppression rate you are recovering with CAPI.
The query Admaxxer runs (on ClickHouse, the analytics datastore):
-- Admaxxer's 24-hour rolling parity check. Runs every 15 minutes
-- against the pixel events, CAPI events, and the Shopify order table.
WITH pixel AS (
SELECT count() AS pixel_purchases
FROM pageviews
WHERE workspace_id = {workspace:String}
AND event_name = 'Purchase'
AND ts >= now() - INTERVAL 24 HOUR
),
capi AS (
SELECT count() AS capi_purchases
FROM capi_events
WHERE workspace_id = {workspace:String}
AND event_name = 'Purchase'
AND ts >= now() - INTERVAL 24 HOUR
),
shopify AS (
SELECT count() AS shopify_orders
FROM orders
WHERE workspace_id = {workspace:String}
AND order_status = 'paid'
AND created_at >= now() - INTERVAL 24 HOUR
)
SELECT
shopify.shopify_orders AS truth,
pixel.pixel_purchases AS pixel,
capi.capi_purchases AS capi,
abs(pixel.pixel_purchases - shopify.shopify_orders) /
nullif(shopify.shopify_orders, 0) AS pixel_drift,
abs(capi.capi_purchases - shopify.shopify_orders) /
nullif(shopify.shopify_orders, 0) AS capi_drift
FROM pixel, capi, shopify;
-- Healthy: both pixel_drift and capi_drift below 0.02 (2%).
-- Yellow: any drift between 2% and 5% — investigate within 24h.
-- Red: any drift above 5% — auto-alert + dashboard banner.
The /attribution/health page surfaces the live values plus the last 50 events with their raw payload and the platform's response code. When a misfire happens you can see exactly which event got rejected and why, without leaving the dashboard.
Top five misfire causes operators hit in the first week
After deploying CAPI on a few hundred Admaxxer customers, five misfire patterns dominate. Operators see them as 'CAPI is broken' but each one is a fixable payload issue.
- Missing phone hash. The user_data block carries em (email) but no ph (phone). Meta's match rate on email-only is lower than email+phone. Fix: ensure your checkout collects phone and that Admaxxer's pixel forwards a SHA-256 hash of the lowercased+trimmed phone (with leading + and country code).
- fbc / fbp not set. The visitor clicked an ad in an iOS Mail environment that stripped the fbclid; the pixel had nothing to write into fbc. The Admaxxer first-party cookie still captures fbp, but a missing fbc lowers match rate. Fix: nothing on the operator side — this is the iOS 17 ATP gap that the iOS 17 attribution recovery playbook addresses upstream.
- event_id collision. A custom checkout reuses the same order ID for cart abandonment and final purchase. The Purchase event with event_id='X' dedupes against the InitiateCheckout event with event_id='X' (Meta dedupes on event_name + event_id, so technically the tuple is unique, but operator error often produces actual Purchase-vs-Purchase collisions). Fix: prefix the event_id with a per-event-type tag in your custom-checkout glue code.
- CMP consent gating CAPI when it should not. Some Cookiebot / OneTrust configurations block the server-side CAPI call when the user denies analytics consent. CAPI does not need analytics consent — it is a legitimate-interest server event for advertising attribution. Fix: configure your CMP to gate the pixel only, not the server feed.
- Account-level Meta de-prioritization. After a sudden 30%+ event-volume spike (typical of a fresh CAPI rollout), Meta's quality scoring may temporarily de-prioritize your pixel. Fix: ramp CAPI rollout over 48 hours rather than flipping the switch instantly; Admaxxer's BFCM-mode toggle does this automatically.
CAPI rate-limiting — batching, backoff, and dead-letter handling
Meta's CAPI endpoint accepts up to 50 events per POST and rate-limits at a per-pixel ceiling that depends on your account standing. For a high-volume store running 1,000+ orders/hour during BFCM, a naive per-event POST will trip rate limits within minutes. Three layers prevent that.
First, server-side batching. Admaxxer queues events in BullMQ (the project-wide Redis-backed worker queue) and flushes them to Meta CAPI in batches of 50 (Meta's per-batch max) every 250ms. The arithmetic: 50 events per batch × 4 batches per second = 200 events per second = 12,000 events per minute of headroom. A naive per-event POST tops out around 1,500/minute.
Second, exponential backoff on HTTP 429 responses. When Meta returns 'rate limited', the retry waits 2^n seconds with jitter (1s, then 2s, then 4s, capped at 60s) before retrying. Jitter prevents thundering-herd behavior when 50 retries fire simultaneously — without jitter, the second retry storms back at the same instant and gets rate-limited again.
Third, dead-letter queue for events older than 24 hours. Meta's CAPI endpoint accepts events for up to 7 days after they happened (the documented 'freshness window'), but match quality degrades sharply after 24 hours. Admaxxer flags any event that has been retrying for >24 hours, drops it from the live queue, and surfaces it in /attribution/health so the team can investigate the systemic cause (usually a payload-shape problem affecting a class of events, not a one-off transient).
The same pattern applies to Google Enhanced Conversions (uploadClickConversions accepts up to 2000 conversions per request, rate-limited per developer-token), TikTok Events API (up to 1000 events per batch), and Pinterest Conversions API (up to 1000 events per batch). Admaxxer's connector module abstracts the per-network differences so the operator does not have to tune them individually.
Operator checklist — 1-hour walkthrough end-to-end
- (00:00) Open Meta Business Manager → Business Settings → System Users. Create system user, generate token, copy.
- (00:10) Business Settings → Data Sources → Pixels → your pixel → copy Pixel ID. Settings → CAPI → Generate Access Token, copy.
- (00:15) Admaxxer → /settings/connections → Meta → Connect. Paste user token, Pixel ID, CAPI token. Verify.
- (00:20) Verify in Meta Events Manager → Test Events → confirm pixel + server events arrive with matching event_ids.
- (00:25) Open Google Ads → Tools → API Center → request developer token. Apply for Basic Access (auto-approved within minutes for most accounts).
- (00:30) Admaxxer → /settings/connections → Google → Connect. OAuth, paste developer token, map conversion actions.
- (00:40) Open TikTok Business Center → Events Manager → CAPI → Generate Token. Copy. Paste into Admaxxer.
- (00:45) Open Pinterest Business → Ads → Conversions → Generate Token. Copy. Paste into Admaxxer.
- (00:50) Browse /attribution/health on Admaxxer. Confirm all four networks show green status indicators.
- (00:55) Place a $1 test order on your storefront. Confirm the event appears across pixel + CAPI + Shopify within 30s.
- (01:00) Done. The dual feed is live, dedup is healthy, and the parity check will alert on any drift over the next 24 hours.
After the 1-hour setup, allow 7 days for Meta's bid algorithm to re-baseline on the new conversion volume. Reported ROAS will rise during this period — that is the recovery showing up, not a phantom lift.
Frequently asked questions
- What is Meta CAPI and how does it differ from the Meta Pixel?
- The Meta Pixel fires from the browser — JavaScript that POSTs page-view, add-to-cart, and purchase events to Meta's edge. The Conversions API (CAPI) fires from your server — your backend POSTs the same events to Meta's Graph API at the /events endpoint. They are complementary: pixel events get a higher trust score from Meta (browser fingerprint + cookie); CAPI events survive ad-blockers + iOS ATP + Safari ITP. Together they form a 'dual-feed' that's deduplicated by event_id so a single conversion never counts twice. Admaxxer ships both feeds in one connector.
- Why does Meta require deduplication via event_id?
- Because a single user purchase will fire two events to Meta: the pixel from their browser, and the CAPI from your server. Without a shared event_id, Meta would count one purchase as two — inflating your reported ROAS and corrupting the bidding algorithm. The event_id is typically the Shopify order number (or any unique transaction ID). Admaxxer's CAPI integration assigns event_id automatically and verifies dedup health daily in /attribution/health — if pixel-only or CAPI-only events spike, we surface it as an alert.
- What's Google Enhanced Conversions and do I need it?
- Google Enhanced Conversions (EC) is the Google Ads equivalent of Meta CAPI — your server POSTs hashed email + phone + click_id to Google's API to recover conversions when the pixel-side gclid is unavailable. It is critical for Performance Max campaigns where Google's bidding leans hard on first-party signals. Admaxxer ships EC alongside Meta CAPI; setup is paste-token from your Google Ads account (a developer token + refresh token; both 1-time generated). The recovered ROAS lift is similar to CAPI: 15-25% on PMax campaigns.
- Does Admaxxer support TikTok Events API and Pinterest Conversions API?
- Yes. The TikTok Events API and Pinterest Conversions API ship in the same connector module as Meta CAPI and Google EC. Same paste-token setup, same deduplication pattern via event_id, same /attribution/health visibility. TikTok-shop attribution is also handled (the TikTok in-feed pixel + the off-TikTok ads API call) so a single conversion isn't double-counted across TikTok's two attribution surfaces.
- How do I verify CAPI is working end-to-end after I paste my tokens?
- Admaxxer's /attribution/health page runs a 24-hour rolling parity check that compares: pixel events received vs CAPI events received vs Shopify orders. Healthy: all three within 2%. Unhealthy: any of the three drifts >5% (typical cause: a CAPI payload field is missing — phone hash or fbc/fbp tokens). The page also shows the most recent 50 events with their raw payload + Meta's response code so you can debug a misfire without leaving the dashboard.
Run this playbook in your own dashboard
Admaxxer ships the pixel + Meta CAPI + Google Enhanced Conversions + Maxxer AI agent + cohort analytics out of the box. The playbook above becomes a live surface in your account after a 5-minute setup.