Magento 2: OpenAI Ads Pixel and CAPI for Magento 2 (Adobe Commerce): Complete Setup
Anyone who runs enterprise stores on Magento 2 (Adobe Commerce) knows how easily frontend tracking falls apart. You have Varnish Full Page Cache (FPC) aggressively caching static templates, complex single-page checkouts, and external payment gateways (like Adyen, Cybersource, or PayPal) that redirect customers across domains. If a customer pays on PayPal and never clicks "Return to Merchant", your checkout_onepage_success block never renders.
The order exists in Magento, but OpenAI Ads Manager never hears about it.
In high-volume B2B or consumer stores, missing 15% of your conversions skews bidding models and inflates reported customer acquisition cost (CAC). To capture every dollar, Magento requires a split setup: layout XML or Hyvä components for storefront browsing, and an asynchronous backend observer on sales_order_place_after that sends purchase data directly to OpenAI's Conversions API (CAPI).
1. Event Mapping Architecture for Adobe Commerce
| Customer Funnel Action | Magento 2 Event / Layout | OpenAI Ads Event | Delivery Method |
|---|---|---|---|
| Catalog Category View | catalog_category_view | page_viewed | Browser Pixel |
| Product Details Page | catalog_product_view | contents_viewed | Browser Pixel |
| Add to Cart Action | Customer Data Section (cart) | items_added | Browser Pixel |
| Initiate OnePage Checkout | checkout_index_index | checkout_started | Browser Pixel |
| Order Confirmation Screen | checkout_onepage_success | order_created | Browser Pixel |
| Order Created in Database | sales_order_place_after | order_created | Server CAPI (Queue Worker) |
2. Front-End Pixel Installation (Luma & Hyvä)
A. Layout XML Injection (default_head_blocks.xml)
Inject the base pixel asynchronously in your custom theme's Magento_Theme/layout/default_head_blocks.xml:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<script src="https://bzrcdn.openai.com/pixel.js" src_type="url"/>
<link src="Comercio_OpenAiAds::js/pixel-init.js"/>
</head>
</page>
In pixel-init.js:
!function(w,d,e,v,n,t,s){if(w.oaiq)return;n=w.oaiq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!w._oaiq)w._oaiq=n;
n.push=n;n.loaded=!0;n.version='1.0';n.queue=[];t=d.createElement(e);t.async=!0;
t.src=v;s=d.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}
(window,document,'script','https://bzrcdn.openai.com/pixel.js');
oaiq('init', 'YOUR_OPENAI_PIXEL_ID');
oaiq('event', 'page_viewed');
B. Success Page Browser Tracking
In your checkout success block template (checkout_onepage_success.phtml), output the order confirmation with the order's increment ID as the deduplication key:
<?php
/** @var \Magento\Checkout\Block\Onepage\Success $block */
$order = $block->getOrder();
if ($order && $order->getId()):
$incrementId = $order->getIncrementId();
$amountCents = (int)round(((float)$order->getBaseGrandTotal()) * 100);
$currency = $order->getBaseCurrencyCode();
$dedupKey = 'magento_order_' . $incrementId;
?>
<script>
if (window.oaiq) {
window.oaiq('event', 'order_created', {
order_id: '<?= $block->escapeJs($incrementId); ?>',
amount: <?= $amountCents; ?>,
currency: '<?= $block->escapeJs($currency); ?>',
event_id: '<?= $block->escapeJs($dedupKey); ?>'
});
}
</script>
<?php endif; ?>
3. Server-Side CAPI Observer on sales_order_place_after
Do not dispatch synchronous HTTP requests directly inside your checkout controller. If OpenAI's API latency spikes, your customer's checkout will freeze. Instead, use an event observer to stage the payload, or dispatch it via Magento's asynchronous Message Queue (RabbitMQ or DB-backed queues).
A. Register the Observer (etc/events.xml)
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="sales_order_place_after">
<observer name="comercio_openai_capi_order" instance="Comercio\OpenAiAds\Observer\OrderPlaceAfterObserver" />
</event>
</config>
B. Observer Implementation (OrderPlaceAfterObserver.php)
<?php
namespace Comercio\OpenAiAds\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\HTTP\Client\Curl;
use Psr\Log\LoggerInterface;
class OrderPlaceAfterObserver implements ObserverInterface
{
private Curl $curl;
private LoggerInterface $logger;
public function __construct(Curl $curl, LoggerInterface $logger)
{
$this->curl = $curl;
$this->logger = $logger;
}
public function execute(Observer $observer)
{
/** @var \Magento\Sales\Model\Order $order */
$order = $observer->getEvent()->getOrder();
if (!$order) return;
try {
$pixelId = 'YOUR_OPENAI_PIXEL_ID';
$capiKey = 'YOUR_OPENAI_CAPI_KEY';
$endpoint = "https://bzr.openai.com/v1/events?pid=" . $pixelId;
$incrementId = $order->getIncrementId();
$amountCents = (int)round(((float)$order->getBaseGrandTotal()) * 100);
$billingAddress = $order->getBillingAddress();
$email = strtolower(trim((string)$order->getCustomerEmail()));
$phone = preg_replace('/\D/', '', (string)$billingAddress->getTelephone());
$dedupKey = 'magento_order_' . $incrementId; // Matches browser event_id
$payload = [
'validate_only' => false,
'events' => [
[
'id' => 'srv_magento_' . $incrementId . '_' . time(),
'type' => 'order_created',
'timestamp_ms' => (int)round(microtime(true) * 1000),
'action_source' => 'website',
'event_id' => $dedupKey,
'user_data' => [
'em' => hash('sha256', $email),
'ph' => !empty($phone) ? hash('sha256', $phone) : null,
'client_ip_address' => $order->getRemoteIp(),
'country' => strtolower((string)$billingAddress->getCountryId())
],
'data' => [
'amount' => $amountCents,
'currency' => $order->getBaseCurrencyCode(),
'order_id' => (string)$incrementId
]
]
]
];
$this->curl->setHeaders([
'Authorization' => 'Bearer ' . $capiKey,
'Content-Type' => 'application/json'
]);
$this->curl->setTimeout(4);
$this->curl->post($endpoint, json_encode($payload));
} catch (\Exception $e) {
$this->logger->error('OpenAI CAPI Error: ' . $e->getMessage());
}
}
}
4. Deduplication Logic
Deduplication keeps your accounting clean when both frontend and backend report the order.
By using magento_order_ followed by the increment ID (e.g., magento_order_100004521), both pipelines supply identical event_id strings. When OpenAI processes both records, it combines the browser's ad click history with the server's validated order amount. If the browser tag fails to fire because of an ad blocker or gateway redirect, your server observer ensures zero revenue drop.
Looking to audit your enterprise Adobe Commerce tracking pipeline? Explore our Done-For-You Ecommerce Tracking service.
Want this done for you?
Fixed-scope setup, tested end-to-end and documented.
Explore the serviceTake the free audit