Ads

Conversions API

Send purchases, leads and offline conversions from your server to Meta, Google, LinkedIn, TikTok, Pinterest, Reddit and ChatGPT with one event shape.

A browser pixel misses a lot: blocked scripts, purchases that close on the phone, deals that sign weeks later in your CRM. Server-side conversions fill the gap. You send the event from your backend once, in one shape, and Pozy delivers it to the ad network's own conversions API, hashed and formatted the way that network expects. Networks then attribute and optimize on it next to what the pixel saw.

Network Connection Send events Notes
Meta metaads Yes Web, app, offline, CRM and messaging events. Event Match Quality read-back.
Google Ads googleads Yes Click and enhanced conversions. Adjustments (refunds, restatements).
LinkedIn linkedinads Yes Offline conversions to a conversion rule. Attribution read-back.
TikTok tiktokads Yes Offline events only (store, CRM, call center).
Pinterest pinterestads Yes Event Quality Score read-back.
Reddit redditads Yes To a pixel created in Reddit Ads Manager.
ChatGPT Ads openaiads Yes Needs the pixel created first.
X xads No Measure X with the X Pixel.

#Send your first event

#Find the destination

Every network calls the receiving object something different. GET /v1/accounts/{accountId}/conversion-destinations lists the ones the ads connection can reach, so you never have to guess the format.

cURL
curl https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/conversion-destinations \
  -H "Authorization: Bearer $POZY_API_KEY"
Response
{
  "platform": "metaads",
  "destinations": [
    { "id": "1729525464415281", "name": "Acme website pixel", "status": "active", "adAccountId": "act_1234567890" }
  ]
}
Network destinationId is Example
Meta The pixel (dataset) id 1729525464415281
Google Ads The conversion action resource name customers/1234567890/conversionActions/987654321
LinkedIn The conversion rule id or URN 104012
TikTok The Offline Event Set id 7057103914977558530
Pinterest The numeric ad account id 549755885175
Reddit The pixel id a2_gu5tcd9ah5se
ChatGPT Ads The pixel's site id 4Kx9TqLm2VbN7wRz1PsYcD

#Send the event

POST /v1/ads/conversions takes the ads connection, the destination and up to thousands of events. The connection's platform picks the network.

curl -X POST https://api.pozy.ai/v1/ads/conversions \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "destinationId": "1729525464415281",
    "events": [{
      "eventName": "Purchase",
      "eventTime": 1804064400,
      "eventId": "order_1042",
      "value": 129.99,
      "currency": "USD",
      "actionSource": "web",
      "sourceUrl": "https://shop.example.com/checkout/thanks",
      "user": {
        "email": "jane@example.com",
        "phone": "+14155551234",
        "ipAddress": "203.0.113.7",
        "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5)",
        "clickIds": { "fbc": "fb.1.1804060000.IwAR2...", "fbp": "fb.1.1803990000.1234567890" }
      },
      "items": [{ "id": "sku-981", "name": "Linen throw", "price": 64.99, "quantity": 2 }]
    }]
  }'

#Check the result

Response
{
  "platform": "metaads",
  "eventsReceived": 1,
  "eventsFailed": 0,
  "failures": [],
  "traceId": "AbCdEf123456"
}

A rejected event is not an HTTP error. The call returns 200 and lists what did not land in failures[], each with eventIndex, eventId, message and code. Always read eventsFailed. traceId is the network's own request id, useful when you contact that network's support.

#The event shape

Field Required What to send
eventName Yes A standard name (Purchase, Lead, CompleteRegistration, AddToCart, InitiateCheckout, AddPaymentInfo, Subscribe, StartTrial, ViewContent, Search, Contact, SubmitApplication, Schedule, PageView) or your own name. See event names.
eventTime Yes Unix time in seconds, UTC. A millisecond value lands thousands of years in the future and is dropped.
eventId Yes Your stable id for this conversion, such as the order id. It is the dedupe key.
value, currency No The conversion value in whole units, and an ISO currency code.
user Yes Who converted. See match keys.
items No Line items: id, name, price, quantity, category.
sourceUrl No The page where it happened.
actionSource No web (default), app, offline, crm, phone_call, system_generated or business_messaging.
messaging No For conversations started from an ad: channel (whatsapp, messenger, instagram_direct) plus ids such as ctwaClid or pageScopedUserId. Meta only.
platformData No Extra network-specific fields Pozy does not model. On Meta they merge into custom_data.

At the request root, testCode sends the batch to the network's test tools, and consent carries the person's choices. Both are explained below.

#Match keys

The more identifiers you send, the more conversions a network can tie back to an ad. Send them in plain text. Pozy normalizes and hashes personal data with SHA-256 to each network's spec before anything leaves Pozy, including Google's rule that strips dots and +suffix from Gmail addresses.

Group Fields under user
Contact email, phone (E.164, with +), firstName, lastName
Location and demographics country, city, state, zip, dob (YYYYMMDD), gender (f or m)
Identifiers externalId (your own user id), ipAddress, userAgent, leadId
Click ids, under clickIds fbc, fbp (Meta), gclid, gbraid, wbraid (Google), li_fat_id (LinkedIn), epik (Pinterest), rdt_cid, rdt_uuid (Reddit)

ipAddress, userAgent and click ids are sent unhashed, as the networks require. LinkedIn takes externalId in plain text too.

Capture click ids at the first visit

Click ids arrive on the landing page URL (fbclid, gclid, li_fat_id, rdt_cid) or in first-party cookies (_fbp, _epik, _rdt_uuid). Store them with the visitor when they land, then attach them to every later conversion. They are the strongest match signal each network has.

Minimum identity per network:

Network Each event needs at least
Meta Any match key; more is better
Google Ads A click id, or an email or phone (enhanced conversions for leads)
LinkedIn An email, li_fat_id, or both first and last name
TikTok An email or a phone
Pinterest An email, or the IP address with the user agent
Reddit A click id, rdt_uuid, email, phone, externalId, or IP with user agent
ChatGPT Ads An email (the identifier it matches on)

Events missing the minimum are reported in failures[] (for example INVALID_EVENT or MISSING_IDENTITY) and the rest of the batch still goes.

#Deduplicate against the pixel

Most sites send the same purchase from the browser pixel and from the server. Use the same eventId in both places and the network counts it once.

Network How the dedupe works
Meta Pixel and server events with the same id merge within 48 hours.
Google Ads eventId becomes the transaction id, so repeated uploads of one conversion are ignored.
LinkedIn When the Insight Tag fires the same id, LinkedIn keeps the tag's event and drops the server copy.
Pinterest Matched against the Pinterest tag by event id.
Reddit Sent as the conversion id and matched against the Reddit pixel.
ChatGPT Ads Matched against the pixel by event id.
The usual cause of inflated numbers

A missing eventId, or a different id on the pixel and the server, counts the conversion twice. Pozy's store installs (see Pixels) set an event id on browser events so they can be matched.

#Event names per network

Network How eventName is used
Meta Any name. Standard names match Meta's built-in events; anything else is a custom event.
Google Ads Ignored. The conversion action decides the category.
LinkedIn Ignored. The conversion rule's type decides the category.
TikTok Sent as is.
Pinterest Standard names map to Pinterest's (Purchase is checkout, CompleteRegistration is signup, PageView is page_visit). Other names match a custom event of that name.
Reddit Standard names map to Reddit's (CompleteRegistration is SIGN_UP, PageView is PAGE_VISIT). Other names become custom events.
ChatGPT Ads Nine names map to its own types (Purchase is order_created, Lead is lead_created, AddToCart is items_added). Other names become custom events.

#Offline and CRM conversions

Set actionSource to where it happened and send the identifiers you have from the CRM.

  • Meta: offline, crm, phone_call and system_generated are all accepted on the same pixel.
  • Google Ads: create a conversion action for uploads (below), then send events with a gclid when you kept one, or a hashed email or phone when you did not.
  • LinkedIn: send to a conversion rule. eventName does not matter; the rule's type does.
  • TikTok: the only path through this endpoint. destinationId is an Offline Event Set and each event needs an email or phone.

#Create a destination for uploads

POST /v1/accounts/{accountId}/conversion-destinations creates one on Google Ads and LinkedIn.

Google: an upload conversion action
curl -X POST https://api.pozy.ai/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/conversion-destinations \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "adAccountId": "1234567890", "name": "Offline purchase", "type": "Purchase" }'
  • Google: type is a standard name or a Google category (PURCHASE, SUBSCRIBE_PAID, SIGNUP, IMPORTED_LEAD, BOOK_APPOINTMENT). The same name again returns the existing action; the same name with a different category returns 409 idempotency_conflict. countingType (MANY_PER_CLICK by default) and primaryForGoal (default false, so it records without driving bids) are Google-only.
  • LinkedIn: a rule binds a type to the destination. By default it attaches to every campaign in the ad account (autoAssociationType: "ALL_CAMPAIGNS"); OBJECTIVE_BASED attaches only matching campaigns and NONE lets you manage it with the associations endpoints. Click and view windows default to 30 and 7 days and take 1, 7, 30, 90 or 365. Creating a rule is not idempotent.

On LinkedIn, GET .../conversion-destinations/{destinationId}/metrics reads post-click and view-through conversions and value back per rule, with granularity of ALL, DAILY, MONTHLY or YEARLY.

#Correct a conversion on Google Ads

POST /v1/ads/conversions/adjustments changes conversions you already uploaded. Google Ads is the only network with this.

adjustmentType Use it for
RETRACTION Refunds, chargebacks, cancelled orders
RESTATEMENT A changed value. Send the new total in restatementValue, not the difference.
ENHANCEMENT Adding an email or phone to a conversion after the fact

Identify the original with orderId, or gclid plus conversionTime. adjustmentTime is Unix seconds.

#Send once, deliver everywhere

When the same purchase should reach every network a customer advertises on, use the profile-level event stream instead of calling each destination.

#Save the destinations

POST /v1/events/destinations links an ads connection and destination to a profile. eventNames limits what it receives, for example only Purchase. Leave it out to forward everything.

cURL
curl -X POST https://api.pozy.ai/v1/events/destinations \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "profileId": "66b2e19d8c3f5a7e9d0b1c2a", "accountId": "66b2e19d8c3f5a7e9d0b1c2d", "destinationId": "1729525464415281", "eventNames": ["Purchase", "Lead"] }'

#Send events to the profile

POST /v1/events takes profileId and the same events array. Pozy forwards each event to every saved destination, exactly as POST /v1/ads/conversions would. Pass destinations in the body to override the saved list for one request.

#Read the stream

GET /v1/events?profileId=... lists stored events, newest first. Personal data is never returned: hasEmail, hasPhone and hasExternalId only say whether an event carried them.

  • One destination failing never blocks the others. Each reports its own result.
  • Pozy stores email, phone and externalId only as SHA-256 hashes, and keeps only the origin, path and attribution parameters of sourceUrl.
  • Events sent with testCode are forwarded but not stored.

#Test mode

testCode at the request root lets you check an integration without polluting live data.

Network What testCode does
Meta The batch shows in the Test Events tab of Events Manager.
Pinterest Pinterest validates the batch and records nothing.
Reddit Sent as Reddit's test id, visible in its Events Manager.

Remove testCode for production traffic. An event sent with it is not counted on Pinterest.

The root consent object takes adUserData and adPersonalization, each GRANTED or DENIED. Split batches by consent state rather than by network.

Network Effect of DENIED
Meta Events are flagged for Limited Data Use, with the location derived from the event.
Google Ads Sent as Google's EEA and UK consent signals. Omit consent outside those regions if you have no data.
Pinterest Each event is marked opt-out.
Reddit User identifiers are dropped; only the click id is sent.

#Match quality

GET /v1/ads/conversions/quality?accountId=...&destinationId=... reads the network's own scoring of your server events, without opening its dashboard.

  • Meta: Event Match Quality per event name, compositeScore from 0 to 10, coverage per identifier in matchKeys, and how well server events overlap the pixel. Web events only.
  • Pinterest: Event Quality Score over the last 14 days, per event name, with matchKeys and pixel overlap. There is no composite score.

#Batching and limits

Pozy splits large batches for you. What happens when one event is bad depends on the network:

Network Chunk size One bad event
Meta 1,000 Fails its chunk
Google Ads 2,000 Listed in failures[]
LinkedIn 5,000 Fails its chunk
TikTok 1,000 The network accepts or rejects a batch whole
Pinterest Per event Reported on its own
Reddit 1,000 Reported on its own; rejected chunks show REDDIT_REJECTED
ChatGPT Ads 1,000 Fails its chunk

Google can take up to 3 hours to show an uploaded conversion. Reddit accepts events up to 7 days old.

#Permissions and reconnects

Some networks grant the conversions permission separately. A connection made before that permission existed has to go through the connect flow again.

Network Symptom Fix
LinkedIn 403 linkedin_reconnect_required Reconnect LinkedIn Ads so the grant includes conversions.
TikTok Calls refused for the Offline Events permission Reconnect TikTok Ads.
Reddit 403 reconnect_required Reconnect Reddit Ads to grant adsconversions.
Pinterest 403 Give the connected user a Business Access role on the ad account.
ChatGPT Ads 422 tracking_tag_required Create the pixel first.

#Endpoints

Endpoint What it does
GET /v1/ads/conversions/quality Get Event Match Quality
POST /v1/ads/conversions Send conversion events
POST /v1/ads/conversions/adjustments Adjust uploaded conversions
GET /v1/ads/conversions/actions List conversion actions
POST /v1/ads/conversions/actions Create website conversion action
GET /v1/ads/conversions/goals List account conversion goals
PATCH /v1/ads/conversions/goals Update account conversion goals
PATCH /v1/ads/conversions/actions/{actionId} Update a conversion action's settings
GET /v1/ads/conversions/custom-goals List custom conversion goals
POST /v1/ads/conversions/custom-goals Create a custom conversion goal
PATCH /v1/ads/conversions/custom-goals/{goalId} Update a custom conversion goal
DELETE /v1/ads/conversions/custom-goals/{goalId} Remove a custom conversion goal
GET /v1/events List stored events
POST /v1/events Send events to every destination
GET /v1/events/destinations List event destinations
POST /v1/events/destinations Add an event destination
DELETE /v1/events/destinations/{destinationId} Remove an event destination
GET /v1/accounts/{accountId}/conversion-destinations List conversion destinations
POST /v1/accounts/{accountId}/conversion-destinations Create a conversion destination
GET /v1/accounts/{accountId}/conversion-destinations/{destinationId} Get a conversion destination
PATCH /v1/accounts/{accountId}/conversion-destinations/{destinationId} Update a conversion destination
DELETE /v1/accounts/{accountId}/conversion-destinations/{destinationId} Delete a conversion destination
GET /v1/accounts/{accountId}/conversion-destinations/{destinationId}/associations List associated campaigns
POST /v1/accounts/{accountId}/conversion-destinations/{destinationId}/associations Associate campaigns
DELETE /v1/accounts/{accountId}/conversion-destinations/{destinationId}/associations Remove associated campaigns
GET /v1/accounts/{accountId}/conversion-destinations/{destinationId}/metrics Get attribution metrics

Meta custom conversions (rules over pixel events) live on the ad account: GET and POST /v1/accounts/{accountId}/custom-conversions.