# OpenAI Ads Conversion API Export Integration

## Overview

The OpenAI Ads Conversion API connector exports server-side conversion events from Treasure AI to an OpenAI Ads Pixel. It uses the [OpenAI Ads Conversions API](https://developers.openai.com/ads/conversions-api) to validate, batch, and deliver standard or custom events.

The connector derives the OpenAI event data shape from each event type, normalizes and hashes restricted user identifiers, and sends a maximum of 1,000 events per request.

## Prerequisites

- Basic knowledge of Treasure AI.
- An active OpenAI Ads account.
- An existing OpenAI Ads web Pixel and its **Pixel ID**.
- A server-side **Conversions API key** for the OpenAI Ads account.


## Limitations and Known Issues

- Events must have timestamps within the previous seven days and no more than ten minutes in the future.
- Each request can contain at most 1,000 events. If OpenAI rejects one event in a request, the entire request fails.
- **Skip Invalid Records** applies to records rejected by connector validation before upload. It cannot isolate one event after OpenAI rejects an uploaded batch.
- The OpenAI API uses only the first three valid, unique values in each user-list field.
- Raw email addresses, phone numbers, external IDs, first names, and last names are normalized and SHA-256 hashed before delivery.
- Android GAID is supported through `android_advertising_id`.
- The OpenAI documentation does not define a complete successful-delivery response schema. Confirm measurement and attribution results in OpenAI Ads Manager.


## Use the Treasure Console

### Create a New Connection

Create the authentication and data connection before exporting query results.

1. Open **Treasure Console**.
2. Navigate to **Integrations Hub > Catalog**.
3. Search for and select **OpenAI Ads Conversion API**.


![](/assets/new_connector_logo.d0d75fa2cf1fe7deb94c1d90f78cdba33a6ca88df7bbb83ee0c41601b67a55a5.01afb486.webp)

1. Select **Create Authentication**.
2. Enter the server-side **Conversions API Key**.


![](/assets/new_connector_config.ab5335996c2d0e625e088756f0b1df3de21550b7c14b7031dc7b6be2c41c4420.01afb486.webp)

1. Select **Continue**.
2. Enter a name for the authentication and select **Done**.
3. Select the authentication, and configure the connector parameters.


### Authentication Parameters

| Field | Description | Required |
|  --- | --- | --- |
| **Conversions API Key** | Server-side key used only to send conversion events. The key is stored as a secret and is not displayed after creation. | Yes |


### Define Your Query

Treasure Console supports multiple ways to export data. To export from Data Workbench:

1. Navigate to **Data Workbench > Queries**.
2. Select **New Query** and define your query.
3. Select **Export Results**.
4. Select an existing OpenAI Ads Conversion API authentication, or create one as described previously.
5. Configure the export parameters.


![](/assets/export_config.d82d97ee87ce4b907ddbc614ea761ca26749edc4d176895748abe54da58dda02.01afb486.webp)

1. Select **Done**.


### Connector Configuration Parameters

| Field | Description | Required | Default |
|  --- | --- | --- | --- |
| **Pixel ID** | The OpenAI Ads Pixel ID that receives conversion events. | Yes |  |
| **Skip Invalid Records** | When enabled, records that fail connector validation are counted and skipped. When disabled, the job stops on the first invalid record. | No | `true` |


## Query Data Specifications

Each query-result row represents one conversion event. Column names are trimmed and matched case-insensitively. For example, `EVENT_ID`, `Event_Id`, and `event_id` are equivalent.

Duplicate names that differ only by case or surrounding whitespace are invalid. Unknown columns are ignored. Columns with `NULL` values are skipped for that record.

### Export Query Specifications

| Specification | Description |
|  --- | --- |
| **Required Columns** | Every record requires:- `event_id`
- `event_type`
- `event_timestamp`

 |
| **Data Shape** | The connector derives `events[].data.type` from `event_type`. Do not include a separate data-type column. |
| **Custom Events** | When `event_type` is `custom`, `custom_event_name` is required. It must contain 1–64 letters, digits, underscores, or hyphens; begin and end with a letter or digit; and not match a standard event name. |
| **Web Events** | When `action_source` is `web`, `source_url` is required and must contain a URL scheme and host. |
| **App Lifecycle Events** | `app_installed` and `app_opened` require `action_source` to be `mobile_app`. |
| **Monetary Values** | `data_currency` is required when `data_amount` is present. For item-level values, provide `content_currency[N]` or an event-level `data_currency` when `content_amount[N]` is present. |
| **Numbered User Fields** | User-list columns accept numeric suffixes, such as `email`, `email1`, and `email2`. OpenAI uses the first three valid, unique values in schema order and ignores additional values without rejecting the event. |
| **Numbered Content Items** | Content columns with the same numeric suffix form one item in `events[].data.contents[]`. The unsuffixed columns form the first item. |
| **Variant Dictionary Pairs** | `content_variant_dict_key[N]` and `content_variant_dict_value[N]` must be provided together. They create a string map on the correspondingly suffixed content item. For example, key `size` and value `medium` produce `{"size":"medium"}`. |
| **Duplicate Columns** | Duplicate column names are not allowed after trimming and case normalization. |
| **Unknown Columns** | Columns that do not match a supported name or pattern are ignored. |


### Supported Event Types

| `event_type` | Derived `data.type` | Description |
|  --- | --- | --- |
| `app_installed` | `customer_action` | A user installs an application. Requires `action_source=mobile_app`. |
| `app_opened` | `customer_action` | A user opens an application. Requires `action_source=mobile_app`. |
| `appointment_scheduled` | `customer_action` | A user books a meeting, demo, or consultation. |
| `checkout_started` | `contents` | A user starts checkout. |
| `contents_viewed` | `contents` | A user views a product, listing, article, or other content unit. |
| `custom` | `custom` | A user-defined event that is not covered by the standard taxonomy. Requires `custom_event_name`. |
| `items_added` | `contents` | A user adds one or more items to a cart, bundle, or selection. |
| `lead_created` | `customer_action` | A user submits a lead form or requests contact. |
| `order_created` | `contents` | A purchase is completed. |
| `page_viewed` | `contents` | A user lands on or views an important page. |
| `registration_completed` | `customer_action` | A user finishes an account or event registration flow. |
| `subscription_created` | `plan_enrollment` | A paid subscription starts. |
| `trial_started` | `plan_enrollment` | A free trial starts. |


### Event Fields

| Column Name | Destination Field | Data Type | Required | Description |
|  --- | --- | --- | --- | --- |
| `event_id` | `events[].id` | String | Yes | Non-empty, stable identifier. Reuse it for retries and browser/API deduplication. |
| `event_type` | `events[].type` | String | Yes | Supported standard event name or `custom`. Matching is case-insensitive. |
| `event_timestamp` | `events[].timestamp_ms` | Long or Timestamp | Yes | Unix time in milliseconds, or an Embulk timestamp converted to milliseconds. Must be within the allowed API window. |
| `custom_event_name` | `events[].custom_event_name` | String | When `event_type=custom` | Custom event name. OpenAI converts the value to lowercase. |
| `source_url` | `events[].source_url` | String | When `action_source=web` | URL where the web conversion occurred. Nonblank values must include a scheme and host. |
| `action_source` | `events[].action_source` | String | Conditional | Accepted values: `web`, `mobile_app`, `offline`, `physical_store`, `phone_call`, `email`, or `other`. Matching is case-insensitive. |
| `oppref` | `events[].oppref` | String | No | Opaque OpenAI attribution identifier. Passed through without modification. |
| `opt_out` | `events[].opt_out` | Boolean or Long | No | Use a boolean or `0`/`1`. |


### Event Data Fields

| Column Name | Destination Field | Data Type | Required | Description |
|  --- | --- | --- | --- | --- |
| `data_amount` | `events[].data.amount` | Long | No | Monetary value in the currency's standard minor unit. For example, use `4200` for USD 42.00. |
| `data_currency` | `events[].data.currency` | String | When `data_amount` is present | Three-letter ISO 4217 code. The connector converts the value to uppercase. |
| `data_plan_id` | `events[].data.plan_id` | String | No | Internal plan identifier. Available only for `plan_enrollment` and `custom` data types. |


### Content Item Fields

Use numeric suffixes to create multiple content items. For example, `content_id1`, `content_name1`, and `content_quantity1` describe the same item.

Content fields are supported when the derived data type is `contents`, `plan_enrollment`, or `custom`. They are invalid for `customer_action` events.

| Column Name | Destination Field | Data Type | Description |
|  --- | --- | --- | --- |
| `content_id`, `content_id[N]` | `events[].data.contents[].id` | String | Internal product, item, or content identifier. |
| `content_group_id`, `content_group_id[N]` | `events[].data.contents[].group_id` | String | Product group or parent item identifier. |
| `content_name`, `content_name[N]` | `events[].data.contents[].name` | String | Human-readable item name. |
| `content_type`, `content_type[N]` | `events[].data.contents[].content_type` | String | Non-empty item category, such as `product`, `plan`, or `page`. |
| `content_quantity`, `content_quantity[N]` | `events[].data.contents[].quantity` | Long | Item quantity. |
| `content_amount`, `content_amount[N]` | `events[].data.contents[].amount` | Long | Item-level monetary value in the currency's standard minor unit. |
| `content_currency`, `content_currency[N]` | `events[].data.contents[].currency` | String | Three-letter ISO 4217 code. May be omitted when `data_currency` applies to the whole event. |
| `content_variant_dict_key`, `content_variant_dict_key[N]` | Key in `events[].data.contents[].variant_dict` | String | Paired with the value column for the same suffix. |
| `content_variant_dict_value`, `content_variant_dict_value[N]` | Value in `events[].data.contents[].variant_dict` | String | Paired with the key column for the same suffix. |


### Hashed User-Matching Fields

Raw values are normalized, UTF-8 encoded, and SHA-256 hashed to lowercase hexadecimal. A supplied 64-character hexadecimal hash is used as-is and converted to lowercase.

| Column Name | Destination Field | Normalization |
|  --- | --- | --- |
| `email`, `email[N]` | `events[].user.emails_sha256[]` | Trim leading and trailing whitespace and convert to lowercase. |
| `phone_number`, `phone_number[N]` | `events[].user.phone_numbers_sha256[]` | Remove whitespace, parentheses, periods, hyphens, a leading plus sign, and leading zeroes. The result must contain 8–15 digits. |
| `external_id`, `external_id[N]` | `events[].user.external_ids_sha256[]` | Trim leading and trailing whitespace. Preserve case and other characters. |
| `first_name`, `first_name[N]` | `events[].user.first_names_sha256[]` | Convert to lowercase and remove whitespace and ASCII punctuation. Preserve non-ASCII characters. |
| `last_name`, `last_name[N]` | `events[].user.last_names_sha256[]` | Convert to lowercase and remove whitespace and ASCII punctuation. Preserve non-ASCII characters. |


### Raw User-Matching Fields

| Column Name | Destination Field | Data Type | Notes |
|  --- | --- | --- | --- |
| `region`, `region[N]` | `events[].user.regions[]` | String | Raw region value. Maximum normalized length is 128 characters. |
| `postal_code`, `postal_code[N]` | `events[].user.postal_codes[]` | String | Letters, numbers, spaces, or hyphens; maximum 32 characters. |
| `city`, `city[N]` | `events[].user.cities[]` | String | Raw city value. Maximum normalized length is 128 characters. |
| `country`, `country[N]` | `events[].user.countries[]` | String | Two-letter country code. The connector converts the value to uppercase. |
| `android_advertising_id` | `events[].user.android_advertising_id` | String | Raw Android GAID in UUID format. Apple IDFA is not supported. |
| `obref` | `events[].user.obref` | String | Opaque browser reference from the Pixel `__obref` cookie. Sent only when nonblank and measurement consent permits. |
| `ip_address` | `events[].user.ip_address` | String | Valid IPv4 or IPv6 address. |
| `user_agent` | `events[].user.user_agent` | String | Non-empty client user-agent string. |


### Example Query

```sql
SELECT
  CONCAT('order-', CAST(order_id AS VARCHAR)) AS event_id,
  'order_created' AS event_type,
  CAST(TO_UNIXTIME(order_time) * 1000 AS BIGINT) AS event_timestamp,
  'web' AS action_source,
  confirmation_url AS source_url,
  total_amount_minor_units AS data_amount,
  currency_code AS data_currency,
  customer_email AS email,
  country_code AS country,
  product_id AS content_id,
  product_name AS content_name,
  'product' AS content_type,
  quantity AS content_quantity,
  'size' AS content_variant_dict_key,
  product_size AS content_variant_dict_value
FROM completed_orders
```

### (Optional) Schedule Query Export Jobs

You can use Scheduled Jobs with Result Export to periodically write the output result to a target destination that you specify.

Treasure Data's scheduler feature supports periodic query execution to achieve high availability.

When two specifications provide conflicting schedule specifications, the specification requesting to execute more often is followed while the other schedule specification is ignored.

For example, if the cron schedule is `'0 0 1 * 1'`, then the 'day of month' specification and 'day of week' are discordant because the former specification requires it to run every first day of each month at midnight (00:00), while the latter specification requires it to run every Monday at midnight (00:00). The latter specification is followed.

#### Scheduling your Job Using Treasure Console

1. Navigate to **Data Workbench > Queries**
2. Create a new query or select an existing query.
3. Next to **Schedule**, select None.
![](/assets/image2021-1-15_17-28-51.f1b242f6ecc7666a0097fdf37edd1682786ec11ef80eff68c66f091bc405c371.0f87d8d4.png)
4. In the drop-down, select one of the following schedule options:
![](/assets/image2021-1-15_17-29-47.45289a1c99256f125f4d887e501e204ed61f02223fde0927af5f425a89ace0c0.0f87d8d4.png)
| Drop-down Value | Description |
|  --- | --- |
| Custom cron... | Review [Custom cron... details](#custom-cron-details). |
| @daily (midnight) | Run once a day at midnight (00:00 am) in the specified time zone. |
| @hourly (:00) | Run every hour at 00 minutes. |
| None | No schedule. |


#### Custom cron... Details

![](/assets/image2021-1-15_17-30-23.0f94a8aa5f75ea03e3fec0c25b0640cd59ee48d1804a83701e5f2372deae466c.0f87d8d4.png)

| **Cron Value** | **Description** |
|  --- | --- |
| `0 * * * *` | Run once an hour. |
| `0 0 * * *` | Run once a day at midnight. |
| `0 0 1 * *` | Run once a month at midnight on the morning of the first day of the month. |
| "" | Create a job that has no scheduled run time. |


```
 *    *    *    *    *
 -    -    -    -    -
 |    |    |    |    |
 |    |    |    |    +----- day of week (0 - 6) (Sunday=0)
 |    |    |    +---------- month (1 - 12)
 |    |    +--------------- day of month (1 - 31)
 |    +-------------------- hour (0 - 23)
 +------------------------- min (0 - 59)
```

The following named entries can be used:

- Day of Week: sun, mon, tue, wed, thu, fri, sat.
- Month: jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec.


A single space is required between each field. The values for each field can be composed of:

div
| Field Value  | Example  | Example Description  |
|  --- | --- | --- |
| A single value, within the limits displayed above for each field. |  |  |
| A wildcard `'*'` to indicate no restriction based on the field. | `'0 0 1 * *'` | Configures the schedule to run at midnight (00:00) on the first day of each month. |
| A range `'2-5'`, indicating the range of accepted values for the field. | `'0 0 1-10 * *'` | Configures the schedule to run at midnight (00:00) on the first 10 days of each month. |
| A list of comma-separated values `'2,3,4,5'`, indicating the list of accepted values for the field. | `'0 0 1,11,21 * *'` | Configures the schedule to run at midnight (00:00) every 1st, 11th, and 21st day of each month. |
| A periodicity indicator `'*/5'` to express how often based on the field's valid range of values a schedule is allowed to run. | `'30 */2 1 * *'` | Configures the schedule to run on the 1st of every month, every 2 hours starting at 00:30. `'0 0 */5 * *'` configures the schedule to run at midnight (00:00) every 5 days starting on the 5th of each month. |
| A comma-separated list of any of the above except the `'*'` wildcard is also supported `'2,*/5,8-10'`. | `'0 0 5,*/10,25 * *'` | Configures the schedule to run at midnight (00:00) every 5th, 10th, 20th, and 25th day of each month. |


1. (Optional) You can delay the start time of a query by enabling the Delay execution.


### Execute the Query

Save the query with a name and run, or just run the query. Upon successful completion of the query, the query result is automatically exported to the specified destination.

Scheduled jobs that continuously fail due to configuration errors may be disabled on the system side after several notifications.

(Optional) You can delay the start time of a query by enabling the Delay execution.

## Activate a Segment in Audience Studio

You can also send segment data to the target platform by creating an activation in the Audience Studio.

1. Navigate to **Audience Studio**.
2. Select a parent segment.
3. Open the target segment, right-mouse click, and then select **Create Activation.**
4. In the **Details** panel, enter an Activation name and configure the activation according to the previous section on Configuration Parameters.
5. Customize the activation output in the **Output Mapping** panel.


![](/assets/ouput.b2c7f1d909c4f98ed10f5300df858a4b19f71a3b0834df952f5fb24018a5ea78.8ebdf569.png)

- Attribute Columns
  - Select **Export All Columns** to export all columns without making any changes.
  - Select **+ Add Columns** to add specific columns for the export. The Output Column Name pre-populates with the same Source column name. You can update the Output Column Name. Continue to select **+ Add Columns**to add new columns for your activation output.
- String Builder
  - **+ Add string** to create strings for export. Select from the following values:
    - String: Choose any value; use text to create a custom value.
    - Timestamp: The date and time of the export.
    - Segment Id: The segment ID number.
    - Segment Name: The segment name.
    - Audience Id: The parent segment number.


1. Set a **Schedule**.


![](/assets/snippet-output-connector-on-audience-studio-2024-08-28.a99525173709da1eb537f839019fa7876ffae95045154c8f2941b030022f792c.8ebdf569.png)

- Select the values to define your schedule and optionally include email notifications.


1. Select **Create**.


If you need to create an activation for a batch journey, review [Creating a Batch Journey Activation](/products/customer-data-platform/journey-orchestration/batch/creating-a-batch-journey-activation).

## (Optional) Configure Export Results in Workflow

Use an existing OpenAI Ads Conversion API connection in Treasure Workflow.

```yaml
_export:
  td:
    database: conversions_db

+openai_conversion_export:
  td>: queries/openai_conversions.sql
  database: ${td.database}
  result_connection: openai_ads_conversions
  result_settings:
    pixel_id: "xxxxxx"
    skip_invalid_records: true
```

## (Optional) Export Integration Using the CLI

The [TD Toolbelt](https://toolbelt.treasuredata.com/) can export query results from the command line. Specify the existing Console authentication and connector settings with `--result`.

```json
{
  "type": "openai_conversion_api",
  "conversions_api_key": "xxxxxx",
  "pixel_id": "xxxxxxx",
  "skip_invalid_records": true
}
```

### CLI Parameters

| Name | Description | Value | Default | Required |
|  --- | --- | --- | --- | --- |
| `type` | Connector type identifier. | `openai_conversion_api` |  | Yes |
| `conversions_api_key` | Server-side Conversions API key | String |  | Yes |
| `pixel_id` | OpenAI Ads Pixel ID receiving the events. | String |  | Yes |
| `skip_invalid_records` | Skip locally invalid records instead of stopping the job. | `true` / `false` | `true` | No |


### CLI Example

```bash
td query \
  --result '{"type":"openai_conversion_api","conversions_api_key":"xxxx","pixel_id":"xxxxxx","skip_invalid_records":true}' \
  -d conversions_db \
  "SELECT event_id, event_type, event_timestamp, email FROM conversion_events" \
  -T presto
```

## Other

- [OpenAI Ads Conversions API](https://developers.openai.com/ads/conversions-api)
- [OpenAI Ads Supported Events](https://developers.openai.com/ads/supported-events)