The Conversions API
Chapter 09 - The Conversions API
The Conversions API (CAPI) is the server-to-server companion to the pixel. OpenAI explicitly calls it a more reliable tracking source than the pixel alone and recommends running both when possible.
Endpoint
POST https://bzr.openai.com/v1/events?pid=<PIXEL-ID>
Authorization: Bearer <API-KEY>
Content-Type: application/json Note the host: bzr.openai.com, not api.ads.openai.com. Conversions and Advertiser API live on different domains.
| Value | Required | Description |
|---|---|---|
pid (query string) | Yes | Pixel ID. |
validate_only (body) | No | When true, validates events without saving them. |
events (body) | Yes | Array of events. Up to 1,000 per batch. |
The events array is required even for a single event. Posting an event at the body root, without the { events: [...] } wrapper, returns 400 Missing required field: events.
Batch failure semantics
If one event in a batch fails, the entire batch fails. Build retry logic accordingly. Two options:
- Send small batches (10-50 events) so a single bad event doesn’t kill a large batch.
- Validate first, set
validate_only: true, identify the bad event, fix or drop it, then send for real.
In production, both. Validate periodically; send small batches.
Provisioning
Pixel ID and Conversions API key are both created from the Conversions tab inside Ads Manager. The Pixel ID is shared between pixel and CAPI, same identifier, both transports. The API key is a separate secret used only by CAPI.
Two key types in Ads Manager. Do not mix them up. Settings → API Keys issues an Advertiser API key scoped for campaign CRUD (Section 3 chapters). Conversions → Manage conversion keys issues a Conversions API key scoped ads.third_party_events.write, prefix sk-svcacct-. Only the Conversions API key works for CAPI; an Advertiser API key in the Authorization header returns 401 Missing scopes: ads.third_party_events.write.
Key difference from the pixel
The pixel captures oppref automatically. The Conversions API does not. The advertiser is responsible for capturing oppref from the landing page (or from the __oppref cookie the pixel stored) and passing it on the server-side event.
The standard pattern on a Shopify store:
- User clicks ad → lands on PDP. URL contains
opprefquery parameter. - Pixel captures
oppref, writes it to__opprefcookie. - Server-side checkout webhook fires. Backend reads
oppreffrom session/cart metadata (which was populated from the cookie or query string at landing). - Backend sends
order_createdto Conversions API withopprefincluded.
Without that bridge, server-side events lose the click attribution.
Webhook context
Payment webhooks (Stripe, Paddle, etc.) fire from the payment provider’s servers. There is no user browser in the loop, so the __oppref cookie set by the pixel is not accessible. The webhook handler must read oppref from a place that survives the redirect.
The pattern: capture oppref at intake (the request that creates the checkout session), persist it through the payment provider’s session metadata, retrieve it in the webhook.
Stripe, at checkout creation:
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [/* ... */],
metadata: { oppref: readOpprefFromRequest(request) ?? '' },
success_url: '...',
cancel_url: '...'
}); Stripe, in the webhook:
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
await sendCapiEvent({
id: `order_${session.metadata?.order_id ?? session.id}`,
type: 'order_created',
oppref: session.metadata?.oppref || undefined,
/* other fields */
});
} Using a stable identifier (order_${order_id}) as the event id also makes the post idempotent under the payment provider’s webhook retry policy.
Example event
curl -X POST "https://bzr.openai.com/v1/events?pid=<PIXEL-ID>"
-H "Authorization: Bearer <API-KEY>"
-H "Content-Type: application/json"
--data '{
"validate_only": false,
"events": [{
"id": "order_12345",
"type": "order_created",
"timestamp_ms": 1773892800000,
"oppref": "oppref_abc",
"source_url": "https://shop.example.com/checkout/confirmation",
"action_source": "web",
"data": {
"type": "contents",
"amount": 2599,
"currency": "USD",
"contents": [{
"id": "sku_123",
"name": "Starter bundle",
"content_type": "product",
"quantity": 1
}]
}
}]
}' Server event metadata fields
| Field | Required | Description |
|---|---|---|
id | Yes | Unique event identifier. Used with type to deduplicate. |
type | Yes | Standard event name or custom. |
timestamp_ms | Yes | Event time in ms. Within last 7 days, no more than 10 minutes ahead. |
custom_event_name | Conditional | Required when type is custom. |
oppref | No | OpenAI privacy-preserving identifier. Forward when available. |
source_url | Conditional | Required when action_source is web. |
action_source | No | One of web, mobile_app, offline, physical_store, phone_call, email, other. |
user | No | Optional user fields that improve attribution accuracy. |
opt_out | No | true opts the event out of future user-level personalisation. Defaults to false. |
data | Yes | Event data matching the type. See Chapter 11. |