Section 2. The Measurement Stack · Last verified: MAY 2026

The Conversions API

Sacha Lefebvre, founder of Paid Ai Search
Sacha Lefebvre Founder, Paid Ai Search · LinkedIn

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.

ValueRequiredDescription
pid (query string)YesPixel ID.
validate_only (body)NoWhen true, validates events without saving them.
events (body)YesArray 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:

  1. Send small batches (10-50 events) so a single bad event doesn’t kill a large batch.
  2. 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:

  1. User clicks ad → lands on PDP. URL contains oppref query parameter.
  2. Pixel captures oppref, writes it to __oppref cookie.
  3. Server-side checkout webhook fires. Backend reads oppref from session/cart metadata (which was populated from the cookie or query string at landing).
  4. Backend sends order_created to Conversions API with oppref included.

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

FieldRequiredDescription
idYesUnique event identifier. Used with type to deduplicate.
typeYesStandard event name or custom.
timestamp_msYesEvent time in ms. Within last 7 days, no more than 10 minutes ahead.
custom_event_nameConditionalRequired when type is custom.
opprefNoOpenAI privacy-preserving identifier. Forward when available.
source_urlConditionalRequired when action_source is web.
action_sourceNoOne of web, mobile_app, offline, physical_store, phone_call, email, other.
userNoOptional user fields that improve attribution accuracy.
opt_outNotrue opts the event out of future user-level personalisation. Defaults to false.
dataYesEvent data matching the type. See Chapter 11.