Conversion Tracking
Create an OpenAI pixel with its Conversions API key, manage its conversion events, install it on a site and send server-side events.
POST /v1/accounts/{accountId}/tracking-tags creates an OpenAI pixel and provisions a Conversions API key for it, in one call.
adAccountId is required by the endpoint and ignored here, because the API key already selects the ad account.
Pass defaultEventType to provision the conversion event setting that goal: "conversions" needs. It takes one of order_created, lead_created, items_added, contents_viewed, checkout_started, registration_completed, subscription_created, trial_started, appointment_scheduled, page_viewed, app_installed or app_opened.
const { data: createdTag } = await zernio.trackingtags.createTrackingTag({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
body: { adAccountId: 'acct_abc123', name: 'Acme website', defaultEventType: 'order_created' }
});
console.log(createdTag.tag.id);Response (201):
{
"platform": "openaiads",
"tag": {
"id": "cds_68e4f0a1b2c3d4e5f6a7b8c9",
"pixelId": "4Kx9TqLm2VbN7wRz1PsYcD",
"siteTagId": "4Kx9TqLm2VbN7wRz1PsYcD",
"name": "Acme website",
"platform": "openaiads",
"kind": "pixel",
"status": "active"
}
}Creating a tag is not idempotent and OpenAI pixels cannot be deleted, so never retry this call automatically. Each call creates another pixel plus another Conversions API key.
If the pixel is created but the key provisioning fails, the pixel stays live on OpenAI and the error names it.
An ad account that is not enabled for pixel management returns 422 with code FEATURE_NOT_AVAILABLE. Your OpenAI partner representative enables it.
An account holds one Zernio-created pixel. A second create answers 409 naming the existing one. More pixels are managed in OpenAI Ads Manager.
Pixel ids and reads
Every OpenAI pixel has two ids:
tag.idis the API resource id (cds_...), which conversion event settings name as their source.tag.siteTagId(also returned aspixelId) is thepixel_idthe on-site code and the Conversions API send.
Per-pixel calls accept either as tagId.
OpenAI documents no single-pixel read. GET .../tracking-tags/{tagId} resolves the pixel from the list, and adds the official oaiq base code plus the pixel's conversion event settings as events.
{
"platform": "openaiads",
"tag": {
"id": "cds_68e4f0a1b2c3d4e5f6a7b8c9",
"pixelId": "4Kx9TqLm2VbN7wRz1PsYcD",
"siteTagId": "4Kx9TqLm2VbN7wRz1PsYcD",
"name": "Acme website",
"platform": "openaiads",
"kind": "pixel",
"status": "active",
"code": "<script>...oaiq('init', '4Kx9TqLm2VbN7wRz1PsYcD')...</script>",
"events": [
{ "id": "ces_68e4f0c9d8e7", "name": "Purchases", "type": "order_created", "siteEvent": "purchase", "siteEventId": "order_created", "status": "active", "clickWindowDays": 30 }
]
}
}GET .../tracking-tags/{tagId}/stats returns the latest Pixel events OpenAI received, at most 50 from the last 15 minutes. It is a live "is the pixel firing" check, not a history.
- Conversions API events never appear there.
startTime/endTimeanswer400. Use the ads insights conversion metrics for totals.- An account not enabled for the event stream answers
422 feature_not_available.
Conversion events
A pixel's conversion event settings are its events. GET .../tracking-tags/{tagId}/events lists the ones sourced on the pixel, and POST creates one.
The body takes only name, type and siteEvent. The attribution window is OpenAI's fixed 30 days.
type is one of:
- an OpenAI standard event:
order_created,lead_created,items_added,contents_viewed,checkout_started,registration_completed,subscription_created,trial_started,appointment_scheduled,page_viewed,app_installed,app_opened - for anything else, the custom event name itself (1 to 64 letters, digits, underscores or dashes, stored lowercase)
siteEvent picks the name the Shopify pixel sends. So search and add_payment_info become the custom events search and addpaymentinfo.
const { data: created } = await zernio.trackingtags.createTrackingTagEvent({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', tagId: 'cds_68e4f0a1b2c3d4e5f6a7b8c9' },
body: { name: 'Searches', siteEvent: 'search' }
});Response (201):
{
"platform": "openaiads",
"event": { "id": "ces_68e4f0d1e2f3", "name": "Searches", "type": "custom", "siteEvent": "search", "siteEventId": "search", "status": "active", "clickWindowDays": 30 }
}Creating an event setting is not idempotent.
The rest answers 501, because OpenAI's conversion-setup API documents only list and create. The endpoints answer 404/405 when probed:
| Operation | Why it is not available |
|---|---|
| Update the pixel | POST, PATCH, PUT and DELETE /v1/conversions/pixels/{id} answer 405. Rename it in OpenAI Ads Manager. |
| Share the pixel | A pixel belongs to one ad account. |
| Update an event | POST, PATCH and PUT /v1/conversions/event_settings/{id} answer 404. Create a new event instead. |
| Delete an event | No delete or archive endpoint. Archive it in OpenAI Ads Manager. |
| Delete the pixel | No delete endpoint. |
Install on a site
POST .../tracking-tags/{tagId}/install puts the pixel on a connected Shopify store or WordPress site.
WordPress gets the official oaiq base code with a page_viewed call.
Shopify sends each storefront event through OpenAI's documented image tag (GET https://bzr.openai.com/v1/sdk/events). Each carries the event id for deduplication, and amounts in the currency's minor unit.
The landing page's oppref click id is kept in the __oppref cookie for 30 days and sent with every event.
The image tag cannot carry the __obref browser id (OpenAI rejects the parameter). Search text is never sent.
Send events
POST /v1/ads/conversions relays server-side events to OpenAI's Conversions API.
destinationId is the pixel wire id, from GET /v1/accounts/{accountId}/conversion-destinations (List conversion destinations).
Zernio hashes email identifiers with SHA-256 and sends money in minor units. Send plaintext and whole currency values.
const { data: sent } = await zernio.conversions.sendConversions({
body: {
accountId: '66b2e19d8c3f5a7e9d0b1c2d',
destinationId: '4Kx9TqLm2VbN7wRz1PsYcD',
events: [{
eventName: 'Purchase',
eventTime: 1798804800,
eventId: 'order_12345',
sourceUrl: 'https://shop.example.com/checkout/confirmation',
value: 25.99,
currency: 'USD',
user: { email: 'customer@example.com' }
}]
}
});
console.log(sent.eventsReceived);Response (200):
{
"platform": "openaiads",
"eventsReceived": 1,
"eventsFailed": 0,
"failures": []
}Purchase, Lead, AddToCart, ViewContent, InitiateCheckout, CompleteRegistration, Subscribe, StartTrial and Schedule map 1 to 1 onto OpenAI's own event types.
So Purchase arrives as order_created, Lead as lead_created and AddToCart as items_added. Any other name is sent as a custom event with the name preserved.
Larger submissions are split into chunks of 1,000 events. Each chunk is all-or-nothing: one malformed event fails its chunk.
eventsFailed plus failures[] name the events that did not land.