[ Caso studio ]
I Custom Pixel di Shopify non vedono il cliente: come abbiamo costruito il bridge server-side
LTV, numero ordini e stato New/Returning non sono accessibili dal Custom Pixel di Shopify per ragioni di sicurezza. Abbiamo costruito un microservizio proxy con Shopify GraphQL API che li espone al dataLayer senza mai toccare il token Admin.
Settore: brand di lifestyle ecommerce — Shopify
Problema: i Custom Pixel Shopify non possono accedere all’Admin API — LTV, numero ordini e stato New/Returning mancanti dal dataLayer
Soluzione: microservizio proxy server-side che interroga la Shopify GraphQL API e restituisce le metriche al pixel
Migrazione chiave: da REST (3 limiti strutturali) a GraphQL (1 query, 4 query points fissi)
Bug trovato in test: orderCount: 0 per tutti — causa di schema, non di autenticazione
Infrastruttura: Render.com Starter, 7 USD/mese
Shopify ha introdotto i Custom Pixel come layer di tracking personalizzabile. La promessa: poter inviare eventi con dati arricchiti a GA4, Meta, TikTok senza dipendere dal pixel nativo limitato.
Il problema che nessuno dice chiaramente: i Custom Pixel girano in un ambiente sandbox isolato. Non possono fare chiamate dirette all’Admin API di Shopify. Quindi non hanno accesso ai dati cliente che renderebbero davvero utile quella segmentazione.
Volete mandare l’LTV del cliente all’evento Purchase su Meta? Il pixel non lo può leggere. Volete distinguere in modo affidabile clienti nuovi da abituali? Il pixel non lo sa. Volete sapere quanti ordini ha fatto quel cliente nel corso della sua vita? Anche qui: inaccessibile.
Questi tre dati — Lifetime Value, numero ordini storici, stato New/Returning affidabile — sono bloccati dietro una barriera di sicurezza che Shopify ha costruito deliberatamente. E con buone ragioni: un Custom Pixel che potesse accedere liberamente all’Admin API potrebbe fare cose molto peggiori.
La soluzione non è bypassare la sicurezza. È aggiungere un layer intermedio che la rispetti.
Il vincolo di sicurezza: perché il pixel non può fare da solo
I Custom Pixel di Shopify operano in un iframe sandbox con Content Security Policy restrittiva. Possono ricevere eventi dallo store (PageView, AddToCart, Purchase, ecc.) e possono fare chiamate verso endpoint esterni — ma non possono contattare direttamente l’Admin API di Shopify.
Il motivo è ovvio: l’Admin API di Shopify richiede un access token con permessi elevati. Se questo token fosse nel codice del Custom Pixel, sarebbe leggibile da chiunque ispezionasse il browser. Chiunque potrebbe estrarlo e usarlo per leggere (o modificare) qualsiasi dato dello store.
Dati inaccessibili dal pixel in modo nativo:
| Dato | Situazione senza API |
|---|---|
| Lifetime Value | Non disponibile |
| Numero ordini storici | Non disponibile |
| Stato New / Returning | Approssimato — derivato da Shopify Analytics, non da dati reali del profilo |
| Token Admin Shopify | Rischio se nel codice client |
Senza questi dati, il dataLayer inviato a GA4, Meta e TikTok è incompleto. Non è possibile segmentare correttamente gli acquisti per tipo di cliente. Campagne di retargeting, lookalike audience, ottimizzazione per “new customer acquisition” — tutto si basa su un segnale degradato.
La soluzione: un microservizio proxy server-side
La logica è semplice: il pixel non può chiamare Shopify, ma può chiamare un endpoint nostro. Il nostro endpoint — che vive su un server, non nel browser — può chiamare Shopify con il token Admin in modo sicuro.
Il token SHOPIFY_ACCESS_TOKEN non viene mai esposto al browser. Vive solo nelle variabili d’ambiente del server.
Il flusso:
- Il Custom Pixel rileva un checkout completato e legge il
customerId - Il pixel chiama il nostro endpoint
POST /inviandocustomerId+ una chiave segreta condivisa - Il server valida la chiave, interroga Shopify GraphQL API con il token Admin
- Shopify restituisce LTV e numero ordini aggregati
- Il server calcola lo stato New/Returning e risponde al pixel
- Il pixel aggiunge i dati al dataLayer → GA4, Meta, TikTok ricevono gli eventi arricchiti
customerId + chiave segreta. Il server risponde con le metriche calcolate.Perché REST non bastava: 3 limiti strutturali
L’implementazione iniziale usava la Shopify REST API — la scelta ovvia per chi conosce Shopify. Ma durante i test emergono tre problemi che la rendono inadatta per questo caso d’uso.
Limite 1 — Finestra di 60 giorni:
Lo scope read_orders restituisce solo gli ordini degli ultimi 60 giorni. Per calcolare il Lifetime Value reale e il numero ordini storici, serve read_all_orders — uno scope che richiede una revisione da parte di Shopify e un processo di approvazione. Non praticabile per tempi rapidi.
Limite 2 — Paginazione oltre 250 ordini: La REST API restituisce al massimo 250 ordini per chiamata. Un cliente con 300 ordini richiede due chiamate sequenziali. Con 600 ordini, tre chiamate. La latenza cresce con la storia del cliente — proprio il contrario di quello che serve per un endpoint chiamato a ogni checkout.
Limite 3 — Costo in query points proporzionale: Ogni ordine restituito consuma quota API. Con clienti ad alto volume, il costo cresce linearmente con il numero di ordini. Per store con clienti molto fedeli, diventa un problema di scala.
La soluzione era nella Shopify Admin GraphQL API.
La query GraphQL: 1 chiamata, dati aggregati, 4 query points
La Shopify Admin GraphQL API espone sul nodo Customer due campi aggregati che eliminano tutti e tre i limiti:
query getCustomerMetrics($customerId: ID!) {
customer(id: $customerId) {
numberOfOrders
amountSpent {
amount
}
}
}
Questi campi sono pre-aggregati da Shopify — non iterano gli ordini singoli. Non hanno limite temporale (includono tutta la storia del cliente), sono precisi per qualsiasi volume di ordini, e costano sempre lo stesso.
| REST (approccio iniziale) | GraphQL (approccio finale) | |
|---|---|---|
| Limit temporale | 60 giorni (scope base) | Nessuno |
| Precisione per clienti con molti ordini | Imprecisa oltre 250 ordini | Precisa sempre |
| Chiamate API per risposta | N (una per batch da 250) | 1 |
| Costo quota | Proporzionale al volume | 4 query points fissi |
Capsule citabile: La Shopify Admin GraphQL API espone sul nodo Customer i campi
numberOfOrderseamountSpent.amountcome valori pre-aggregati — non come raccolta di ordini da iterare. Questo significa che una singola query restituisce il totale degli ordini lifetime e il valore speso lifetime, indipendentemente da quanti ordini ha fatto il cliente. Il costo fisso misurato in produzione è di 4 query points su 2000 disponibili. Non esistono i limiti di paginazione o finestra temporale della REST API per questi campi aggregati.
Il bug trovato in test: orderCount: 0 per tutti
Durante i test intermedi, il servizio restituiva orderCount: 0 per ogni cliente — inclusa la Co-founder dello store, che aveva 34 ordini storici registrati.
L’errore non era nell’autenticazione (il token funzionava), non era nella connettività (Shopify era raggiungibile), non era nella logica del server. Era nello schema della query.
Causa: l’implementazione iniziale usava il percorso totalPriceSet.shopMoney.amount per leggere il valore degli ordini — un campo derivato dalla struttura degli ordini REST. Questo campo esiste sugli oggetti Order singoli, ma non esiste sul nodo Customer come aggregato.
La query restituiva struttura corretta ma campo nullo — e il parsing produceva 0 invece di errore.
Correzione: sostituzione con i campi aggregati nativi del nodo Customer:
# Prima (errato — percorso REST trasferito su GraphQL)
# totalPriceSet.shopMoney.amount → non esiste come aggregato
# Dopo (corretto — campi aggregati nativi GraphQL)
numberOfOrders # conteggio lifetime, campo diretto del Customer
amountSpent { # valore aggregato lifetime, non totalPriceSet
amount
}
Dopo la correzione: test su 3 clienti reali dello store, tutti con orderCount e lifetimeValue corretti. Il caso più significativo: la Co-founder con 34 ordini storici, da cui il bug era stato scoperto.
Sicurezza: cosa non finisce mai nel browser
Il design del servizio parte dal principio che il token Admin Shopify non deve mai uscire dal server. L’architettura garantisce questo a più livelli.
Autenticazione dell’endpoint:
Ogni chiamata dal pixel include una SECRET_API_KEY condivisa — una stringa casuale configurata come variabile d’ambiente sia sul server che nel Custom Pixel. Il server rifiuta qualsiasi chiamata senza chiave valida (HTTP 200 con error.type: AUTH_ERROR + valori default di fallback).
Il token Admin non transita mai:
Il flusso OAuth è stato completato una volta sola durante l’installazione (GET /auth + /auth/callback). Il token permanente ottenuto è stato configurato come variabile d’ambiente su Render. Da quel momento non viene mai trasmesso — né al pixel, né a terzi.
Fallback sicuro invece di crash:
Il servizio gestisce tutti i casi di errore restituendo sempre HTTP 200 con un payload di fallback. Il pixel non va mai in crash — nel caso peggiore usa i valori di default: customerStatus: "New - First Order", lifetimeValue: "0.00", orderCount: 0.
| Scenario | Risposta | Comportamento |
|---|---|---|
| apiKey errata | success: false |
AUTH_ERROR + valori default |
| customerId mancante | success: false |
VALIDATION_ERROR + valori default |
| Shopify non raggiungibile | success: false |
SHOPIFY_API_ERROR + valori default |
| Tutto corretto | success: true |
Dati reali del cliente |
Infrastruttura: 7 USD/mese, deploy automatico da GitHub
Il servizio è ospitato su Render.com (piano Starter) con deploy automatico ad ogni push su main. L’istanza è sempre attiva — nessun cold start che aggiunge latenza al checkout.
| Voce | Dettaglio |
|---|---|
| Provider | Render.com |
| Piano | Starter — istanza sempre attiva |
| Costo mensile | 7 USD/mese |
| Deploy | Automatico da GitHub (main) |
| Riavvio automatico | Sì (in caso di crash) |
| Alternativa documentata | Vercel — piano gratuito per traffico basso, nessuna modifica al codice |
Le variabili d’ambiente critiche (SECRET_API_KEY, SHOPIFY_ACCESS_TOKEN) vivono nel dashboard di Render — non nel codice, non nel repository.
In scope: anche TikTok tracking
Parallelamente alla Customer Data API, il progetto comprendeva la messa a punto del tracking TikTok per il brand.
Situazione iniziale: due pixel TikTok attivi sullo store — uno operativo, uno che mandava praticamente solo Pageview. La prima operazione è stata identificare quale tenere.
Interventi eseguiti su TikTok:
- Implementato l’evento
Leadsu due landing page con form attivi - Aggiunto il tag Pageview via Stape (browser) con
event_id— necessario per abilitare la deduplicazione con il lato server-side - Disabilitato il pixel non operativo (confermato dal team del brand)
Il pattern di deduplicazione con event_id applicato a TikTok è lo stesso usato per Meta: stesso identificatore univoco su browser e server, piattaforma deduplica e scarta il duplicato più povero di informazioni.
Il passo finale: aggiornare il Custom Pixel
Il servizio è live su Render. La migrazione REST→GraphQL è completata. I test in produzione hanno verificato:
GET /health→{ ok: true }— servizio attivo- Query GraphQL su 3 clienti reali — quota: 4 points su 2000
POST /concustomerIdvalido — restituiscecustomerStatus,lifetimeValue,orderCountcorretti
Il passo finale rimasto al momento del rilascio: aggiornare il codice del Custom Pixel per puntare al nuovo endpoint GraphQL invece del vecchio endpoint REST. Una modifica di configurazione — l’URL dell’endpoint e la SECRET_API_KEY — senza impatto sulla logica del pixel.
Da quando l’endpoint è live, ogni checkout arricchisce il dataLayer con dati cliente reali. GA4 riceve lo stato New/Returning affidabile. Meta e TikTok ricevono il LTV per ottimizzare le campagne di acquisizione verso i profili ad alto valore.
Hai dati cliente mancanti nel dataLayer Shopify?
Se stai usando i Custom Pixel di Shopify e il tuo dataLayer non include LTV, numero ordini o stato New/Returning affidabile, il vincolo è strutturale — non un bug nel tracking. La soluzione richiede un layer server-side.
Con LayerData facciamo un audit del tuo dataLayer in 15 minuti e identifichiamo quali dati cliente mancano e cosa serve per aggiungerli.
Domande frequenti
I Custom Pixel Shopify possono davvero non accedere all’Admin API?
Sì. Shopify ha deliberatamente isolato i Custom Pixel in un ambiente sandbox per ragioni di sicurezza. Possono ricevere eventi dallo store e fare chiamate HTTP verso endpoint esterni, ma non possono contattare l’Admin API — che richiederebbe un token con permessi elevati. Se quel token fosse nel codice del pixel, sarebbe leggibile da chiunque ispezionasse il browser.
Perché GraphQL e non un’altra chiamata REST con lo scope read_all_orders?
Lo scope read_all_orders richiede una revisione manuale da parte di Shopify prima di essere concesso. Anche una volta ottenuto, il problema della paginazione rimane: per clienti con molti ordini, servono N chiamate sequenziali. GraphQL risolve entrambi i problemi strutturalmente — i campi numberOfOrders e amountSpent.amount sul nodo Customer sono pre-aggregati, non derivati dalla lista ordini.
Quanto pesa sull’esperienza di checkout chiamare un servizio esterno?
Il Custom Pixel si attiva sulla pagina order status (post-acquisto), non durante il flusso di checkout. La chiamata all’endpoint avviene dopo che l’ordine è già stato confermato — non è nel critical path della conversione. La latenza dell’endpoint (Render Starter, istanza sempre attiva) non impatta in alcun modo il completamento del checkout.
Il servizio funziona se Shopify è irraggiungibile temporaneamente?
Sì. Il servizio restituisce sempre HTTP 200 — in caso di errore, risponde con valori di fallback sicuri (customerStatus: "New - First Order", lifetimeValue: "0.00", orderCount: 0). Il pixel non va mai in crash. Gli eventi vengono comunque inviati a GA4, Meta e TikTok — con dati cliente meno precisi, ma senza interruzioni.