---
title: "Send conversion events"
method: POST
path: "/v1/ads/conversions"
tags: ["Conversions"]
---

# Send conversion events

`POST /v1/ads/conversions`

Relay one or more conversion events to the target ad platform's native Conversions API.
Platform is inferred from the provided `accountId`. Requires the Ads add-on.

Supported platforms:

- Meta (`metaads`) via Graph API
- Google Ads (`googleads`) via Data Manager API `ingestEvents`
- LinkedIn (`linkedinads`) via `/rest/conversionEvents`
- TikTok (`tiktokads`) via the Offline Events API `/offline/batch/` — OFFLINE conversions only
- OpenAI Ads (`openaiads`) via its Conversions API (a separate host, `bzr.openai.com`)

`destinationId` semantics differ per platform:

- Meta: pixel (dataset) ID, e.g. `123456789012345`
- Google: conversion action resource name, e.g. `customers/1234567890/conversionActions/987654321`
- LinkedIn: conversion rule ID or URN, e.g. `104012` or `urn:lla:llaPartnerConversion:104012`
- TikTok: Offline Event Set ID, e.g. `7057103914977558530`
- OpenAI Ads: pixel wire id (numeric `pixel_id`, distinct from the internal pixel id), as returned by `GET /v1/accounts/{accountId}/conversion-destinations`

TikTok notes: this path sends OFFLINE conversions (in-store / CRM / call-center), not web-pixel
events. Each event must carry an email or phone (TikTok requires at least one). The connected
TikTok ads account must have granted the Offline Events permission; older grants must reconnect.

OpenAI Ads notes: requires a tracking tag (pixel) to already exist on the account — returns 422
with code `TRACKING_TAG_REQUIRED` if `POST /v1/accounts/{accountId}/tracking-tags` hasn't been
called yet.

Callers can list valid destinations via `GET /v1/accounts/{accountId}/conversion-destinations`.

All PII (email, phone, names, external IDs) is hashed with SHA-256 server-side per each
platform's normalization spec, including Google's Gmail-specific dot/plus-suffix stripping.
Send plaintext. LinkedIn `externalIds` are passed through as plaintext per LinkedIn's spec;
only emails and phones are hashed.

For LinkedIn, the connected account must have been authorized after the Conversions API
rollout (i.e. the OAuth grant must include `rw_conversions`). Older accounts must reconnect.

Batching is handled automatically. Meta caps at 1000 events per request and rejects the
entire batch if any event is malformed. Google caps at 2000. LinkedIn caps at 5000 and is
also all-or-nothing per chunk. OpenAI Ads caps at 1000 per request; larger submissions are
split into 1000-event chunks, each all-or-nothing (a malformed event fails every event in
that chunk, not the whole request).

Dedup: pass a stable `eventId` on every event. Meta and LinkedIn use it to dedupe against
browser-side pixel/Insight Tag events; Google maps it to `transactionId`.

Per-platform `eventName` semantics:

- Meta: free-form. Standard names (Purchase, Lead, ...) match Meta's built-in events; custom strings are accepted.
- Google: ignored. The conversion action's category determines the event type. Send the standard name closest to your action for documentation, but the platform will not branch on it.
- LinkedIn: ignored. The conversion rule's `type` (LEAD, PURCHASE, etc.) is locked to the destination at rule-creation time. Send the standard name for documentation; LinkedIn does not branch on it.
- OpenAI Ads: a fixed subset of standard names (Purchase, Lead, AddToCart, ViewContent, InitiateCheckout, CompleteRegistration, Subscribe, StartTrial, Schedule) maps 1:1 onto OpenAI's own event-type enum; any other standard name or custom string is sent as `type: custom` with the name preserved.

## Request body

- object
  - `accountId` string, required — SocialAccount ID (metaads, googleads, linkedinads, tiktokads, or openaiads).
  - `destinationId` string, required — Platform destination identifier. For Meta, the pixel/dataset ID. For Google, the conversion action resource name. For LinkedIn, the conversion rule ID or full `urn:lla:llaPartnerConversion:{id}` URN. For OpenAI Ads, the pixel wire id.
  - `events` ConversionEvent[], required
    - `eventName` string, required — Standard event name (Purchase, Lead, CompleteRegistration, AddToCart, InitiateCheckout, AddPaymentInfo, Subscribe, StartTrial, ViewContent, Search, Contact, SubmitApplication, Schedule) or a custom string (only supported on platforms that accept custom events — Meta and OpenAI Ads). Per-platform behavior: - Meta: free-form; standard names match Meta's built-ins. - Google: ignored — the conversion action's category determines the type. - LinkedIn: ignored — the conversion rule's `type` is locked to the destination. - OpenAI Ads: a fixed subset of standard names (Purchase, Lead, AddToCart, ViewContent, InitiateCheckout, CompleteRegistration, Subscribe, StartTrial, Schedule) maps 1:1 onto OpenAI's own event-type enum; anything else is sent as a custom event with the name preserved.
    - `eventTime` integer, required — When the conversion happened, in unix seconds.
    - `eventId` string, required — Unique dedup key. The same eventId must be used on pixel + CAPI to prevent double-counting. Mapped to event_id on Meta, transactionId on Google, eventId on LinkedIn (LinkedIn deduplicates against Insight Tag events with the same eventId; the Insight Tag event wins when both arrive).
    - `value` number — Conversion value in the specified currency.
    - `currency` string — ISO 4217 currency code.
    - `user` object, required — User identity fields. More signals mean higher match rates.
      - `email` string — Plaintext email. Hashed server-side.
      - `phone` string — Phone number, ideally E.164. Hashed server-side.
      - `firstName` string — Plaintext first name. Hashed server-side.
      - `lastName` string — Plaintext last name. Hashed server-side.
      - `externalId` string — Stable customer identifier (e.g. CRM user ID). Hashed server-side for Meta and Google. Sent as plaintext to LinkedIn (LinkedIn's Conversions API spec requires the raw value). Maximum effective list size on LinkedIn is 1.
      - `ipAddress` string — Client IP address. Sent plaintext.
      - `userAgent` string — Client user-agent string. Sent plaintext.
      - `country` string — ISO 3166-1 alpha-2 country code, e.g. 'us'.
      - `city` string — Meta advanced matching (ct). Plaintext city; normalized + SHA-256 hashed server-side. Meta only.
      - `state` string — Meta advanced matching (st). 2-letter ANSI for US; hashed server-side. Meta only.
      - `zip` string — Meta advanced matching (zp). US uses first 5 digits; hashed server-side. Meta only.
      - `dob` string — Meta advanced matching (db). YYYYMMDD; hashed server-side. Meta only.
      - `gender` string — Meta advanced matching (ge). 'f' or 'm'; hashed server-side. Meta only.
      - `leadId` string — Meta lead ID from a Lead Ad submission, as a string. Required for Conversion Leads CRM events: send it with `actionSource: 'crm'` and `platformData: { event_source: 'crm', lead_event_source: '<CRM name>' }`. Forwarded unhashed to Meta's `user_data.lead_id`. Meta only.
      - `clickIds` object — Platform click identifiers captured from the originating ad click.
        - `fbc` string — Meta click ID (from fbclid URL param).
        - `fbp` string — Meta browser ID (_fbp cookie).
        - `gclid` string — Google click ID (from gclid URL param).
        - `gbraid` string — Google iOS 14.5+ app attribution ID.
        - `wbraid` string — Google iOS 14.5+ web-to-app attribution ID.
        - `li_fat_id` string — LinkedIn first-party ad tracking click ID. Captured by parsing `li_fat_id` from landing-page URLs after the advertiser enables enhanced conversion tracking on the LinkedIn Insight Tag. Sent to LinkedIn as the LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID userId. Opaque token, not hashed.
    - `items` object[] — Item-level detail for ecommerce events.
      - `id` string
      - `name` string
      - `price` number
      - `quantity` integer
      - `category` string
    - `sourceUrl` string, uri — URL where the conversion originated (used by Meta).
    - `actionSource` 'web' | 'app' | 'offline' | 'crm' | 'phone_call' | 'system_generated' — Where the conversion happened. Used by Meta. Google also requires an event source internally; omitting this field sends OTHER to Google. Send an explicit value for accurate origin reporting.
    - `platformData` object — Escape hatch for platform-specific fields we haven't normalized. On Meta, keys are shallow-merged into `custom_data` only: fields Zernio already builds (`value`, `currency`, `contents`, `num_items`) always win on collision, and `user_data` (hashed match keys) is never touched. Use first-class fields (e.g. `user.leadId`) for anything that must reach `user_data`.
  - `testCode` string — Meta `test_event_code` passthrough. Ignored by Google, LinkedIn, and OpenAI Ads.
  - `consent` object — Batch-level user consent. Required by Google for EEA/UK events under the Feb 2026 restrictions. On Meta, any DENIED flag enables Limited Data Use on every event in the batch (data_processing_options ["LDU"] with geolocation, country 0 / state 0); GRANTED or absent consent sends events with Meta's default processing. Ignored by LinkedIn.
    - `adUserData` 'GRANTED' | 'DENIED'
    - `adPersonalization` 'GRANTED' | 'DENIED'

## Response `200`

Events processed. Inspect `eventsFailed` and `failures[]` to detect
partial failure. For Meta, a batch is all-or-nothing (either every
event in a chunk succeeds, or every event in the chunk is listed
in failures). For Google, the API returns success/failure at the
request level only. For OpenAI Ads, each 1000-event chunk is
all-or-nothing, same as Meta.

- object
  - `platform` 'metaads' | 'googleads' | 'linkedinads' | 'tiktokads' | 'openaiads'
  - `eventsReceived` integer — Events accepted by the platform.
  - `eventsFailed` integer — Events rejected (see failures).
  - `failures` object[]
    - `eventIndex` integer — Index into the submitted events array.
    - `eventId` string — Echoes back the eventId of the failed event.
    - `message` string
    - `code` union
      - string
      - integer
  - `traceId` string — Platform trace ID for debugging. fbtrace_id for Meta, requestId for Google. Absent for LinkedIn (LinkedIn's conversionEvents endpoint does not surface a trace ID) and OpenAI Ads (no trace ID surfaced).

## Other responses

- `400` — Invalid body (missing accountId/destinationId/events, malformed event shape).
- `401` — Unauthorized
- `403` — Ads access required (Ads add-on on legacy plans, included on usage-based plans), OR (for LinkedIn) the connected account lacks the `rw_conversions` scope and must be reconnected.
- `404` — Account not found or not accessible.
- `422` — OpenAI Ads only: no tracking tag (pixel) exists yet for this account. Code `TRACKING_TAG_REQUIRED` — create one via `POST /v1/accounts/{accountId}/tracking-tags` first.
- `429` — LinkedIn token-level rate limit hit (600 requests/min, 300k/day per token). Retry with backoff. Meta and Google have their own rate-limit semantics surfaced via platform-specific 4xx responses.

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/revisions/d0ab3b4eb0ae/schema)
