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

# Send conversion events

`POST /v1/messaging/ads/conversions`

## Request

- Base URL: `https://apihub.co.il`
- URL: `https://apihub.co.il/v1/messaging/ads/conversions`
- Auth: HTTP bearer

Sends conversion events, such as purchases or leads, from your server to an ad platform so it can attribute and optimize ads.

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`)
- Pinterest (`pinterestads`) via `POST /v5/ad_accounts/{id}/events`
- Reddit (`redditads`) via the Reddit Conversions API (`POST /api/v3/pixels/{pixelId}/conversion_events`)

`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`
- Pinterest: numeric ad account id, e.g. `549755885175` (Pinterest attributes to the ad account, not to a tag)
- Reddit: pixel id, e.g. `a2_abc123`, as returned by `GET /v1/accounts/{accountId}/conversion-destinations` (any other shape returns 400)

Pinterest notes: each event needs an `email`, or `ipAddress` plus `userAgent`; others are
listed in `failures` with code `INVALID_EVENT` and the rest are still sent. `clickIds.epik`
(the `_epik` cookie) is sent as `click_id`. `actionSource` `web` stays `web`, offline
sources become `offline`, and app events need `platformData.action_source` `app_android`
or `app_ios`. Pinterest answers per event, so one bad event does not fail its chunk
(failures carry code `PINTEREST_EVENT_FAILED`). `consent.adUserData: DENIED` sets `opt_out`.
The connected user needs a Business Access role on the ad account (403 otherwise).

Reddit notes: each event needs a match key: `clickIds.rdt_cid`, `clickIds.rdt_uuid`,
`email`, `phone`, `externalId`, or `ipAddress` plus `userAgent`. Reddit accepts events up to
7 days old, and `eventTime` is unix SECONDS (a value more than an hour in the future is
refused as milliseconds). Events failing these checks are listed in `failures` with code
`INVALID_EVENT` and the rest are still sent. Email (lowercased, dots and `+tags` stripped
from the local part), phone (E.164 with the leading `+`) and `externalId` are SHA-256
hashed; IP and user agent go plaintext. `consent.adUserData: DENIED` sends no user fields.
`actionSource` maps to WEBSITE (`web`), APP (`app`) or OTHER, overridable with
`platformData.action_source`; `platformData.order_id` is forwarded. `testCode` is sent as
Reddit's `test_id`. The connection must hold the `adsconversions` permission: one
authorized without it returns 403 `reconnect_required` (reconnect via
GET /v1/connect/redditads/ads), and a Reddit user with no role on the ad account that owns
the pixel returns 403 `insufficient_permissions`. Reddit throttling and other rejections are
reported per chunk in `failures` with code `REDDIT_REJECTED`.

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). Reddit works the same way, in chunks of 1000.

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`; Reddit receives it as
`conversion_id`, its dedup key against Reddit Pixel events.

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.
- Pinterest: standard names map onto Pinterest's (Purchase = `checkout`, AddToCart = `add_to_cart`, InitiateCheckout = `initiate_checkout`, AddPaymentInfo = `add_payment_info`, Lead = `lead`, CompleteRegistration = `signup`, Subscribe = `subscribe`, ViewContent = `view_content`, Search = `search`, PageView = `page_visit`); any other string is sent as is, so it matches an advertiser defined event of that name.


**Billing:** Request; paid actions are priced in credits (see GET /v1/messaging/prices) · **Rate limit:** 60/min

## Headers

- `Idempotency-Key` string
- `X-Tag` string

## Request body

- object
  - `events` object[], required
    - `user` object, required: User identity fields. More signals mean higher match rates.
      - `dob` string: Meta advanced matching (db). YYYYMMDD; hashed server-side. Meta only.
      - `zip` string: Meta advanced matching (zp). US uses first 5 digits; hashed server-side. Meta only.
      - `city` string: Meta advanced matching (ct). Plaintext city; normalized + SHA-256 hashed server-side. Meta only.
      - `email` string: Plaintext email. Hashed server-side.
      - `phone` string: Phone number, ideally E.164. Hashed server-side.
      - `state` string: Meta advanced matching (st). 2-letter ANSI for US; 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.
      - `country` string: ISO 3166-1 alpha-2 country code, e.g. 'us'.
      - `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).
        - `epik` string: Pinterest click ID (the `epik` URL param or `_epik` cookie). Sent to Pinterest as `click_id`, not hashed.
        - `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.
        - `rdt_cid` string: Reddit click ID (the `rdt_cid` URL param Reddit adds to ad clicks). Sent to Reddit as `click_id`, not hashed.
        - `rdt_uuid` string: Reddit Pixel browser id (the `_rdt_uuid` first-party cookie). Sent to Reddit as `user.uuid`, not hashed.
        - `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.
      - `lastName` string: Plaintext last name. Hashed server-side.
      - `firstName` string: Plaintext first name. Hashed server-side.
      - `ipAddress` string: Client IP address. Sent plaintext.
      - `userAgent` string: Client user-agent string. Sent plaintext.
      - `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.
    - `items` object[]: Item-level detail for ecommerce events.
      - `id` string
      - `name` string
      - `price` number
      - `category` string
      - `quantity` integer
    - `value` number: Conversion value in the specified currency.
    - `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), and `metadata.conversion_id` on Reddit (Reddit's dedup key against Reddit Pixel events).
    - `currency` string: ISO 4217 currency code.
    - `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, OpenAI Ads and Reddit). 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. - Reddit: Purchase, Lead, CompleteRegistration (SIGN_UP), AddToCart, AddToWishlist, ViewContent, Search and PageView (PAGE_VISIT) map onto Reddit's tracking types; anything else is sent as a CUSTOM event with the name preserved.
    - `eventTime` integer, required: When the conversion happened, in unix seconds.
    - `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`.
  - `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. On Reddit, `adUserData: DENIED` drops every user field from the events. Ignored by LinkedIn.
    - `adUserData` 'GRANTED' | 'DENIED'
    - `adPersonalization` 'GRANTED' | 'DENIED'
  - `testCode` string: Meta `test_event_code` passthrough. On Pinterest any value sends the batch with `test=true` (validated, not recorded). On Reddit, sent as `test_id` so the events show in the pixel's test view. Ignored by Google, LinkedIn, and OpenAI Ads.
  - `accountId` string, required: SocialAccount ID (metaads, googleads, linkedinads, tiktokads, openaiads, pinterestads, or redditads).
  - `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. For Pinterest, the numeric ad account id. For Reddit, the pixel id (`a2_...`).

## Response `200`

Completed

- object
  - `id` string: Execution id — stable across internal retries; use it with GET /v1/executions/{id}
  - `status` 'pending' | 'processing' | 'completed' | 'partial' | 'failed' | 'cancelled' | 'timeout'
  - `operation` string
  - `tag` string, nullable: Your X-Tag, if sent
  - `created_at` string, date-time
  - `completed_at` string, date-time, nullable
  - `output` object: Operation result (see each operation). While a Studio render is processing: `stage` (queued · transcribing · downloading · preparing · cleaning_voice · mixing_audio · rendering · finishing), `phase` (the engine's: download · prepare · voice · audio · encode · qa · upload, or null), `progress` (a percent, 0–100) and `progress_fraction` (0–1), `eta_seconds` (seconds left, never below 0) and `estimate_seconds`
    - `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), OpenAI Ads and Reddit (no trace ID surfaced).
    - `failures` object[]
      - `code` union
        - string
        - integer
      - `eventId` string: Echoes back the eventId of the failed event.
      - `message` string
      - `eventIndex` integer: Index into the submitted events array.
    - `platform` 'metaads' | 'googleads' | 'linkedinads' | 'tiktokads' | 'openaiads' | 'pinterestads' | 'redditads'
    - `eventsFailed` integer: Events rejected (see failures).
    - `eventsReceived` integer: Events accepted by the platform.
  - `error` string, nullable
  - `usage` object
    - `credits_charged` number, nullable
    - `credits_reserved` number
    - `estimated_credits` number
    - `cost_source` 'provider_reported' | 'calculated' | 'estimated' | 'null', nullable
    - `provider_usage` object, nullable: Normalised consumption, e.g. { VIDEO_SECOND: 22.4 }
  - `balance` object
    - `available` number
    - `reserved` number

## Other responses

- `400`: Invalid input
- `402`: Insufficient credits / project budget
- `404`: Resource not found (or not yours)
- `429`: Rate limited
- `503`: Service temporarily paused

---

[API](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai.md) · [All operations](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/llms.txt) · [OpenAPI document](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/revisions/a8caa42835ce?raw)
