> ## Documentation Index
> Fetch the complete documentation index at: https://docs.datalyr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI Ads

> Connect an OpenAI Ads Conversions API key and deliver conversions.

When you finish, one Datalyr event reaches the OpenAI Ads Conversions API and returns HTTP 200.

## Before you start

| Requirement  | Detail                                                                 |
| ------------ | ---------------------------------------------------------------------- |
| API key      | **OpenAI Ads Manager → Settings → Conversions API**. Starts with `sk-` |
| Pixel ID     | The same screen. Starts with `px_`                                     |
| A live event | The trigger event exists in **Events**                                 |

<Warning>
  Treat the API key as a secret. Do not paste it into a browser console, a screenshot, or a public issue.
</Warning>

## Connect OpenAI Ads

<Steps>
  <Step title="Open Sources">
    Open **Sources**. Find the **OpenAI Ads** card.
  </Step>

  <Step title="Paste the credentials">
    Select **connect**. Fill **API key**. Fill **Pixel ID**.
  </Step>

  <Step title="Save">
    Select **connect** in the dialog.
  </Step>
</Steps>

We fire a `validate_only: true` request against your pixel before we save it, so bad credentials fail here rather than at your first live conversion.

| Connect-time error            | Meaning                                           |
| ----------------------------- | ------------------------------------------------- |
| `Invalid API key`             | HTTP 401 or 403. The key is wrong or revoked      |
| `Pixel ID not found`          | HTTP 404. The pixel does not exist under this key |
| `Could not reach OpenAI CAPI` | Network failure. Retry the connect                |

## Create the rule

<Steps>
  <Step title="Open the editor">
    Open **Conversions**. Select **manage rules**. Select **new rule**.
  </Step>

  <Step title="Set the trigger">
    Fill **rule name**. Pick the **trigger event**. Pick **OpenAI** as **target platform**.
  </Step>

  <Step title="Pick the pixel">
    Pick the pixel under **OpenAI Pixel**. Select **next**.
  </Step>

  <Step title="Map the event">
    Pick the **platform standard event**. Set **value** to `dynamic` for revenue events. Set **currency**.
  </Step>

  <Step title="Save">
    Select **save**. Enable the rule.
  </Step>
</Steps>

## Reference

We post to `POST https://bzr.openai.com/v1/events?pid={pixel_id}` with an `Authorization: Bearer` header. The pixel goes on the URL, never in the body. We abort the request after 15 seconds.

### Event types

Each type forces a `data.type` shape. That shape decides which fields OpenAI accepts.

| `type`                   | `data.type`       | Carries `contents` | Carries `plan_id` |
| ------------------------ | ----------------- | ------------------ | ----------------- |
| `order_created`          | `contents`        | yes                | no                |
| `checkout_started`       | `contents`        | yes                | no                |
| `items_added`            | `contents`        | yes                | no                |
| `contents_viewed`        | `contents`        | yes                | no                |
| `page_viewed`            | `contents`        | yes                | no                |
| `subscription_created`   | `plan_enrollment` | yes                | yes               |
| `trial_started`          | `plan_enrollment` | yes                | yes               |
| `lead_created`           | `customer_action` | no                 | no                |
| `registration_completed` | `customer_action` | no                 | no                |
| `appointment_scheduled`  | `customer_action` | no                 | no                |
| `custom`                 | `custom`          | yes                | yes               |

A `custom` type also carries `custom_event_name`. We lowercase it and force it to match `^[a-z0-9_-]{1,64}$`.

### Money

OpenAI takes amounts in the currency's minor unit, so we multiply by the per-currency factor.

| Currency class                           | Factor | Example                |
| ---------------------------------------- | ------ | ---------------------- |
| Two-decimal, such as `USD`, `EUR`, `GBP` | 100    | \$49.99 becomes `4999` |
| Zero-decimal, such as `JPY`, `KRW`       | 1      | ¥1000 becomes `1000`   |
| Three-decimal, such as `KWD`, `BHD`      | 1000   | 1.250 becomes `1250`   |

`data.currency` is required whenever `data.amount` is present. We write the rule's currency, else `USD`.

### `action_source`

We send one of `web`, `mobile_app`, `offline`, `physical_store`, `phone_call`, `email`, `other`. A `web` event with no resolvable page URL degrades to `other`, because OpenAI requires `source_url` on `web`.

### Timestamps

`timestamp_ms` is epoch milliseconds. OpenAI accepts a window of 7 days into the past and 10 minutes into the future. We clamp a future timestamp to now, and skip an older event with the reason `timestamp_out_of_window`.

### Attribution and identity

`oppref` is the OpenAI click ID. We read it from the `oppref` URL parameter, the `__oppref` first-party cookie, and the attribution history of linked visitors. The lookup window is 7 days.

The `user` object is off by default. Turn it on per rule with `config.send_user_data`.

| Field                | Normalization                                                           | Hashed       |
| -------------------- | ----------------------------------------------------------------------- | ------------ |
| `email_sha256`       | lowercase, trim                                                         | SHA-256, hex |
| `external_id_sha256` | verbatim, with no lowercase and no trim                                 | SHA-256, hex |
| `country`            | ISO alpha-2, uppercase                                                  | plaintext    |
| `city`               | trim, first 128 characters                                              | plaintext    |
| `zip_code`           | trim, characters outside `A-Za-z0-9`, space, and dash removed, first 32 | plaintext    |
| `ip_address`         | as-is                                                                   | plaintext    |
| `user_agent`         | as-is; dropped when it is `node`                                        | plaintext    |

OpenAI Ads accepts no phone field, so we never send one.

After a marketing denial, we set `opt_out: true` and send no `user` object and no `oppref`.

### Deduplication

We send `id`, the same UUID the browser pixel fires with, and OpenAI collapses the pair into one conversion.

## Verify

1. Open **Conversions**. Select **manage rules**. Open your rule.
2. Select **send test event**. The request carries `validate_only: true`.
3. Confirm the delivery row shows HTTP 200 and no `error` in the body.
4. Trigger one real event.
5. Confirm the rule's `sent` count went up by 1.

## When it does not work

| Symptom                                               | Cause                                     | Check                                     | Fix                                                 |
| ----------------------------------------------------- | ----------------------------------------- | ----------------------------------------- | --------------------------------------------------- |
| Delivery `skipped`, reason `timestamp_out_of_window`  | The event is over 7 days old              | The event timestamp in **Events**         | Fix the source's delivery lag                       |
| Delivery `skipped`, reason `feature_flag_off`         | OpenAI delivery is off for this workspace | The delivery row                          | Contact support to enable it                        |
| Delivery `skipped`, reason `global_kill_switch`       | OpenAI delivery is paused                 | The Datalyr status page                   | Wait. Delivery resumes on its own                   |
| Response mentions `invalid api key` or `unauthorized` | The key was rotated or revoked            | The **OpenAI Ads** card in **Sources**    | Select **configure**. Paste the new key             |
| Response mentions `hash` or `user_data`               | A `user` field is the wrong shape         | The request payload on the delivery row   | Remove the user-data mapping from the rule          |
| Response mentions `timestamp`                         | OpenAI rejected the event time            | The `timestamp_ms` in the request payload | Confirm the source sends a real event time          |
| Response mentions `oppref`                            | The click ID is malformed                 | The `oppref` in the request payload       | Confirm the landing page keeps the parameter intact |
| Every conversion reports `attribution_type: none`     | No `oppref` reached the event             | The event payload in **Events**           | Pass `oppref` yourself as an event property         |
| Amounts are 100 times too large                       | The source already sends minor units      | The `value_path` sample value             | Point `value_path` at the major-unit amount         |

## Next

* [Conversion delivery](/advanced/conversion-delivery): retry, dedup, and status rules for every platform.
* [Conversions](/product/conversions): read one delivery row and its response body.
* [Attribution](/attribution/index): which click IDs the tracking script captures.
