Skip to content

Facebook Catalog & Conversions API

Flow ID: IN-07 | Module(s): feeds, MetaConversionsApi, DeferredTask | Complexity: High Last Updated: 2026-05-07

Business Overview

This integration has two distinct components:

  1. Facebook Catalog Feed -- An XML product feed for the Facebook/Meta Commerce Manager. The controller (AdvFacebookCatalog) extends AdvGoogleCatalog, inheriting 95% of the RSS 2.0 generation logic and overriding only the product ID transformation and security/config keys.

  2. Meta Conversions API (CAPI) -- Server-side event tracking that sends 7 actively dispatched event types (plus 1 handler-only type) to Meta's Graph API. Events are dispatched via the DeferredTaskRunner (post-response execution) with circuit breaker protection, ensuring zero impact on TTFB. The EventType enum defines 17 standard Meta event types; 8 have createCustomData handlers, 7 of which are wired to controllers. This provides server-side deduplication with the browser-based Meta Pixel.

API Reference

Catalog Feed Endpoint

MethodURLAuthResponse
GET/xml/facebook-catalogToken query param (optional)RSS 2.0 XML

Same query parameters as Google Merchant: token, brand, category, top, stock, curr.

Conversions API Events

All events are sent server-to-server to Meta's Graph API (graph.facebook.com/v{version}/{pixel_id}/events).

EventTrigger LocationPriorityCustom Data
AddToCartAdvApiCartControllerNORMAL (50)content_ids, contents (id/qty/price), content_name, content_type, currency, value
PurchaseAdv_checkoutCRITICAL (100)content_ids, contents, currency, delivery_category, order_id, value
InitiateCheckoutAdv_orderCRITICAL (100)content_ids, contents, currency, num_items, value
SearchAdv_searchNORMAL (50)search_string
ViewContentAdv_products, Adv_vendorsNORMAL (50)content_ids, content_name, content_type
CompleteRegistrationAdv_customerNORMAL (50)email, status
AddToWishlistAdv_customerNORMAL (50)content_ids, content_name, content_type, contents, currency, value
ContactNot wired (handler only)--email

Code Flow

Catalog Feed

Controller Inheritance

AdvXml -> AdvGoogleCatalog -> AdvFacebookCatalog -> Facebook_catalog

AdvFacebookCatalog overrides four methods from AdvGoogleCatalog:

  1. __construct(): Uses xmlDbIdKey['facebookCatalog'] and reads XML_FEEDS.CUSTOM_PRICE_FACEBOOK
  2. secureXml(): Checks IS_ENABLED_FACEBOOK, IS_PROTECTED_FACEBOOK, FACEBOOK_TOKEN
  3. parseItem(): Calls parent::parseItem() then overrides g:id with productIdToFaceBookId() (Facebook-specific prefix)
  4. getDbData(): Calls product_model->getFeedProductsForFacebook($brandId, $categoryId, $topSellers, $topStock) with optional query param filters (brand, category, top, stock)

Product ID Generation

php
// ecommercen/helpers/tracking_helper.php
function productIdToFaceBookId($productId) {
    return registry->value('TRACKING', 'PRODUCT_ID_PREFIX_FACEBOOK') . $productId;
}

Conversions API

Service Initialization

FacebookConversionService.__construct()
  1. Check FACEBOOK_CONVERSION.ENABLED registry flag
  2. In non-production: require PIXEL_TESTING_CODE (test mode)
  3. Load PIXEL_ID + PIXEL_ACCESS_TOKEN
  4. Initialize Meta Business SDK: Api::init(pixelId, null, accessToken, $log_crash=false)
     (false disables the SDK CrashReporter which otherwise writes to php://stdout per request)
  5. Configure custom HTTP client: MetaHttpClient (timeout, connectTimeout)
  6. Create Config VO and MetaClient

Event Dispatch Flow

Controller (e.g., Adv_checkout)
  -> Build $customData array (event-specific payload)
  -> Build $userData array (customer PII, or null for anonymous)
  -> di()->get('deferred_task')->defer(
       task: fn() => $fb->dispatchEvent(EventType::PURCHASE, $customData, $userDataArr),
       maxCostSeconds: $fb->getMaxCostSeconds(),
       priority: DeferredTaskRunner::PRIORITY_CRITICAL,
       name: 'meta.purchase',
     )

After fastcgi_finish_request() (response already sent to client):

DeferredTaskRunner->run()
  -> flushResponse()             # fastcgi_finish_request / litespeed_finish_request
  -> Execute tasks by priority (CRITICAL=100 > NORMAL=50 > LOW=10)
  -> FacebookConversionService->dispatchEvent()
     -> circuitBreaker->isOpen()     # fail fast if breaker tripped
     -> createEvent()                # build Event with UserData + CustomData
     -> MetaClient->dispatch()       # EventRequest->execute() to Graph API
     -> circuitBreaker->recordSuccess() or recordFailure()

User Data Collection

The service collects user data at two levels:

Anonymous users (no $userData passed):

  • external_id: From fb_session_user session variable
  • client_ip_address: From CI_Input->ip_address()
  • client_user_agent: From constructor-injected user agent string
  • fbc: From _fbc cookie (or generated from fbclid query param)
  • fbp: From _fbp cookie (or server-generated via Fbp VO)

Authenticated users (full $userData array):

  • All anonymous fields plus: email, phone, first/last name, city, state, country, zip, birthdate (parsed to dobd/dobm/doby), f5first/f5last (first 5 chars), fi (first initial)
  • fb_login_id: Set when login_referrer session variable is 'facebook' and oauth2_id session variable is present (Facebook OAuth2 login)
CookieFormatTTLPurpose
_fbpfb.{subdomain}.{timestamp_ms}.{random}Set by browser pixelFacebook browser parameter
_fbcfb.{subdomain}.{timestamp_ms}.{fbclid}90 daysFacebook click identifier

If _fbc is absent but fbclid query param exists, the service generates and sets the _fbc cookie in fb.1.{timestamp_ms}.{fbclid} format.

If _fbp is absent, the server generates a synthetic value via the Fbp value object: fb.1.{timestamp_ms}.{random_int} (random between 1,000,000,000 and 1,999,999,999).

Architecture

File Map

Catalog Feed

FilePurpose
application/controllers/Facebook_catalog.phpThin CI router wrapper
ecommercen/feeds/controllers/AdvFacebookCatalog.phpFeed controller -- extends Google, overrides ID/security
ecommercen/feeds/controllers/AdvGoogleCatalog.phpParent feed controller (inherited)
src/Feeds/Output/OutputGoogleMerchantXml.phpRSS 2.0 output (shared with Google)

Conversions API

FilePurpose
src/MetaConversionsApi/Service/FacebookConversionService.phpMain service -- init, dispatch, event creation, user data
src/MetaConversionsApi/Client/ClientInterface.phpClient contract (dispatch, dispatchSafe)
src/MetaConversionsApi/Client/MetaClient.phpGraph API client -- EventRequest->execute()
src/MetaConversionsApi/Client/MetaHttpClient.phpCustom cURL HTTP client with configurable timeouts
src/MetaConversionsApi/Enum/EventType.phpString-backed enum: 17 Meta event types
src/MetaConversionsApi/Enum/ActionSource.phpString-backed enum: website, email, phone_call, etc.
src/MetaConversionsApi/Pixel/Config.phpConfig VO (pixelId, accessToken, testingCode)
src/MetaConversionsApi/Pixel/Pixel.phpPixel VO (id, nullable accessToken)
src/MetaConversionsApi/ValueObject/Fb.phpAbstract base for fbp/fbc value objects
src/MetaConversionsApi/ValueObject/Fbp.php_fbp cookie format (fb.subdomain.time.random)
src/MetaConversionsApi/ValueObject/Fbc.php_fbc cookie format (fb.subdomain.time.clickId)

Deferred Task Infrastructure

FilePurpose
src/DeferredTask/DeferredTaskRunner.phpPriority queue runner with time budget, post-response execution
src/DeferredTask/DeferredTask.phpTask VO (callback, maxCostSeconds, priority, name)
application/config/container/deferred_task.phpDI registration with configurable budget/logger
application/config/container/circuit_breakers.phpDI registration for circuit_breaker.meta (and 4 other named breakers)
application/config/circuit_breakers.phpThreshold, cooldown, multiplier config per circuit breaker

Event Dispatch Locations

ControllerEventDeferred Task Name
ecommercen/api/controllers/AdvApiCartController.phpAddToCartmeta.add_to_cart
ecommercen/checkout/controllers/Adv_checkout.phpPurchasemeta.purchase
ecommercen/eshop/controllers/Adv_order.phpInitiateCheckoutmeta.initiate_checkout
ecommercen/search/controllers/Adv_search.phpSearchmeta.search
ecommercen/eshop/controllers/Adv_products.phpViewContentmeta.view_content
ecommercen/eshop/controllers/Adv_vendors.phpViewContentmeta.view_content
ecommercen/eshop/controllers/Adv_customer.phpCompleteRegistrationmeta.complete_registration
ecommercen/eshop/controllers/Adv_customer.phpAddToWishlistmeta.add_to_wishlist

Circuit Breaker Integration

The FacebookConversionService receives an optional CircuitBreaker instance. The Meta circuit breaker is registered as circuit_breaker.meta in application/config/container/circuit_breakers.php alongside four other named breakers (matomo, manago, project_agora, advisable_ai). Each breaker's threshold, cooldown, max cooldown, multiplier, and TTL buffer are loaded from application/config/circuit_breakers.php config keys (e.g., cb_meta_threshold, cb_meta_cooldown_seconds). The breaker uses the L2 cache adapter (cache.l2) for state persistence.

When Meta's API becomes unresponsive:

  1. Failures accumulate against the threshold (configurable, default 3)
  2. Circuit opens -- all subsequent dispatchEvent() calls return immediately (isOpen() check)
  3. After cooldown, a single probe request is allowed through (half-open state); the probe slot is claimed atomically via storeIfAbsent to prevent concurrent probes
  4. On probe success, circuit closes and all state is deleted; on failure, reopens with exponentially increased cooldown (multiplier, capped at maxCooldownSeconds)

This prevents cascading timeouts when Meta's infrastructure is degraded, keeping the deferred task budget available for other tasks.

DeferredTaskRunner Mechanics

  • Priority queue: Tasks execute in priority order (CRITICAL=100 first)
  • Time budget: Configurable via DI container (default 10s); tasks declare maxCostSeconds
  • Post-response: Runs after fastcgi_finish_request() -- invisible to end users
  • Budget enforcement: Tasks exceeding remaining budget are skipped (logged as notice)
  • Error isolation: Failed tasks do not block subsequent tasks

Data Model

Custom Data per Event Type

EventFieldsNotes
AddToCartcontent_ids, contents[], content_name, content_type, currency, valuecontents has id/qty/item_price per SKU
Purchasecontent_ids, contents[], currency, delivery_category, order_id, valuedelivery_category: in_store or home_delivery
InitiateCheckoutcontent_ids, contents[], currency, num_items, valueAggregated cart data
Searchsearch_stringSearch query text
ViewContentcontent_ids, content_name, content_typeProduct page identifiers
CompleteRegistrationemail, statusRegistration outcome
AddToWishlistcontent_ids, content_name, content_type, contents[], currency, valueSame structure as AddToCart
ContactemailHandler defined but not wired to any controller; available for client extension

Event Payload Structure

Each event sent to Meta includes:

json
{
  "event_name": "Purchase",
  "event_time": 1712188800,
  "event_source_url": "https://example.com",
  "event_id": "{random_32_hex}",
  "action_source": "website",
  "opt_out": false,
  "data_processing_options": [],
  "custom_data": { ... },
  "user_data": {
    "em": "{sha256_email}",
    "ph": "{sha256_phone}",
    "fn": "{sha256_first_name}",
    "ln": "{sha256_last_name}",
    "ct": "{sha256_city}",
    "st": "{sha256_state}",
    "zp": "{sha256_zip}",
    "country": "{sha256_country}",
    "external_id": "{session_id}",
    "client_ip_address": "{ip}",
    "client_user_agent": "{ua}",
    "fbc": "fb.1.{ts}.{clickid}",
    "fbp": "fb.1.{ts}.{random}"
  }
}

Note: The Meta Business SDK handles PII hashing (SHA-256) automatically via the UserData setters.

Configuration

Registry Keys (FACEBOOK_CONVERSION Group)

KeyTypePurpose
ENABLEDboolMaster toggle for Conversions API
PIXEL_IDstringMeta Pixel ID
PIXEL_ACCESS_TOKENstringServer-side access token
PIXEL_TESTING_CODEstringTest event code (required in non-production)
TIMEOUTintHTTP request timeout in seconds (default: 5)
CONNECT_TIMEOUTintHTTP connection timeout in seconds (default: 3)

Registry Keys (XML_FEEDS Group)

KeyTypePurpose
IS_ENABLED_FACEBOOKboolFeed toggle
IS_PROTECTED_FACEBOOKboolEnable token auth
FACEBOOK_TOKENstringExpected token value
CUSTOM_PRICE_FACEBOOKboolEnable custom price modifier

Registry Keys (TRACKING Group)

KeyTypePurpose
PRODUCT_ID_PREFIX_FACEBOOKstringPrefix for Facebook product IDs

DI Container

The deferred_task service is registered in application/config/container/deferred_task.php:

  • $timeBudgetSeconds: From deferred_task_budget_seconds config
  • $enabled: From deferred_task_enabled config
  • $logger: service(\Advisable\Logger\NamedLoggerInterface::class) — the same single Monolog logger every component shares (application/config/container/deferred_task.php:20). The runner calls $logger?->withName('deferred-task') so its records carry %channel% = deferred-task.

The FacebookConversionService itself is not registered explicitly — it is instantiated on demand by Adv_front_controller::__construct at ecommercen/core/Adv_front_controller.php:116-123. Its NamedLoggerInterface $logger (src/MetaConversionsApi/Service/FacebookConversionService.php:47) is fetched via di()->get(\Advisable\Logger\NamedLoggerInterface::class)->withName('facebook-conversion') at the call site, so service-internal log records carry %channel% = facebook-conversion. The MetaClient constructed inside the service uses its own channel meta-conversions-api (src/MetaConversionsApi/Client/MetaClient.php:28).

Environment Requirements

  • Non-production environments require PIXEL_TESTING_CODE to be set (events sent with test event code for debugging in Meta Events Manager)
  • Production environments can leave PIXEL_TESTING_CODE empty

Client Extension Points

Catalog Feed

  1. Override controller: Subclass AdvFacebookCatalog in application/controllers/ to customize field mapping
  2. Custom price modifier: Enable XML_FEEDS.CUSTOM_PRICE_FACEBOOK for per-product markup
  3. Product ID prefix: Configure TRACKING.PRODUCT_ID_PREFIX_FACEBOOK for catalog-pixel ID alignment

Conversions API

  1. Custom HTTP client: Implement ClientInterface and inject via DI to replace MetaClient
  2. Additional events: The EventType enum defines 17 event types; only 7 are actively dispatched (Contact has a handler but is not wired) -- additional types can be wired from custom controllers by calling dispatchEvent() with the appropriate EventType
  3. Circuit breaker tuning: Adjust threshold, cooldown, and backoff parameters via DI container configuration
  4. Timeout tuning: Configure TIMEOUT and CONNECT_TIMEOUT registry keys per-client based on network conditions

Business Rules

Catalog Feed

  1. Feed disabled: Returns 403 when IS_ENABLED_FACEBOOK is falsy
  2. ID format: Uses Facebook-specific prefix (may differ from Google prefix) for cross-platform ID consistency
  3. Inherited behavior: All Google Merchant field mapping, sale price logic, and image handling apply identically

Conversions API

  1. Environment gate: In non-production, service is disabled unless PIXEL_TESTING_CODE is set
  2. Missing config: If PIXEL_ID or PIXEL_ACCESS_TOKEN is empty, all dispatches silently return
  3. Circuit breaker: When open, all events are dropped silently (no logging, no error)
  4. Priority ordering: Purchase and InitiateCheckout events use PRIORITY_CRITICAL (100) to ensure they execute first within the budget
  5. Time budget: Each Meta task declares maxCostSeconds = timeout + connectTimeout (default 8s). If the deferred task runner's budget is exhausted, remaining Meta events are skipped (logged at notice level)
  6. Event deduplication: Each event gets a unique event_id (32-byte random hex) for browser-server deduplication
  7. Cookie fallback: Server generates synthetic _fbp/_fbc values when cookies are absent, ensuring Meta can still attribute events
  8. Error isolation: A failed dispatch is caught, logged, and recorded as a circuit breaker failure -- subsequent events still execute

Known Issues & Security Gaps

No known issues at this time.

Wiki Guide: Circuit Breaker Guide -- circuit breaker pattern used by CAPI dispatchers