Cómo Instalar el Píxel de ChatGPT Ads en Shopify (Custom Pixel, Sin Apps)

Tiendas Online

Si haces publicidad en ChatGPT (OpenAI Ads) desde tu tienda Shopify, hay un problema de fondo antes de optimizar nada: la plataforma no sabe qué pasa después del clic. ¿Compró? ¿Por cuánto? ¿Quién es? Sin esa información no hay campaña que aprenda ni presupuesto que se asigne bien. En esta guía instalamos el primero de los dos canales que resuelven esto — el Píxel del navegador — de la forma correcta: dentro de Eventos de clientes de Shopify, no pegado en el tema.

Mira el vídeo completo con la instalación en pantalla, o sigue el paso a paso escrito debajo:
¿Qué es el Custom Pixel de ChatGPT Ads y cómo se instala en Shopify? Es un script que Shopify ejecuta dentro de un sandbox aislado (Eventos de clientes) para avisar a OpenAI Ads de cada paso de la compra: vista de página, ficha de producto, carrito, checkout y pedido confirmado. Se instala desde Configuración → Eventos de clientes → Add custom pixel, nunca en theme.liquid, porque el tema no cubre la página de pago.

El problema de fondo: medir la conversión

Para que OpenAI Ads pueda optimizar campañas, necesita saber qué pasa después de que alguien hace clic en un anuncio. A eso se le llama conversión, y para medirla bien hacen falta dos canales que se complementan:
  • El Píxel (navegador): un script que corre en el ordenador o móvil del visitante. Es el que cubre esta guía.
  • La Conversions API / CAPI (servidor): una llamada que hace directamente el servidor a OpenAI. Se cubre en la Parte 2 de esta serie.
¿Por qué los dos? Porque el pixel del navegador puede fallar — bloqueadores de anuncios, Safari, cookies rechazadas. El servidor nunca falla por esas causas. Usar ambos a la vez da el dato más completo y fiable posible.

Paso 0 — Crear el Pixel en OpenAI Ads Manager

Antes de tocar Shopify, hace falta el pixel dado de alta en la cuenta de anuncios: en Ads Manager ve a Conversiones → Fuente de datos → Crear y genera un pixel nuevo. Copia el «ID del pixel» — lo necesitarás en el paso siguiente.
¿Y el «evento de conversión»? No hace falta todavía. Ads Manager tiene otra pestaña donde se nombra un tipo de dato (por ejemplo «Order Created») para seleccionarlo luego en una campaña. Eso no es necesario ni para instalar ni para verificar el pixel — la herramienta de verificación («Flujo de eventos») capta los datos en bruto igual. Se retoma más adelante, al crear la campaña.
Ignora el «Código de configuración» de esa pantalla. Justo después de crear el pixel, Ads Manager muestra un script genérico pensado para cualquier web. No sabe que vas a usarlo en un Custom Pixel de Shopify, así que no incluye el funnel completo, el hasheo de usuario ni la deduplicación. De esa pantalla solo copia el ID del pixel — el código real es el del Anexo, más abajo.

Paso 1 — Instalar el Custom Pixel en Shopify

No lo pegues en theme.liquid. Existe otra forma de instalar el pixel pegando el script directamente en el tema. No la uses: no llega a la página de pago (el checkout no forma parte del tema) y no respeta el consentimiento de cookies del visitante.

Dónde se instala

En el admin de Shopify: Configuración → Eventos de clientes → Add custom pixel.
  1. Ponle un nombre identificable, por ejemplo «ChatGPT Ads».
  2. Pulsa «Add pixel» para crearlo (queda vacío, listo para configurar).
  3. Verás dos secciones antes del código: «Permission» y «Data sale».

Qué marcar en Privacidad del cliente

Estas opciones determinan cuándo se le permite al pixel recopilar datos, respetando el consentimiento del visitante (RGPD en la UE, CCPA en California):
SecciónOpciónQué marcar
PermisoObligatorioMarcado — el pixel solo actúa con el permiso del visitante
PermisoMarketing — categoría correcta para atribución publicitaria
PermisoInformes y estadísticasNo — este pixel no es analítica interna
PermisoPreferenciasNo aplica
Venta de datos«Los datos califican como venta de datos» — clasificación estándar para pixeles de terceros
Aviso legal: esta es la práctica estándar del sector para este tipo de pixel, pero no sustituye asesoría legal. Si tu negocio tiene un asesor de privacidad/RGPD propio, confírmalo con esa persona antes de publicar en producción.

El código a pegar

Baja hasta el campo «Code» del propio Custom Pixel y pega el contenido completo del Anexo, al final de este artículo. Antes de guardar, sustituye un único valor: <TU-PIXEL-ID> por el Pixel ID copiado en el Paso 0. El resto del código no necesita ningún cambio — ya reconoce automáticamente vistas de página, vistas de producto, añadidos al carrito, inicio de checkout y compra completada.
Pega el código UNA sola vez. Si el campo ya tenía algo de una prueba anterior, bórralo todo antes de pegar la versión nueva. Tenerlo repetido duplica cada evento y la segunda copia puede reinicializar el pixel con un ID equivocado, invalidando la primera. Antes de guardar, busca con Ctrl+F la cadena «<TU-PIXEL-ID>» en el campo — si aparece, todavía queda un resto de una copia vieja.

Qué cubre este pixel

Evento de ShopifyEvento enviado a OpenAIQué mide
page_viewedpage_viewedVisitas a páginas
product_viewedcontents_viewedAlguien mira una ficha de producto
product_added_to_cartitems_addedAñadir al carrito
checkout_startedcheckout_startedEmpieza el proceso de pago
checkout_completedorder_createdCompra confirmada

Cómo mejora la coincidencia de usuario (email, teléfono, nombre)

Cuando alguien navega sin haber iniciado sesión — la inmensa mayoría de los visitantes — no hay ningún dato de usuario que enviar todavía. Por eso el código refuerza el objeto «user» en dos momentos clave del checkout:
  • En checkout_started: en cuanto Shopify expone email, teléfono o dirección de quien paga, se hashean en SHA-256 y se reenvían.
  • En checkout_completed: se repite el refuerzo, más un external_id (el ID interno de cliente de Shopify) que solo existe aquí, porque el pedido no se crea hasta que la compra se confirma.
El external_id no siempre podrá rellenarse, y no es un fallo. Un comprador invitado que nunca crea una cuenta a veces no genera un «customer» asociado al pedido — no es un error del código, es que ese dato no existe en origen. El email, en cambio, debería aparecer en la gran mayoría de los pedidos, porque Shopify no permite completar una compra sin él.

Paso 2 — Comprobar que funciona

El propio Custom Pixel incluye un «Pixel Helper» que muestra en tiempo real qué eventos se han recibido mientras navegas la tienda:
  1. Abre la tienda en una pestaña nueva: entra a una página de producto, añade algo al carrito, empieza el checkout.
  2. Con el Pixel Helper abierto, confirma que aparecen page_viewed, product_viewed, items_added y checkout_started según avanzas.
  3. Completa un pedido de prueba (pago manual, sin coste real) y confirma que aparece checkout_completed en la página de agradecimiento.
  4. En paralelo, en Ads Manager → Conversiones → Flujo de eventos, con «Iniciar sondeo» activo, deberías ver los mismos eventos llegando por el canal pixel_sdk.
Resumen en una frase: instalamos un pixel dentro de Eventos de clientes de Shopify (no en el tema) que avisa a OpenAI de cada paso del recorrido de compra — desde que alguien entra a la tienda hasta que confirma un pedido — respetando siempre el consentimiento de cookies del visitante.
Sobre la Parte 2 de esta serie: el pixel del navegador no basta por sí solo — cualquier visitante que rechace las cookies o use un bloqueador de anuncios nunca generará estos eventos, aunque compre. La Parte 2 monta el canal complementario: un Webhook de Shopify + un Cloudflare Worker propio que confirma cada compra directamente desde el servidor, sin depender del navegador.

Preguntas frecuentes

¿Puedo instalar el pixel de ChatGPT Ads pegándolo en theme.liquid?

No. El checkout de Shopify no forma parte del tema, así que ese método nunca llega a la página de pago y no respeta el consentimiento de cookies. El único método correcto es un Custom Pixel creado desde Configuración → Eventos de clientes.

¿Hace falta crear un «evento de conversión» en Ads Manager antes de instalar el pixel?

No. Ese paso solo sirve para poder seleccionar el evento al configurar una campaña. La verificación del pixel mediante «Flujo de eventos» capta los datos en bruto, con o sin un evento de conversión creado.

¿Por qué el pixel del navegador no es suficiente por sí solo?

Porque bloqueadores de anuncios, Safari o el rechazo de cookies pueden impedir que se dispare, aunque el visitante compre. Combinarlo con una Conversions API del lado del servidor (Parte 2 de esta serie) cubre esos casos y da el dato más completo posible.

¿Qué pasa si el external_id no aparece en un pedido?

No es un error del código. Un comprador invitado que nunca crea una cuenta a veces no genera un «customer» en Shopify asociado a ese pedido concreto, así que no hay ID que hashear. El email sí debería aparecer en la gran mayoría de los pedidos.
¿Prefieres que te lo instale y verifique yo mismo?Reviso tu cuenta de Ads Manager, configuro el Custom Pixel y confirmo que los eventos llegan correctamente. Contáctame

Anexo — Código completo del Custom Pixel

Aquí sí hay algo que tocar (solo una cosa). Este código corre en el navegador y no tiene acceso a variables secretas externas — por eso el Pixel ID va escrito directamente en el código. Busca la línea var OPENAI_PIXEL_ID = "<TU-PIXEL-ID>"; y sustituye <TU-PIXEL-ID> (con las comillas incluidas) por el Pixel ID copiado en el Paso 0. Es la única línea que cambia en todo el archivo.
Código a pegar en Configuración → Eventos de clientes → Custom Pixel → campo «Code»:
/**
* Custom Pixel de Shopify — ChatGPT Ads (OpenAI Ads)
* Instalar en: Shopify Admin > Settings > Customer events > Add custom pixel
*
* Sustituye <TU-PIXEL-ID> por el Pixel ID real de tu cuenta de Ads Manager
* (Conversiones > Fuente de datos > tu pixel > "ID del pixel").
*
* IMPORTANTE: en un Custom Pixel creado desde el Admin, `analytics`, `browser`
* e `init` ya existen como variables GLOBALES del sandbox — NO se usa
* `register(...)` (eso es solo para App Pixel Extensions distribuidas como
* apps de Shopify). Tampoco hay acceso al `oaiq` de la página principal
* (el sandbox está aislado), así que el SDK hay que cargarlo aquí dentro.
*/
var OPENAI_PIXEL_ID = "<TU-PIXEL-ID>";
var OPENAI_DEBUG = true; // pon esto a false cuando ya esté todo confirmado
// ---------------------------------------------------------------------
// 0. Cargar el SDK de OpenAI dentro del propio sandbox del Custom Pixel
// ---------------------------------------------------------------------
(function (w, d, s, u) {
if (w.oaiq) return;
var q = function () {
q.q.push(arguments);
};
q.q = [];
w.oaiq = q;
var js = d.createElement(s);
js.async = true;
js.src = u;
var f = d.getElementsByTagName(s)[0];
f.parentNode.insertBefore(js, f);
})(window, document, "script", "https://bzrcdn.openai.com/sdk/oaiq.min.js");
// ---------------------------------------------------------------------
// 1. Utilidades
// ---------------------------------------------------------------------
async function sha256Hex(value) {
if (!value) return undefined;
var enc = new TextEncoder();
var buf = await crypto.subtle.digest("SHA-256", enc.encode(value));
return Array.prototype.map
.call(new Uint8Array(buf), function (b) { return b.toString(16).padStart(2, "0"); })
.join("");
}
function normalizeEmail(email) {
return email ? email.trim().toLowerCase() : undefined;
}
// Deja solo dígitos, quita ceros/+ iniciales, tal como pide OpenAI para el teléfono.
function normalizePhone(phone) {
if (!phone) return undefined;
var digits = phone.replace(/[\s().-]/g, "").replace(/^\+/, "").replace(/^0+/, "");
return digits || undefined;
}
// Minúsculas, sin espacios ni puntuación, conservando acentos/ñ tal como pide OpenAI.
function normalizeName(name) {
if (!name) return undefined;
return name.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "") || undefined;
}
function toMinorUnits(amount) {
var n = parseFloat(amount);
return isFinite(n) ? Math.round(n * 100) : undefined;
}
function extractNumericOrderId(rawId) {
if (!rawId) return undefined;
var match = String(rawId).match(/(\d+)$/);
return match ? match[1] : String(rawId);
}
// Extrae el precio unitario de una variante, venga de donde venga (product_viewed, cartLine, lineItem de checkout).
function unitPriceOf(item) {
var variant = item.variant || item.merchandise || {};
return variant.price || {};
}
function toContentItem(item) {
var variant = item.variant || item.merchandise || {};
var product = variant.product || {};
var price = unitPriceOf(item);
return {
id: String(variant.sku || variant.id || item.id || ""),
name: product.title || variant.title || item.title || "",
content_type: "product",
quantity: item.quantity || 1,
amount: toMinorUnits(price.amount),
currency: price.currencyCode,
};
}
/**
* Construye el objeto "user" hasheado a partir de los datos de checkout
* (email, teléfono, nombre, dirección) y/o de un cliente ya identificado.
* Devuelve {} si no hay ningún dato disponible.
*/
async function buildUserObject(customer, checkout) {
var address = (checkout && (checkout.billingAddress || checkout.shippingAddress)) || {};
var email = normalizeEmail((checkout && checkout.email) || (customer && customer.email));
var phone = normalizePhone((checkout && checkout.phone) || (customer && customer.phone));
var firstName = normalizeName(address.firstName || (customer && customer.firstName));
var lastName = normalizeName(address.lastName || (customer && customer.lastName));
var externalId = customer && customer.id ? String(customer.id) : undefined;
var user = {};
var emailHash = await sha256Hex(email);
var phoneHash = await sha256Hex(phone);
var firstNameHash = await sha256Hex(firstName);
var lastNameHash = await sha256Hex(lastName);
var idHash = await sha256Hex(externalId);
if (emailHash) user.email_sha256 = emailHash;
if (phoneHash) user.phone_number_sha256 = phoneHash;
if (firstNameHash) user.first_name_sha256 = firstNameHash;
if (lastNameHash) user.last_name_sha256 = lastNameHash;
if (idHash) user.external_id_sha256 = idHash;
if (address.countryCode) user.country = address.countryCode;
if (address.city) user.city = address.city;
if (address.zip) user.postal_code = address.zip;
return user;
}
/** Llama a init otra vez con los datos de usuario ya disponibles (no hace falta repetir pixelId). */
async function refreshUserData(customer, checkout) {
var user = await buildUserObject(customer, checkout);
if (Object.keys(user).length) {
oaiq("init", { pixelId: OPENAI_PIXEL_ID, user: user });
}
}
// ---------------------------------------------------------------------
// 2. Inicialización del pixel, con datos de usuario si ya se conocen
// (solo cubre al visitante que YA está logueado al cargar la página)
// ---------------------------------------------------------------------
(async function initPixel() {
var customer = (init.data && init.data.customer) || {};
var user = await buildUserObject(customer, null);
var payload = { pixelId: OPENAI_PIXEL_ID, debug: OPENAI_DEBUG };
if (Object.keys(user).length) payload.user = user;
oaiq("init", payload);
})();
// ---------------------------------------------------------------------
// 3. Vistas de página y de producto
// ---------------------------------------------------------------------
analytics.subscribe("page_viewed", function () {
oaiq("measure", "page_viewed", { type: "contents" });
});
analytics.subscribe("product_viewed", function (event) {
var v = event.data.productVariant || {};
oaiq("measure", "contents_viewed", {
type: "contents",
amount: toMinorUnits(v.price && v.price.amount),
currency: v.price && v.price.currencyCode,
contents: [toContentItem({ variant: v, quantity: 1 })],
});
});
// ---------------------------------------------------------------------
// 4. Añadir al carrito
// ---------------------------------------------------------------------
analytics.subscribe("product_added_to_cart", function (event) {
var cartLine = event.data.cartLine || {};
var totalAmount = cartLine.cost && cartLine.cost.totalAmount;
oaiq("measure", "items_added", {
type: "contents",
amount: toMinorUnits(totalAmount && totalAmount.amount),
currency: totalAmount && totalAmount.currencyCode,
contents: [toContentItem(cartLine)],
});
});
// ---------------------------------------------------------------------
// 5. Checkout iniciado — AQUÍ es donde por fin se conoce al comprador,
// aunque sea invitado, así que aprovechamos para reforzar el "user"
// ---------------------------------------------------------------------
analytics.subscribe("checkout_started", async function (event) {
var checkout = event.data.checkout || {};
var total = checkout.totalPrice;
await refreshUserData(null, checkout);
oaiq("measure", "checkout_started", {
type: "contents",
amount: toMinorUnits(total && total.amount),
currency: total && total.currencyCode,
contents: (checkout.lineItems || []).map(toContentItem),
});
});
// ---------------------------------------------------------------------
// 6. Compra completada — con event_id compartido con el Worker, y con
// los datos de usuario reforzados otra vez por si cambiaron
// ---------------------------------------------------------------------
analytics.subscribe("checkout_completed", async function (event) {
var checkout = event.data.checkout || {};
var total = checkout.totalPrice;
// El customer.id solo existe aquí dentro de checkout.order (no en checkout_started,
// porque el pedido todavía no se ha creado en ese punto del funnel).
var orderCustomer = (checkout.order && checkout.order.customer) || null;
await refreshUserData(orderCustomer, checkout);
var rawOrderId = checkout.order && checkout.order.id;
var orderId = extractNumericOrderId(rawOrderId);
var eventId = orderId ? "order_" + orderId : undefined;
oaiq(
"measure",
"order_created",
{
type: "contents",
amount: toMinorUnits(total && total.amount),
currency: total && total.currencyCode,
contents: (checkout.lineItems || []).map(toContentItem),
},
eventId ? { event_id: eventId } : {}
);
});

Serie ChatGPT Ads en Shopify · Parte 1 de 2. La Parte 2 (Conversions API vía Webhook + Cloudflare Worker) se publicará próximamente en este blog y en el canal de YouTube.

¿Vendes con Shopify y quieres hacer bien tu tracking? Te ayudo a montar el Píxel, la Conversions API y las campañas completas de ChatGPT Ads, Google Ads o Meta. Hablemos

Deja un comentario