Meta Conversions API (CAPI)
Datafly Signal delivers events to Meta (Facebook) server-to-server using the Conversions API (CAPI). This provides reliable event delivery that is not affected by ad blockers or browser tracking prevention.
Prerequisites
Before configuring Meta CAPI in Signal, you need a Meta Business account with an active Pixel and a system user access token. Follow the steps below to set everything up.
Step 1: Create a Meta Business Account
If you don’t already have one:
- Go to business.facebook.com and click Create Account.
- Enter your business name, your name, and business email.
- Complete the setup wizard.
Step 2: Create or Find Your Meta Pixel
- In Meta Business Suite, go to Events Manager (left sidebar, under Data Sources).
- If you already have a Pixel, click on it and note the Pixel ID (a numeric ID like
1234567890). - To create a new Pixel:
- Click Connect Data Sources > Web > Connect.
- Name your Pixel (e.g. “My Website Pixel”) and enter your website URL.
- Select Conversions API as the connection method.
- Click Create Pixel. Note the Pixel ID.
Step 3: Create a System User
System users are the recommended way to generate long-lived access tokens for server-to-server integrations. Personal access tokens expire and should not be used in production.
- Go to Business Settings > Users > System Users.
- Click Add to create a new system user.
- Give it a name (e.g. “Datafly Signal”) and set the role to Admin.
- Click Create System User.
Step 4: Assign Permissions to the System User
- On the system user you just created, click Add Assets.
- Select Pixels from the asset type list.
- Find your Pixel and toggle on Manage Pixel permission.
- Click Save Changes.
Step 5: Generate an Access Token
- Still on the system user page, click Generate New Token.
- Select the following permissions:
ads_managementbusiness_management
- Click Generate Token.
- Copy the token immediately — it will only be shown once.
Store this token securely. If you lose it, you will need to generate a new one. The token does not expire unless revoked.
Step 6: Verify Your Domain (Recommended)
Domain verification improves event attribution and is required for some features:
- Go to Business Settings > Brand Safety > Domains.
- Click Add and enter your domain.
- Choose a verification method (DNS TXT record is recommended).
- Complete verification.
Configure in Signal
Now that you have your Pixel ID and access token, configure the integration in Signal.
Configuration Fields
| Field | Required | Description |
|---|---|---|
pixel_id | Yes | Your Meta Pixel ID (numeric). Found in Meta Events Manager. |
access_token | Yes | System user access token with ads_management permission. |
test_event_code | No | Test event code for validation. Events sent with this code appear in the Events Manager Test Events tab but do not affect reporting. |
Management UI Setup
- Go to Integrations > Add Integration > Meta Conversions API.
- Choose a variant:
- Retail — full e-commerce funnel (product views, cart, checkout, purchase, refunds)
- Travel — search, booking, and cancellation events
- B2B / SaaS — sign-up, trial, subscription, and lead events
- Default — standard page view and conversion tracking
- Enter your
pixel_idandaccess_token. - Select consent categories (typically
advertisingormarketing). - Click Save.
Management API Setup
curl -X POST http://localhost:8084/v1/admin/integrations \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_id": "src_abc123",
"vendor": "meta_capi",
"name": "Meta CAPI Production",
"enabled": true,
"config": {
"pixel_id": "1234567890",
"access_token": "your_access_token"
},
"consent_categories": ["advertising"]
}'API Endpoint
POST https://graph.facebook.com/v25.0/{pixel_id}/events?access_token={access_token}Events are sent as a JSON array in the data field.
Identity Signals
Meta uses multiple identity signals to match server events to user profiles. The more signals you provide, the higher your Event Match Quality (EMQ) score.
Automatic Signals
These are sent automatically by Signal — no configuration needed:
| Signal | Field | Description |
|---|---|---|
fbp | user_data.fbp | Facebook browser ID. Signal self-generates this in the format fb.1.{timestamp}.{random} and stores it as a first-party cookie. |
fbc | user_data.fbc | Facebook click ID. Automatically extracted from the fbclid URL parameter and formatted as fb.1.{timestamp}.{fbclid}. |
external_id | user_data.external_id | Set to the Datafly anonymous_id (hashed). Provides a stable cross-session identifier. |
client_ip_address | user_data.client_ip_address | Visitor’s IP address, forwarded from the original request. |
client_user_agent | user_data.client_user_agent | Visitor’s User-Agent string, forwarded from the original request. |
User-Provided Signals (Hashed)
When a user is identified via _df.identify() with traits, the following fields are SHA-256 hashed before sending to Meta:
| Signal | Hashing | Description |
|---|---|---|
em (email) | SHA-256, lowercase, trimmed | User’s email address |
ph (phone) | SHA-256, digits only, with country code | User’s phone number |
fn (first name) | SHA-256, lowercase, trimmed | First name |
ln (last name) | SHA-256, lowercase, trimmed | Last name |
ct (city) | SHA-256, lowercase, no spaces | City |
st (state) | SHA-256, lowercase, 2-letter code | State/region |
zp (zip) | SHA-256, trimmed | Postal/zip code |
country | SHA-256, lowercase, 2-letter ISO code | Country |
All PII hashing is performed server-side by the Delivery Worker before the data leaves your infrastructure. Raw PII is never sent to Meta.
How to Send User Data
Call _df.identify() when a user logs in, registers, or submits a form:
_df.identify("user-123", {
email: "[email protected]",
phone: "+44 7700 900123",
firstName: "Jane",
lastName: "Doe",
city: "London",
country: "GB"
});Signal normalises and hashes these fields automatically before sending to Meta.
Event Mapping
| Datafly Event | Meta Event | Notes |
|---|---|---|
page (page view) | PageView | Sent for every page view |
Order Completed / Product Purchased | Purchase | Requires value, currency |
Product Added | AddToCart | Includes content_ids, content_type |
Checkout Started | InitiateCheckout | Includes value, currency, num_items |
Product Viewed | ViewContent | Includes content_ids, content_type |
Lead Generated | Lead | Lead form submission |
Signed Up | CompleteRegistration | User registration |
Products Searched | Search | Includes search_string |
Product Added to Wishlist | AddToWishlist | Includes content_ids |
| Custom events | Passed through | Sent as custom event name |
Example: Purchase Event
Datafly.js call:
_df.track("Order Completed", {
order_id: "ORD-001",
total: 129.99,
currency: "USD",
products: [
{ product_id: "SKU-A", name: "Widget", price: 49.99, quantity: 2 },
{ product_id: "SKU-B", name: "Gadget", price: 30.01, quantity: 1 }
]
});Meta CAPI payload sent by Signal:
{
"data": [
{
"event_name": "Purchase",
"event_time": 1706540000,
"event_id": "evt_abc123def456",
"event_source_url": "https://example.com/checkout/confirmation",
"action_source": "website", // or "app" for mobile SDK events
"user_data": {
"client_ip_address": "203.0.113.50",
"client_user_agent": "Mozilla/5.0 ...",
"fbp": "fb.1.1706540000.1234567890",
"fbc": "fb.1.1706539000.AbCdEfGhIjKl",
"external_id": "a1b2c3d4e5f6..."
},
"custom_data": {
"currency": "USD",
"value": 129.99,
"order_id": "ORD-001",
"content_ids": ["SKU-A", "SKU-B"],
"content_type": "product",
"contents": [
{ "id": "SKU-A", "quantity": 2, "item_price": 49.99 },
{ "id": "SKU-B", "quantity": 1, "item_price": 30.01 }
],
"num_items": 3
}
}
]
}Event Match Quality (EMQ)
Event Match Quality is Meta’s score (out of 10) indicating how well your server events can be matched to Meta user profiles. A higher EMQ means better ad optimisation and attribution.
How to Improve EMQ
- Send
fbpandfbc: Signal handles this automatically. Thefbpcookie is self-generated andfbcis extracted from thefbclidURL parameter. - Include user data: Pass email and phone via
_df.identify()when users log in or submit forms. These are hashed before delivery. - Send IP and User-Agent: Signal forwards these automatically from the original browser request.
- Use
external_id: Enabled by default — the hashedanonymous_idis sent asexternal_id.
A typical Signal integration achieves an EMQ of 6-8 with automatic signals alone, and 8-10 when user-provided data (email, phone) is available.
Testing Your Integration
Step 1: Use Test Event Code
- In Meta Events Manager, go to your Pixel > Test Events tab.
- Copy the test event code (e.g.
TEST12345). - Add it to your Signal integration config:
{
"config": {
"pixel_id": "1234567890",
"access_token": "your_access_token",
"test_event_code": "TEST12345"
}
}- Trigger events on your website — they will appear in the Test Events tab within seconds.
Step 2: Verify in Events Manager
- Go to Events Manager > your Pixel > Overview.
- You should see events arriving with the “Server” badge (indicating server-side delivery).
- Click on individual events to inspect the payload and match quality.
Step 3: Check Event Match Quality
- In Events Manager, go to Data Sources > your Pixel > Event Match Quality.
- Review the EMQ score and recommendations.
- If the score is below 6, add more user identity signals via
_df.identify().
Remove the test_event_code before going to production. Events sent with a test code are not used for ad optimisation or reporting.
Deduplication
If you are running both Signal server-side delivery and the Meta Pixel client-side (during a migration period), you must deduplicate events to prevent double-counting.
Signal includes a unique event_id with every event. To deduplicate:
- Pass the same
event_idto both the client-side Meta Pixel and Datafly.js. - Meta will automatically deduplicate events with matching
event_nameandevent_idreceived within a 48-hour window.
// Generate a shared event ID
const eventId = crypto.randomUUID();
// Send via Meta Pixel (client-side)
fbq('track', 'Purchase', { value: 129.99, currency: 'USD' }, { eventID: eventId });
// Send via Datafly.js (server-side)
_df.track("Order Completed", {
event_id: eventId,
total: 129.99,
currency: "USD"
});Once you have validated that server-side events are arriving correctly and your EMQ score is healthy, you can remove the client-side Meta Pixel entirely. This eliminates the client-side tag, improves page performance, and removes the deduplication requirement.
Troubleshooting
| Problem | Solution |
|---|---|
| Events not appearing in Events Manager | Check the access token has ads_management permission. Verify the Pixel ID is correct. |
| Low Event Match Quality | Add user identity signals via _df.identify(). Ensure fbp and fbc cookies are being set. |
OAuthException errors | The access token may have been revoked or the system user lacks permissions. Regenerate the token. |
| Duplicate events | If running both client-side and server-side, ensure matching event_id values for deduplication. |
Invalid parameter errors | Check that required fields (event_name, event_time, action_source) are present. Review the payload in Signal’s event debugger. |
Rate Limits
Meta’s Conversions API supports high throughput:
| Limit | Value |
|---|---|
| Events per API call | 1,000 (batched) |
| Sustained rate | ~1,000 events/second |
Signal batches events automatically when batching is enabled for the integration. For most deployments, the default settings are sufficient.