Facebook CAPI in Keitaro: setup guide without losing conversions
event_id The browser-side Facebook pixel does not see everything. Ad blockers, private browsing modes, and Safari's ITP restrictions cut off part of your conversions - the script on the landing page simply cannot fire the event in time, or at all. The Conversions API (CAPI) solves this differently: Keitaro sends conversion data straight from its own server to Meta's server, bypassing the browser entirely. Below is how to enable this in Keitaro two different ways, and how to avoid the usual traps with duplicate events and data formatting.
Quick test for tokens and CAPI sending
Before the full setup, verify your access and send a test event manually through the free Pixel Activator.
Why do you need Facebook CAPI if a pixel is already installed?
The standard pixel depends on JavaScript running in the user's browser. If the script is blocked, or the browser strips third-party cookies, the event never reaches Facebook. CAPI sends the same conversion fact through a server-side POST request, regardless of what happens in the client's browser. The recommended setup is to run the pixel and CAPI together: Meta deduplicates the overlap through event_id instead of you having to pick one channel over the other.
What you need before you start: Pixel ID and access token
Pixel ID and Access Token are the two values without which neither the built-in Keitaro module nor a manual request will work. Where exactly to find them in the Facebook interface is covered separately in Facebook Pixel ID and Access Token. The token is shown once at creation - save it immediately, it cannot be viewed again.
How do you enable the built-in Facebook Conversion integration in Keitaro?
This is the main path for most Keitaro accounts, and it is simpler than building the request by hand.
- Open Third-party Integrations in the Keitaro menu and select Facebook Conversion.
- Click Add account to open the integration creation form.
- Fill in the fields: Name (any label for the integration), Choose campaigns (which Keitaro campaigns send conversions to this account), Pixel ID, Conversions token.
- Turn on Use proxy if needed - see the section on proxies below for when that applies.
- Save the integration and move on to Mapping (next section) - without it, events go out but are not counted under the right statuses.
If your plan does not have the Facebook Conversion section in the menu, that is a plan limitation, not a broken account. Below is a path that works on any plan.
Automation instead of a manual build
If you would rather not deal with form fields and Mapping statuses by hand, Most takes over the setup and upkeep of the integration for you.
What if your plan does not include Facebook Conversion?
S2S Postback in Keitaro is a general-purpose tool for sending data to an external address, available on any plan. That makes it a working substitute for the built-in integration when your plan does not include it.
- Open the campaign page, the S2S Postbacks tab, and click Add a postback.
- Set the method to POST and the address to
graph.facebook.com/{API_VERSION}/{PIXEL_ID}/events?access_token={TOKEN}- substitute your own Pixel ID, token, and the current Graph API version (check Meta's documentation for the current version - it changes periodically). - In the request body, send JSON with a
dataarray where each event requires four fields at minimum:event_name(e.g.Purchase),event_time(Unix time in seconds, no more than 7 days in the past - otherwise Facebook rejects the entire request),action_sourceset towebsite, anduser_datawith at leastfbcandfbp. - Add
event_idto every event - without it, deduplication with the browser pixel will not work (more on this in the duplicates section below).
Example of a minimal request body:
{
"data": [
{
"event_name": "Purchase",
"event_time": 1721460000,
"event_id": "kt_8841203_purchase",
"action_source": "website",
"event_source_url": "LANDING_PAGE_URL",
"user_data": {
"fbc": "fb.1.1721400000000.PAZXh0bgNhZW0BMABhZGlkAasd123",
"fbp": "fb.1.1721300000000.1098115397",
"client_ip_address": "203.0.113.10",
"client_user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
}
]
}The event_time, IP, and user agent values come from your Keitaro traffic source macros. The full macro list is in the "S2S postback placeholders" reference on docs.keitaro.io; above, they are filled in by hand just to show the format clearly.
Here is how the two paths compare in practice:
| Criterion | Built-in module (Facebook Conversion) | DIY (S2S Postback + Graph API) |
|---|---|---|
| Keitaro plan | advanced, expert, team, enterprise | any |
fbc/fbp | built automatically | you build them by hand (formula below) |
| Status Mapping | through the built-in Mapping UI | you write the status-to-event logic into the request body yourself |
Flexibility of user_data/custom_data fields | limited to the integration form | full - any field the Graph API accepts |
| Reacting to Facebook's changing requirements | Keitaro updates it | you update it yourself |
How do you map Keitaro statuses to Facebook events?
After creating the integration, open Mapping inside it and pick the right Facebook event for every status you care about. An unmapped status simply will not go out. A finished integration can be duplicated with the Clone button along with its Mapping settings - handy when you set up a second campaign with the same status logic.
How do you build fbc from fbclid by hand?
If you are taking the S2S Postback route (see above), you will need to build the fbc field yourself. Per Meta's documentation, the formula is version.subdomainIndex.creationTime.fbclid, where version is always fb, subdomainIndex is 1 in the vast majority of cases, creationTime is Unix time in milliseconds, and fbclid is the value of that URL parameter from the click, unchanged and case-sensitive.
Example: a click arrives at example.com/?fbclid=PAZXh0bgNhZW0BMABhZGlkAasd123 at Unix time 1721400000000 ms. The resulting fbc:
fb.1.1721400000000.PAZXh0bgNhZW0BMABhZGlkAasd123The built-in Facebook Conversion module (see above) builds fbc/fbp for you automatically - constructing the formula by hand is only needed on the DIY path through S2S Postback.
How do you avoid duplicate conversions between the pixel and CAPI?
If the landing page already has pixel code installed, Facebook can receive the same conversion twice. For the system to recognize the duplicate, both events - browser and server - must carry the same event_id. The mechanism and worked examples are covered separately in event_id and deduplication.
What is Event Match Quality and how do you raise it?
The more valid user_data fields an event carries (email, phone, IP, user agent, fbc/fbp), the higher the EMQ and the more precise the attribution. Check the score in Events Manager, on the event source's detail card. If EMQ is low after launching the integration, first check whether fbc/fbp are actually reaching Facebook (see above), and whether email/phone, if collected, are sent hashed with SHA-256 as Meta's documentation requires.
When should you turn on Use proxy?
Without a proxy, requests to the Facebook API go directly from the Keitaro server. That is fine in most cases, but under high request frequency, or when operating from a region Facebook is temporarily restricting, the built-in Use proxy option lowers the risk of being blocked.
Do you need to warm up the pixel before turning on CAPI?
Turning on CAPI does not cancel the warm-up rule: a fresh pixel needs conversions introduced gradually, not all at once from an accumulated backlog. The exact steps are in pixel warming.
What do you do about leads older than 7 days (error 2804003)?
If you tried to upload a backlog of old leads and got error 2804003, that is the event_time limit documented by Facebook itself, not a Keitaro bug. A plain swap to the current time will break the deduplication described above. The full breakdown of a safe workaround is in Facebook CAPI Error 2804003: How to bypass the 7-day limit.
How do you check that the integration actually works?
- In Events Manager, open your pixel's Test Events tab and copy the
test_event_code. - Send a test event with that code (via Pixel Activator or a direct request).
- Confirm the event shows up with a server icon, and, when sent from the browser in parallel, with a Deduplicated status.
- If the event never arrives, check the S2S postback log in Keitaro: Maintenance → Logs → S2S postback log - it shows Facebook's response code for every request you send, regardless of which path you set up.
Automating it with Most - when manual setup stops paying off
Everything above - Mapping, the fbc format, deduplication, watching EMQ - needs constant attention: tokens expire, statuses change, and Facebook periodically shifts its field requirements. Most pulls conversions from Keitaro on a schedule, builds user_data in the right format itself, and makes sure events neither duplicate nor fall outside the 7-day window.
Frequently asked questions
Sources
Sources
- Meta for Developers - Conversions API, Using the API (request format,
event_time,event_id): developers.facebook.com/docs/marketing-api/conversions-api/using-the-api - Meta for Developers - Conversions API Parameters (required event fields): developers.facebook.com/docs/marketing-api/conversions-api/parameters
- Meta for Developers - fbp and fbc Parameters (the
fbcformat): developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc - Meta for Developers - Dataset Quality API (EMQ definition): developers.facebook.com/docs/marketing-api/conversions-api/dataset-quality-api
- Keitaro Docs - Facebook Conversions (built-in module, form fields, plans): docs.keitaro.io/en/third-party-integrations/facebook-conversions.html
- Keitaro Docs - S2S Postback (generic postback, macros): docs.keitaro.io/en/campaigns-and-streams/s2s-postback.html
- Keitaro Docs - Logs (the sent-postback log): docs.keitaro.io/en/maintenance/logs.html
A complete system for connecting your tracker to ad networks, built in a single pass - without manually watching Mapping and logs.
