Sync Events to the Meta Conversions API
Sync Events to the Meta Conversions API
Sync customer events to the Meta Conversions API from your CRM or database. Field mapping, SHA-256 hashing, event_id deduplication. Warehouse optional.
No credit card required
Free 100k syncs every month
The Meta Pixel was the obvious way to track conversions until iOS 14, ad blockers, and the cookie deprecation roadmap quietly broke it. Your Event Match Quality score has been sliding for two years and your retargeting audiences are smaller than they should be. The Meta Conversions API (also called the Facebook Conversions API or Facebook CAPI) is the fix, but every published guide assumes you already operate a Snowflake or BigQuery warehouse plus a reverse-ETL tool. Most teams don't, so the project sits on the backlog.
This Meta CAPI setup guide shows the direct path: sync conversion events from your CRM, Postgres, Stripe, or Shopify straight to the Meta Conversions API. Warehouse optional, no SDK, no dbt models. Setup takes about 30 minutes.
What events to send to the Meta Conversions API
Meta accepts a fixed set of standard events plus any custom event you define. Start with the events your ad campaigns optimize toward, not every event you can capture. Five carry most of the weight:
Purchase: completed payment. Source from Stripe charges, Shopify orders, or a
subscription_paymentstable in Postgres.Lead: qualified form fill or sales-accepted lead. Source from HubSpot or Salesforce when
lifecyclestage = "lead".AddToCart: cart action on your storefront. Source from Shopify or a cart events table.
CompleteRegistration: signup or trial start. Source from your app database
userstable whencreated_atlands in the last sync window.InitiateCheckout: checkout step before payment. Source from Shopify or your storefront.
Custom events fill the gaps. If you optimize toward "trial-to-paid conversion" or "demo booked," create a custom event with that name in Meta Events Manager and map it from the matching CRM or database state. Meta's optimization treats custom events the same as standard ones once they have enough volume.
Event | Common source | Required fields |
|---|---|---|
Purchase | Stripe, Shopify, Postgres | value, currency, em, fbp |
Lead | HubSpot, Salesforce | em, ph, external_id |
AddToCart | Shopify, storefront DB | content_ids, value, currency |
CompleteRegistration | App database, Auth provider | em, external_id |
InitiateCheckout | Shopify, checkout service | content_ids, value, currency |
How the Meta Conversions API replaces pixel-only tracking
Pixel-only setups still work for a fraction of your traffic. Safari has restricted third-party cookies for years. Apple's App Tracking Transparency cut iOS attribution to opt-in users, roughly 25%. Chrome's cookie deprecation will close the remaining gap.
The Conversions API is server-to-server. Your backend posts the event directly to Meta with whatever first-party identifiers you have on the customer record: hashed email, hashed phone, the fbc click ID, the fbp browser ID, the customer's external_id. Meta resolves each event to a user profile using whichever identifiers match. More identifiers, higher match quality, better optimization.
Two implementation details decide whether the Conversions API actually helps:
First-party identifier coverage. Email and phone are the strongest signals. If your CRM has them on every contact, your match quality lands above 7. If half your records are missing email, match quality stalls in the 4-5 range and Meta's optimizer underperforms.
Pixel deduplication. Sending the same purchase from both the Pixel and the Conversions API doubles your reported conversions unless
event_idmatches across both. Meta dedupes on theevent_id+event_namepair.
We recommend running both. The Pixel catches browser context the server doesn't see (referrer, page URL, fbc cookie). The Conversions API catches everything the browser missed. Together, with proper deduplication, they push match quality higher than either alone.
How to sync CRM or database events to the Meta Conversions API
Skip the warehouse. The CRM-to-Meta and database-to-Meta paths are direct.
1. Pick your source. For B2B Lead events, that's your CRM (HubSpot, Salesforce, Pipedrive, Attio). For Purchase events, that's your billing tool (Stripe) or storefront (Shopify). For app-side events like CompleteRegistration, that's your application database (Postgres, MySQL).
2. Connect Meta Ads as a destination. Authenticate with a Meta System User access token. Pick the Pixel ID for the ad account you want events attributed to. The same Oneprofile destination can serve multiple Pixel IDs if you run separate accounts per region or product.
3. Map your event payload. Each event needs event_name, event_time (Unix timestamp), action_source (website, app, email, chat, system_generated, physical_store, business_messaging, other), and user_data populated with as many identifiers as you have. Source-specific notes:
Stripe to Meta Purchase: Map
charge.amounttovalue(divide by 100 for dollars),charge.currencytocurrency,customer.emailtoem(hashed),charge.createdtoevent_time,charge.idtoevent_id. Filter oncharge.status = "succeeded".HubSpot to Meta Lead: Map
contact.emailtoem(hashed),contact.phonetoph(hashed),contact.idtoexternal_idandevent_id,contact.createdatetoevent_time. Filter onlifecyclestage = "lead"or your qualified stage.Postgres to Meta CompleteRegistration: Map
users.emailtoem(hashed),users.idtoexternal_idandevent_id,users.created_attoevent_time. Use a delta sync so only new rows since the last run get sent.Shopify to Meta Purchase: Map
order.total_pricetovalue,order.currencytocurrency,order.emailtoem(hashed),order.idtoevent_id, line item product IDs tocontent_ids.
4. Set the schedule. Use real-time sync for Stripe and Shopify so purchases reach Meta within seconds of the charge. Use a 15-minute schedule for CRM sources where leads change state in batches. Anything slower than hourly hurts optimization quality because Meta favors fresh events when training the bidder.
Field mapping and PII hashing for the Meta Conversions API
Meta requires SHA-256 hashing on every personal identifier before it leaves your server. The hash must be lowercase hex, applied to the lowercased and trimmed value (no whitespace, no formatting). Phone numbers must be E.164 without the leading plus.
Field | Source value | Hashed? | Notes |
|---|---|---|---|
em | Yes (SHA-256) | Lowercase, trim whitespace | |
ph | Phone | Yes (SHA-256) | Digits only, E.164 minus "+" |
fn | First name | Yes (SHA-256) | Lowercase, no punctuation |
ln | Last name | Yes (SHA-256) | Lowercase, no punctuation |
external_id | Your customer ID | Yes (SHA-256) | Stable across the customer |
fbc | Click ID cookie | No | Format: fb.1.timestamp.fbclid |
fbp | Browser ID cookie | No | Format: fb.1.timestamp.random |
client_ip_address | Server-captured IP | No | Hashed by Meta on receipt |
client_user_agent | Browser user agent | No | Hashed by Meta on receipt |
In Oneprofile, toggle SHA-256 on each PII field in the mapping interface. The hash runs in the destination layer so the source value never leaves your account in plaintext. If your source already stores hashed emails, set the field to "pass through unhashed" so Oneprofile doesn't double-hash.
The fbc and fbp cookies are the trickiest part. They live in the browser, set by the Meta Pixel. To include them in server-side events, your frontend has to read them and pass them to your backend on form submit or purchase confirmation. Store them on the contact, lead, or order record so they flow through with the rest of the payload. Without fbc, Meta can't match the conversion back to the original ad click.
Event deduplication: Meta Pixel vs Conversions API using event_id
Run the Pixel and the Conversions API together or you leave match quality on the table. The catch: both will report the same purchase unless you tell Meta they're the same event.
Meta dedupes on the event_id + event_name pair. If both sources send a Purchase with event_id = "order_8745", Meta keeps one copy and discards the duplicate. If event_id is missing or different, Meta counts both.
Three rules make this work:
Use the source record ID as event_id. For purchases, the order or charge ID. For leads, the CRM contact ID. For registrations, the user ID. Stable, unique, and available on both client and server.
Set the same event_id on the Pixel. Your client-side Pixel fire needs
eventIDin the parameters object. Pull it from a hidden field, a server-rendered variable, or the response of the action that created the record.Send the Pixel event first when possible. The browser fires immediately on page load; the server fires when the sync runs. With a 15-minute schedule, the Pixel reliably arrives first. With real-time sync, Meta still dedupes but the order can flip.
Verify deduplication in Events Manager > Overview. Look at the "Server" and "Browser" columns. If both show similar daily counts but the deduplicated total is roughly the higher of the two, deduplication is working.
Scheduling, testing, and monitoring Meta Conversions API sync
The Conversions API silently accepts requests with mismatched fields. The events arrive, but match quality stays low because Meta couldn't resolve the user. Test before you trust the dashboard.
Test Events. Meta Events Manager has a Test Events tab with a unique test code. Add test_event_code to your payload (Oneprofile has a per-sync test mode) and confirm the event appears within 60 seconds. Watch the Test Events panel for warnings: missing required fields, malformed timestamps, unhashed PII.
Diagnostics tab. After events flow to production for a few hours, check Events Manager > Diagnostics. Meta surfaces issues like "Event match quality below threshold" or "Duplicate events detected" with concrete counts. Fix what you can, ignore what's expected (some pixel-only Cookie warnings are unavoidable).
Event Match Quality (EMQ) score. EMQ runs from 0 to 10 per event type. Above 6.0 is solid; above 8.0 is excellent. The fastest way to raise EMQ is to add more identifiers to the payload. Phone often moves the score by a full point if you weren't sending it before.
Failed events in Oneprofile. When the Conversions API rejects an event (expired token, malformed payload, rate limit), the record gets captured for review instead of disappearing. Common rejections:
Access token expired. System User tokens can be set to never expire; use that.
event_timemore than 7 days old. Meta rejects stale events. Filter your initial backfill to the last 7 days.Missing
action_source. Default it to "website" or "system_generated" depending on origin.
Extending to other platforms. The same source feeds Google Ads, LinkedIn, and TikTok server-side APIs. Add a destination for each, map the platform-specific click ID (gclid, li_fat_id, ttclid) and the same hashed identifiers, and you have offline conversion tracking across every paid channel without a second integration project.
The hardest part of the Meta Conversions API isn't the API. It's getting your conversion events out of the systems they live in without standing up a warehouse first.
Do I need a data warehouse to use the Meta Conversions API?
What's the difference between the Meta Pixel and the Conversions API?
Which PII fields does the Meta Conversions API require to be hashed?
How does event_id deduplication work between the Meta Pixel and Conversions API?
How fresh do events need to be when sent to the Meta Conversions API?
Can the same setup push events to Google, LinkedIn, and TikTok Conversions APIs?