How to set up Meta CAPI step by step
Complete technical tutorial for Meta Conversion API in 2026: from access token to validation in Events Manager. In 11 minutes you'll have a working setup capable of reaching Match Quality 8+.
Setting up Meta CAPI takes 8 steps: (1) generate access token in Events Manager → (2) copy Pixel ID → (3) build payload with event_name + event_time + user_data + custom_data → (4) POST to graph.facebook.com/v19.0/{PIXEL_ID}/events → (5) share event_id with the pixel for dedupe → (6) hash email/phone with SHA-256 → (7) test in Events Manager → (8) remove test_event_code in production. Match Quality 8+ reachable in 7-14 days.
Prerequisites
Before starting, make sure you have:
- A Meta Business Manager account (business.facebook.com);
- A Facebook Pixel already created and running on the site (with at least PageView firing);
- Admin access on the pixel;
- A backend server to run the code (PHP, Node, Python, Ruby — any works);
- Ability to make outbound HTTPS requests (firewall allowing graph.facebook.com).
Step 1 — Generate the access token
- Open Events Manager;
- Select the pixel you want to use;
- Go to Settings → scroll down to Conversions API;
- Click Generate access token;
- Copy the token. You won't be able to see it again later. Store it safely.
META_ACCESS_TOKEN) on the server.
Step 2 — Copy the Pixel ID
Still in Events Manager, at the top of the pixel page you'll see the Pixel ID (15 digits). Copy it. Goes alongside the access token.
Step 3 — Build the payload
Minimum payload structure for a Purchase event:
{
"data": [{
"event_name": "Purchase",
"event_time": 1716640000,
"event_id": "order_10042_purchase",
"action_source": "website",
"event_source_url": "https://yourstore.com/checkout/success",
"user_data": {
"em": ["a3f2b9c8d4e5f6a1..."],
"ph": ["b4e5f6a2c3d9..."],
"client_ip_address": "200.123.45.67",
"client_user_agent": "Mozilla/5.0...",
"fbp": "fb.1.1716640000.123456789",
"fbc": "fb.1.1716640000.IwAR..."
},
"custom_data": {
"value": 99.90,
"currency": "USD",
"content_ids": ["SKU-001"],
"content_type": "product"
}
}],
"test_event_code": "TEST12345"
}
Required fields:
- event_name: Meta standard name — Purchase, Lead, CompleteRegistration, AddToCart, etc.
- event_time: Unix timestamp in seconds, not milliseconds. Common mistake.
- action_source:
"website","app","phone_call", etc. - event_id: for deduplication with the client pixel (see dedupe guide).
- user_data: user identifiers (email, phone, IP). The more fields, the higher the Match Quality.
Step 4 — Send via cURL (or your favorite HTTP client)
curl -X POST "https://graph.facebook.com/v19.0/{PIXEL_ID}/events" \
-H "Content-Type: application/json" \
-d '{
"data": [...],
"access_token": "EAAxx..."
}'
In PHP:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://graph.facebook.com/v19.0/{$pixel_id}/events");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'data' => [$event_payload],
'access_token' => $access_token
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
Successful response (HTTP 200):
{
"events_received": 1,
"messages": [],
"fbtrace_id": "AbcDefGhi..."
}
Step 5 — Share event_id with the pixel for deduplication
On the frontend, when firing the pixel:
fbq('track', 'Purchase', {
value: 99.90,
currency: 'USD'
}, {
eventID: 'order_10042_purchase' // ← same as CAPI
});
We break this down in the complete deduplication guide.
Step 6 — Hash PII with SHA-256
Email and phone can never go in plaintext. Normalize before hashing:
// PHP
function hashEmail($email) {
return hash('sha256', strtolower(trim($email)));
}
function hashPhone($phone) {
// Normalize to E.164: +15551234567
$digits = preg_replace('/[^0-9]/', '', $phone);
// Example: assume US (+1) if 10 digits
if (strlen($digits) === 10) $digits = '1' . $digits;
return hash('sha256', '+' . $digits);
}
$user_data = [
'em' => [hashEmail($order->customer_email)],
'ph' => [hashPhone($order->customer_phone)],
'fn' => [hash('sha256', strtolower(trim($order->customer_first_name)))],
'ln' => [hash('sha256', strtolower(trim($order->customer_last_name)))],
'ct' => [hash('sha256', strtolower($order->city))],
'st' => [hash('sha256', strtolower($order->state))],
'zp' => [hash('sha256', preg_replace('/[^0-9]/', '', $order->zip))],
'country' => [hash('sha256', 'us')],
'client_ip_address' => $_SERVER['REMOTE_ADDR'],
'client_user_agent' => $_SERVER['HTTP_USER_AGENT'],
'fbp' => $_COOKIE['_fbp'] ?? null,
'fbc' => $_COOKIE['_fbc'] ?? null,
];
user_data fields you send, the higher the Match Quality. Minimum acceptable: email + phone + IP + UA. To hit EMQ 9+, also send name (first/last), city, state, zip, country and click IDs (fbc/fbp).
Step 7 — Test in Events Manager
Before going to production, test with test_event_code:
- Events Manager → your pixel → Test Events tab;
- Copy the test code (format:
TEST12345); - Include in payload:
"test_event_code": "TEST12345"; - Fire the event. Within seconds it appears in Events Manager with a "Test event" label;
- Check: user_data fields hashed? event_id matching the pixel?
Step 8 — Validate in production
After testing, remove the test_event_code from the production payload. Otherwise Meta marks it as a test and does not count it for optimization.
Monitor in Events Manager:
- Event Activity: events arriving per hour;
- Diagnostics: warnings about issues (failing dedupe, malformed fields);
- Event Match Quality: score 1-10 per event — target 7+.
Common troubleshooting
"Event time in the past" or "in the future"
You're sending the timestamp in milliseconds. Meta expects seconds. Divide by 1000.
EMQ stuck at 0
Most likely the email/phone is not being hashed, or being hashed wrong (without lowercase/trim first). Compare your hash with echo -n "[email protected]" | shasum -a 256.
Duplicated events (Meta warns)
event_id of the pixel != event_id of CAPI. Make sure it's the SAME string. See dedupe guide.
"Invalid OAuth access token"
Token expired or was revoked. Generate another in Events Manager.
FAQ
Do I need a developer to set up CAPI?
It depends. Setup via Shopify or a CMS with native integration takes 5-15 minutes without code. Setup via webhook or directly on the API requires a dev for about 2-4 hours. Setup via SaaS (Trakvo, Stape) requires 0 code — just OAuth connection.
How long until I see Match Quality improvement?
EMQ updates within 48h after events start arriving. To climb from 5 → 8+ you need at least 7 days of consistent events with enriched PII (email + phone + click IDs).
Does Meta charge to use CAPI?
No. CAPI is free. You only pay for your infra (server) or for the SaaS that handles it for you.
Can I configure CAPI without the pixel?
Technically yes, but Meta does NOT recommend it. The pixel catches fast events on the client (PageView, AddToCart) with low latency. CAPI covers critical conversions with privacy. Running both together = best scenario.
What is the CAPI access token and where do I generate it?
The access token is a secret key that authorizes your server to send events to Meta. Generated in Meta Events Manager → Settings → Conversions API → Generate Access Token. Treat it as a password — never expose it on the frontend.
Skip all this setup
Trakvo handles Meta CAPI, TikTok Events API and Google Enhanced Conversions automatically. Setup in 15 minutes, EMQ 8+ guaranteed, 24/7 monitoring.
Talk to the team