The ChatGPT Ads pixel is the short route to measurement, and on many websites it's enough. But it goes blind in a very common case: a conversion that doesn't finish on the page where it's installed.
It happened to us: our sign-up is completed in the client dashboard, not on the website, so we send it from the server. This is what we learnt, traps included. No pixel yet? Start with lesson 3 of module 12 (Intermediate).
When the pixel isn't enough
Move to the Conversions API, which sends events from your server, when:
- The conversion finishes on another domain with no pixel: a payment gateway, a booking system, a client dashboard.
- The conversion happens on the server: a sign-up confirmed later, a lead your CRM marks as good.
- You want more reliability: OpenAI describes the API as more reliable than the pixel alone.
The usual approach is to combine both and deduplicate.
The thread you can't lose: the oppref
Whoever clicks your ad arrives with a click identifier in the URL: the
oppref. The pixel captures it and stores it in a first-party cookie,
__oppref. On the server nobody does that for you: collect it and send
it with the event exactly as it came.
The challenge is carrying it through to the conversion, perhaps on another domain and days later: a hidden form field, a parameter in the link or your database will do. We store it on the website on arrival and retrieve it on the server.
Deduplication: one conversion, one count
If the same conversion arrives through the pixel and the API, OpenAI counts it once when the pixel ID, the event name and the event identifier all match.
⚠️ Pitfall: the identifier has a different name on each side:
event_idin the pixel,idin the Conversions API. Generate the value once (the order number, for example) and use it in both.
The data type of each event
Each standard event requires a data type and, if it doesn't match, the
API rejects the whole batch. It happened to us when we sent a
sign-up with the data type from the official example, which is a
purchase: sign-up requires customer_action. The full table:
| Event | What it measures | Data type |
|---|---|---|
registration_completed |
Registration completed | customer_action |
lead_created |
Lead created | customer_action |
appointment_scheduled |
Appointment booked | customer_action |
app_installed |
App installed | customer_action |
app_opened |
App opened | customer_action |
order_created |
Order placed (purchase) | contents |
checkout_started |
Checkout started | contents |
items_added |
Added to basket | contents |
contents_viewed |
Content viewed | contents |
page_viewed |
Page viewed | contents |
subscription_created |
Subscription created | plan_enrollment |
trial_started |
Trial started | plan_enrollment |
custom |
Custom event | custom |
Also, amount and currency go together, and the custom event
carries a name of your own, 1 to 64 characters (letters, digits,
hyphens or underscores, starting and ending with a letter or digit, and
not matching a standard event).
💡 Ninja trick: before copying an official example, check which event it uses and adjust the data type to yours: a single mistyped event sinks the whole batch.
The hashed email: better attribution, still personal data
You can add the customer's email hashed with SHA-256 to improve attribution. Normalise it first (no stray spaces, all lowercase): "Ana@Example.com" and "ana@example.com" produce different hashes.
⚠️ Pitfall: hashing isn't anonymising. A hashed email is still personal data: we declared it in our privacy policy before sending it. This isn't legal advice; check it with whoever handles your data protection.
The pixel's automatic advanced matching does something similar on its own: it detects customer information on your website (such as form entries) and hashes it with SHA-256 in the browser. The documentation doesn't explain how to switch it off, so mention it in your privacy policy too.
Attribution: which window counts
- Click-through: set in each event's configuration; the official examples use 30 days.
- View-through: a fixed 1-day window, for reporting only. Optimisation and billing run on clicks.
Line up the windows before comparing with Google Ads or your analytics.
Conversion campaigns: a single standard event
You need exactly one active standard event as the goal; custom events can't be used for optimisation. And it needs volume: if you sell little, optimising towards leads or sign-ups may suit you better. The objective and the event can't be changed later: you'd have to create another campaign (lesson 3 of this module).
Consent, on the server too
The pixel starts with consent switched on: in Europe, call
oaiq("consent", false) before initialising it and switch it on when
the person accepts (anything blocked isn't resent). On the server, the
filter is yours: apply your website's consent criteria before forwarding
the oppref or the email, and stop if someone withdraws consent.
Two keys that get mixed up
Ads Manager has two different keys, and we mixed them up:
- The conversions key, in Conversions (behind the key icon): for sending events from your server.
- The API key, in Settings → API Keys: for managing the account via the API (lesson 8).
Both live on your server, never in your website's code.
What to remember
- No pixel where the conversion finishes? Send it from the server with
the
opprefexactly as it came. - Deduplicate with the same pixel, event and identifier (
event_idin the pixel,idin the API). - Each event requires its data type; if it doesn't match, the whole batch fails.
- The hashed email improves attribution, but it's still personal data.
- View-through (1 day) is for reporting only; optimisation and billing run on clicks.
- Conversion campaigns: a single standard event, with volume and no going back.