IntegrationsAdvertisingMeta Conversions API (CAPI)

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.

API Endpoint

POST https://graph.facebook.com/v18.0/{pixel_id}/events

Events are sent as a JSON array in the data field, with the access_token as a query parameter or in the request body.

Configuration

FieldRequiredDescription
pixel_idYesYour Meta Pixel ID (numeric). Found in Meta Events Manager.
access_tokenYesSystem user access token with ads_management permission. Generate in Meta Business Settings > System Users.
test_event_codeNoTest event code for validation (e.g. TEST12345). Events sent with this code appear in the Events Manager Test Events tab.

Management UI Setup

  1. Go to Integrations > Add Integration > Meta Conversions API.
  2. Enter your pixel_id and access_token.
  3. Select consent categories (typically advertising or marketing).
  4. 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"]
  }'

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

SignalFieldDescription
fbpuser_data.fbpFacebook browser ID. Datafly Signal self-generates this in the format fb.1.{timestamp}.{random} and stores it as a first-party cookie.
fbcuser_data.fbcFacebook click ID. Automatically extracted from the fbclid URL parameter and formatted as fb.1.{timestamp}.{fbclid}.
external_iduser_data.external_idSet to the Datafly anonymous_id (hashed). Provides a stable cross-session identifier.
client_ip_addressuser_data.client_ip_addressVisitor’s IP address, forwarded from the original request.
client_user_agentuser_data.client_user_agentVisitor’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:

SignalHashingDescription
em (email)SHA-256, lowercase, trimmedUser’s email address
ph (phone)SHA-256, digits only, with country codeUser’s phone number
fn (first name)SHA-256, lowercase, trimmedFirst name
ln (last name)SHA-256, lowercase, trimmedLast name
ct (city)SHA-256, lowercase, no spacesCity
st (state)SHA-256, lowercase, 2-letter codeState/region
zp (zip)SHA-256, trimmedPostal/zip code
countrySHA-256, lowercase, 2-letter ISO codeCountry

All PII hashing is performed server-side by the Delivery Worker before the data leaves your infrastructure. Raw PII is never sent to Meta.

Hashing Example

Input email: John.Doe@Example.com

Normalisation steps:

  1. Trim whitespace: John.Doe@Example.com
  2. Lowercase: john.doe@example.com
  3. SHA-256 hash: 836f82db99121b3481011f16b49dfa5fbc714a0d1b1b9f784a1ebbbf5b39577f

Event Mapping

Datafly EventMeta EventNotes
page (page view)PageViewSent for every page view
Order Completed / Product PurchasedPurchaseRequires value, currency
Product AddedAddToCartIncludes content_ids, content_type
Checkout StartedInitiateCheckoutIncludes value, currency, num_items
Product ViewedViewContentIncludes content_ids, content_type
Lead GeneratedLeadLead form submission
Signed UpCompleteRegistrationUser registration
Products SearchedSearchIncludes search_string
Product Added to WishlistAddToWishlistIncludes content_ids
Custom eventsPassed throughSent 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 Datafly:

{
  "data": [
    {
      "event_name": "Purchase",
      "event_time": 1706540000,
      "event_id": "evt_abc123def456",
      "event_source_url": "https://example.com/checkout/confirmation",
      "action_source": "website",
      "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
      }
    }
  ]
}

Action Source

The action_source field tells Meta where the event originated. For all browser-originated events collected by Datafly.js, this is always set to website.

The action_source must be website for events that originate from a user browsing your site. Using incorrect values will cause Meta to reject the event or reduce match quality.

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

  1. Send fbp and fbc: Datafly Signal handles this automatically. The fbp cookie is self-generated and fbc is extracted from the fbclid URL parameter.
  2. Include user data: Pass email and phone via _df.identify() when users log in or submit forms. These are hashed before delivery.
  3. Send IP and User-Agent: Datafly Signal forwards these automatically from the original browser request.
  4. Use external_id: Enabled by default — the hashed anonymous_id is sent as external_id.

A typical Datafly Signal integration achieves an EMQ of 6-8 with automatic signals alone, and 8-10 when user-provided data (email, phone) is available.

Deduplication

If you are running both Datafly Signal server-side delivery and the Meta Pixel client-side (during a migration period, for example), you must deduplicate events to prevent double-counting.

Datafly Signal includes a unique event_id with every event. To deduplicate:

  1. Pass the same event_id to both the client-side Meta Pixel and Datafly.js.
  2. Meta will automatically deduplicate events with matching event_name and event_id received 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"
});
⚠️

Deduplication only works when the event_name and event_id match exactly. If you are migrating from client-side to server-side, plan to remove the client-side Meta Pixel once server-side delivery is validated.

Test Events

Use the test_event_code configuration to send events to Meta’s test events tool without affecting production data:

{
  "config": {
    "pixel_id": "1234567890",
    "access_token": "your_access_token",
    "test_event_code": "TEST12345"
  }
}

Test events appear in Meta Events Manager > Test Events. The test event code is found in the Test Events tab of your Pixel settings.

⚠️

Remove the test_event_code before going to production. Events sent with a test code are not used for ad optimisation or reporting.

API Response

A successful response from Meta:

{
  "events_received": 1,
  "messages": [],
  "fbtrace_id": "AbCdEfGhIjKlMnOpQrStUv"
}

An error response:

{
  "error": {
    "message": "Invalid parameter",
    "type": "OAuthException",
    "code": 100,
    "error_subcode": 2804003,
    "fbtrace_id": "AbCdEfGhIjKlMnOpQrStUv"
  }
}

Rate Limits

Meta’s Conversions API has the following limits:

SettingDefault
rate_limit_rps200
rate_limit_burst400

Meta allows up to 1,000 events per API call (batched) and generally supports high throughput. The default limits are conservative and suitable for most deployments.

If you send more than 1,000 events per second sustained, consider increasing rate_limit_rps or contact your Meta representative for guidance.