Meta Conversions API (CAPI) Setup Guide
Recover conversion data lost to iOS14 and cookie deprecation. Complete guide to implementing server-side tracking with event deduplication and EMQ optimization.
B2B on Meta actually works — if CAPI is set up properly. 90% of B2B teams set it up wrong: no server-side events, bad event deduplication, EMQ scores under 5, or missing customer data for matching. The result looks like "Meta doesn't work for B2B." It's not Meta. It's the implementation.
See enterprise GTM fails when you measure and paid media isn't the problem.
CAPI vs Pixel: How They Work Together
CAPI doesn't replace the Meta Pixel. It complements it. The ideal setup runs both in parallel:
| Feature | Meta Pixel (Browser) | CAPI (Server) |
|---|---|---|
| Tracking Method | JavaScript + Cookies | Server-to-Server API |
| Blocked by Ad Blockers | Yes | No |
| Affected by iOS ATT | Yes (85%+ opt-out) | No |
| Affected by Cookie Deprecation | Yes | No |
| Real-Time Data | Instant | Near-instant (async) |
| PII Handling | Client-side hashing | Server-side hashing (more secure) |
Implementation Options Compared
Choose your CAPI implementation based on your tech stack and resources:
| Method | Best For | Complexity | Cost | Deduplication |
|---|---|---|---|---|
| GTM Server-Side | Most B2B companies | Medium | $0-50/mo | Built-in |
| HubSpot Native | HubSpot-only forms | Low | Free (Pro+) | Automatic |
| Direct API | Custom backends | High | Dev time | Manual |
| Partner Integrations | Segment, Zapier, etc. | Low | Varies | Depends |
GTM Server-Side (Recommended)
GTM Server-Side is the sweet spot for most B2B companies. It centralizes all tracking, handles deduplication automatically, and works with any form provider.
Deploy Server Container
Create a GTM Server container and deploy to Google Cloud Run (free tier eligible) or your preferred host.
Configure GA4 Client
Set up the GA4 Client to receive events from your web container via the Measurement Protocol.
Add Meta CAPI Tag
Use the official Meta Conversions API tag to transform and forward events to Meta's servers.
HubSpot Native CAPI
If HubSpot handles all your forms, enable native CAPI in Settings > Marketing > Ads > Meta integration. HubSpot automatically sends Lead events with full customer parameters.
Event Setup: What to Track
Map your key conversion points to Meta standard events:
| Your Action | Meta Event | Priority | Notes |
|---|---|---|---|
| Form submission (gated content) | Lead | High | Primary conversion for B2B |
| Demo/consultation request | Lead or Contact | High | Use Contact for higher-intent |
| Free trial signup | CompleteRegistration | High | Product-led growth companies |
| Newsletter signup | Subscribe | Medium | Lower intent but useful |
| Pricing page view | ViewContent | Medium | Custom params: content_type=pricing |
| All page views | PageView | Low | Pixel handles this well |
Example CAPI Payload (Lead Event)
{
"data": [{
"event_name": "Lead",
"event_time": 1713276000,
"event_id": "lead_abc123_1713276000",
"event_source_url": "https://yoursite.com/demo",
"action_source": "website",
"user_data": {
"em": ["sha256_hashed_email"],
"ph": ["sha256_hashed_phone"],
"fn": ["sha256_hashed_firstname"],
"ln": ["sha256_hashed_lastname"],
"ct": ["sha256_hashed_city"],
"st": ["sha256_hashed_state"],
"zp": ["sha256_hashed_zip"],
"country": ["sha256_hashed_country"],
"external_id": ["your_crm_contact_id"],
"client_ip_address": "192.168.1.1",
"client_user_agent": "Mozilla/5.0...",
"fbc": "fb.1.1713276000.AbCdEf",
"fbp": "fb.1.1713276000.1234567890"
},
"custom_data": {
"lead_source": "demo_request",
"value": 500,
"currency": "USD"
}
}],
"access_token": "YOUR_ACCESS_TOKEN"
} Deduplication Strategy
When running Pixel + CAPI together, you MUST deduplicate to avoid counting conversions twice.
How Deduplication Works
- Generate a unique
event_idwhen the conversion happens (client-side) - Pass the same event_id to both Pixel and CAPI
- Meta matches events by
event_name+event_id - If both arrive within 48 hours, Meta keeps only one (the first received)
Generating event_id
Use a deterministic ID when possible (e.g., transaction ID, form submission ID). Otherwise, generate a UUID on form submit:
// Generate event_id on form submit
const eventId = `lead_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
// Push to dataLayer for GTM
dataLayer.push({
'event': 'form_submit',
'event_id': eventId,
'user_email': userEmail // For CAPI payload
});
// Fire Pixel with same event_id
fbq('track', 'Lead', {}, { eventID: eventId }); Verifying Deduplication
In Events Manager > Test Events, check the "Deduplication" column. You should see "Deduplicated" for events that arrived via both Pixel and CAPI.
Testing and Validation
Always test in Meta's Test Events tool before going live.
Test Mode Setup
- Go to Events Manager > Your Pixel > Test Events
- Copy your
test_event_code - Add it to your CAPI payload:
"test_event_code": "TEST12345" - Submit a test conversion on your site
- Verify it appears in the Test Events tab
Validation Checklist
| Check | Expected Result | Fix If Wrong |
|---|---|---|
| Event appears in Test Events | Yes, within seconds | Check API response, verify endpoint |
| Deduplication shows "Deduplicated" | Yes, if Pixel also fired | Verify same event_id passed to both |
| EMQ score is 6+ | Yes | Add more customer parameters |
| No API errors in response | 200 OK | Check payload format, access token |
Event Match Quality (EMQ) Optimization
EMQ is Meta's 0-10 score measuring how well your events can be matched to Facebook users. Higher EMQ = better targeting and attribution.
Parameters That Improve EMQ
| Parameter | Key | Impact | Notes |
|---|---|---|---|
| Email (hashed) | em | High | Most important - always include |
| Phone (hashed) | ph | High | Format: country code + number |
| External ID | external_id | High | Your CRM contact ID |
| Click ID (fbc) | fbc | High | From _fbc cookie |
| Browser ID (fbp) | fbp | Medium | From _fbp cookie |
| First Name | fn | Medium | Lowercase, hashed |
| Last Name | ln | Medium | Lowercase, hashed |
| IP Address | client_ip_address | Medium | NOT hashed |
| User Agent | client_user_agent | Medium | NOT hashed |
| City, State, Zip | ct, st, zp | Low | Helpful but not critical |
Hashing Requirements
- Use SHA256 for all PII (email, phone, names, location)
- Lowercase and trim whitespace BEFORE hashing
- Phone: remove special characters, include country code (e.g., 14155551234)
- IP address and user agent are NOT hashed
// Example: Hashing email in JavaScript
const crypto = require('crypto');
const email = ' John.Doe@Example.com ';
const normalized = email.toLowerCase().trim();
const hashed = crypto.createHash('sha256').update(normalized).digest('hex');
// Result: e7b4c9f3a2... Implementation Checklist
Use this checklist to ensure your CAPI implementation is complete:
Setup
- Generate CAPI access token in Events Manager
- Store token securely (environment variable, not client-side)
- Map conversion events to Meta standard events
- Choose implementation method (GTM SS, HubSpot, Direct)
Deduplication
- Generate event_id on conversion trigger
- Pass same event_id to Pixel AND CAPI
- Verify deduplication in Test Events
Event Quality
- Include hashed email on all events
- Include fbc and fbp cookies (pass from browser)
- Include external_id (CRM contact ID)
- Include client_ip_address and client_user_agent
- Verify EMQ score is 6+ in Events Manager
Validation
- Test with test_event_code in Test Events
- Verify events appear within seconds
- Confirm no API errors in response
- Remove test_event_code for production
- Monitor Event Match Quality over 7 days
Need Help Implementing CAPI?
Our team has set up Meta CAPI for dozens of B2B companies. Get server-side tracking right the first time.
Talk to Our TeamThe argument behind the playbook
Related Resources
New playbook every other week
Join 3,500+ B2B marketers getting tactics from $50M+ in managed ad spend.
Subscribe to 42/ Newsletter