Skip to main content

OpenAI Conversions

This guide covers how to submit conversion events to OpenAI through the TNT Growth API.

Authentication

All requests require an x-api-key header with your client-specific API key provided by TNT Growth.

-H "x-api-key: YOUR_API_KEY"

Submitting a Conversion

Endpoint

POST /api/openai/events/add

Request Body Schema

FieldTypeRequiredDescription
pixelIdstringYesYour OpenAI Pixel ID (provided during onboarding)
idstringYesUnique event ID for deduplication
typestringYesOpenAI event type (see below)
timestampMsnumberYesUnix timestamp in milliseconds of when the event occurred
customEventNamestringConditionalCustom event name. Required when type is custom
opprefstringNoOpenAI attribution reference captured from the ad click
sourceUrlstringConditionalURL where the event occurred. Required when actionSource is web
actionSourcestringNoWhere the event originated (see below). Defaults to offline when omitted
userDataobjectNoCustomer identifiers for matching (see below)
optOutbooleanNoForwarded to OpenAI as opt_out; does not suppress userData transmission or matching
validateOnlybooleanNoAsk OpenAI to validate the event without processing it
dataobjectYesConversion details (see below)
ingestionSourcestringNoOrigin of the event (e.g. pixel, conversion-api)
utmSourcestringNoUTM source parameter (e.g. openai) — stored for attribution reporting
utmMediumstringNoUTM medium parameter (e.g. paid-search) — stored for attribution reporting
utmCampaignstringNoUTM campaign name — stored for attribution reporting
utmTermstringNoUTM search keyword — stored for attribution reporting
utmContentstringNoUTM content parameter (ad/creative variant) — stored for attribution reporting
This endpoint does not accept tntId

Unlike the other conversion endpoints, the OpenAI path does not read the attribution handle. Nothing on this path resolves an attribution session, so the field is deliberately not declared rather than accepted and ignored. Send oppref and userData — those are what OpenAI matches on.

Timestamp window

timestampMs must be within the last 7 days and cannot be more than 10 minutes in the future.

Event Types

type must be one of:

app_installed, app_opened, appointment_scheduled, checkout_started, contents_viewed, custom, items_added, lead_created, order_created, page_viewed, registration_completed, subscription_created, trial_started

When type is custom, include customEventName.

Action Sources

actionSource must be one of:

web, mobile_app, offline, physical_store, phone_call, email, other

If actionSource is omitted, TNT Growth sends the event to OpenAI with offline.

Action source requirements
  • sourceUrl is required when actionSource is web
  • app_installed and app_opened require actionSource to be mobile_app

User Data Fields

Include as many identifiers as possible to improve match quality.

FieldTypeDescription
emailstringCustomer email address. Hashed before sending to OpenAI
emailSha256stringSHA-256 hashed email address
externalIdstringYour internal customer ID. Hashed before sending to OpenAI
externalIdSha256stringSHA-256 hashed external customer ID
countrystringCustomer country
citystringCustomer city
zipCodestringCustomer postal/ZIP code
ipAddressstringIP address of the customer
userAgentstringBrowser user agent string

Raw email and externalId values are hashed on TNT Growth servers before they leave. If you already hash identifiers yourself, send emailSha256 or externalIdSha256.

Consent and optional identifiers

TNT Growth forwards any optional identifiers you include. If consent is absent for a user identifier, omit that identifier from userData; setting optOut does not remove it from the outbound OpenAI event.

Data Fields

FieldTypeRequiredDescription
typestringYesData payload type (see below)
planIdstringNoPlan or subscription identifier
amountnumberNoInteger amount in minor currency units (for USD, 15000 represents $150.00)
currencystringConditionalCurrency code. Required when amount is present
contentsarrayNoArray of content objects (see below)

data.type must be one of: contents, customer_action, plan_enrollment, custom.

Content Fields (data.contents[])

FieldTypeRequiredDescription
idstringNoContent, product, or item ID
namestringNoContent, product, or item name
contentTypestringNoType/category of the content
quantitynumberNoQuantity of the content
amountnumberNoInteger amount in minor currency units (for USD, 15000 represents $150.00)
currencystringConditionalCurrency code. Required when content amount is present

The example below uses 15000 with USD, which represents $150.00.

Example Request

TIMESTAMP_MS=$(date +%s000)

curl -X POST https://flex.tntgrowth.io/api/openai/events/add \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"pixelId": "px_openai_123456789",
"id": "order-12345",
"type": "order_created",
"timestampMs": '"${TIMESTAMP_MS}"',
"oppref": "oppref_abc123",
"sourceUrl": "https://example.com/thank-you",
"actionSource": "web",
"userData": {
"email": "customer@example.com",
"externalId": "cust-12345",
"country": "US",
"city": "Austin",
"zipCode": "78701",
"ipAddress": "203.0.113.10",
"userAgent": "Mozilla/5.0"
},
"data": {
"type": "contents",
"amount": 15000,
"currency": "USD",
"contents": [
{
"id": "SKU-001",
"name": "Treatment Plan",
"contentType": "service",
"quantity": 1,
"amount": 15000,
"currency": "USD"
}
]
},
"utmSource": "openai",
"utmMedium": "paid-search",
"utmCampaign": "brand-search-q2",
"utmTerm": "best treatment center",
"utmContent": "search-ad-variant-b"
}'

Example Response

{
"message": "OpenAI event successfully queued for client Example Client",
"data": {
"submissionId": "8f3c2d10-7e6b-4a51-9d8c-3b2e1f0a9b8c",
"duplicate": false
}
}

When a conversion is dedup-skipped (the same event was submitted within the last 24 hours), submissionId is null and duplicate is true.

Checking Submission Status

After submitting a conversion, use the submission ID to check processing status.

Endpoint

GET /api/conversions/submissions/:submissionId

Example Request

curl https://flex.tntgrowth.io/api/conversions/submissions/8f3c2d10-7e6b-4a51-9d8c-3b2e1f0a9b8c \
-H "x-api-key: YOUR_API_KEY"

Example Response

{
"id": "8f3c2d10-7e6b-4a51-9d8c-3b2e1f0a9b8c",
"source": "OPENAI",
"externalAccountId": "px_openai_123456789",
"expectedCount": 1,
"createdAt": "2026-05-11T12:00:00.000Z",
"scheduledFor": null,
"counts": {
"success": 1,
"failed": 0
},
"status": "complete"
}

Status Values

StatusDescription
pendingConversion is queued and waiting to be processed
pending_deliverySubmission is scheduled for future delivery. Currently only emitted by Google Ads ingest paths; OpenAI submissions never enter this state.
completeConversion processed successfully
complete_with_errorsProcessing finished but some conversions failed — use GET /api/conversions/openai/failed?submissionId=<id> to fetch the actual error messages
stalledProcessing has not progressed — contact support

The scheduledFor field is always null for OpenAI submissions and is included only for schema parity with Google Ads, which uses it for deferred delivery.

Inspecting Failed Conversions In Your Batch

When a submission ends with complete_with_errors, the submission status endpoint surfaces only the count of failed rows. Fetch the actual error messages with:

GET /api/conversions/openai/failed?submissionId=<submissionId>

The response payload mirrors the Google Ads failed-conversion shape — see Google Ads → Inspecting Failed Conversions In Your Batch.

Best Practices

  • Use a stable id for deduplication — send the same id if retrying the same conversion event
  • Include oppref when available — this is OpenAI's attribution reference and provides the strongest click-level attribution signal
  • Send user identifiers like email, externalId, ipAddress, and userAgent to improve match quality
  • Use accurate millisecond timestampstimestampMs must reflect when the event actually occurred and must be within OpenAI's accepted time window
  • Include sourceUrl for web events — OpenAI web events require it when actionSource is web
  • Poll with a delay — wait 30-60 seconds after submission before checking status