# Platforms

What each ad platform gets from your events: the destination to pick, which identifiers and click ids are sent and how, event names, test mode and limits.

import { PlatformMark } from '@/components/platforms/platform-mark';

An event goes out once per destination, in that platform's own format. These tables show what each platform reads from it, so you know which fields matter where.

## Destinations

A destination is where a platform records conversions. List an account's destinations with `GET /v1/accounts/{accountId}/conversion-destinations`, then use its `id` as `destinationId`.

| Platform | Account | Destination |
|---|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | `metaads` | A pixel (dataset) |
| <PlatformName slug="google-ads" page="conversions" /> | `googleads` | An enabled conversion action, `customers/{id}/conversionActions/{id}` |
| <PlatformName slug="linkedin-ads" page="conversions" /> | `linkedinads` | A conversion rule set to Conversions API |
| <PlatformName slug="tiktok-ads" page="conversions" /> | `tiktokads` | An offline event set |
| <PlatformName slug="pinterest-ads" page="conversions" /> | `pinterestads` | The ad account |
| <PlatformName slug="reddit-ads" page="conversions" /> | `redditads` | A pixel, `a2_…` |
| <PlatformName slug="openai-ads" page="conversions" /> | `openaiads` | A pixel, once a tracking tag exists |

## Customer identifiers

Plain values you send are hashed with SHA-256 before they leave Zernio. "Plain" below means the platform requires the raw value.

| Platform | Email | Phone | Name | `externalId` | IP and user agent | Location |
|---|---|---|---|---|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | Hashed | Hashed | Hashed | Hashed | Plain | Hashed city, state, zip, country |
| <PlatformName slug="google-ads" page="conversions" /> | Hashed | Hashed | Hashed, with country and zip | Not sent | Plain | Plain country and zip, with the name |
| <PlatformName slug="linkedin-ads" page="conversions" /> | Hashed | Not sent | Plain, both names needed | Plain | Not sent | Country |
| <PlatformName slug="tiktok-ads" page="conversions" /> | Hashed | Hashed | Not sent | Not sent | Not sent | Not sent |
| <PlatformName slug="pinterest-ads" page="conversions" /> | Hashed | Hashed | Hashed | Hashed | Plain | Hashed city, state, zip, country |
| <PlatformName slug="reddit-ads" page="conversions" /> | Hashed | Hashed | Not sent | Hashed | Plain | Not sent |
| <PlatformName slug="openai-ads" page="conversions" /> | Hashed | Not sent | Not sent | Hashed | Plain | Plain city, region, postal code, country |

Google normalizes Gmail addresses before hashing (dots and `+suffix` removed). Reddit does it for every address.

LinkedIn and TikTok need an email or phone on each event, and Pinterest needs an email or the IP and user agent.

## Click ids

| Platform | Reads |
|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | `fbc`, `fbp` |
| <PlatformName slug="google-ads" page="conversions" /> | `gclid`, `gbraid`, `wbraid` |
| <PlatformName slug="linkedin-ads" page="conversions" /> | `li_fat_id` |
| <PlatformName slug="pinterest-ads" page="conversions" /> | `epik` |
| <PlatformName slug="reddit-ads" page="conversions" /> | `rdt_cid`, `rdt_uuid` |
| <PlatformName slug="tiktok-ads" page="conversions" /> | None yet |
| <PlatformName slug="openai-ads" page="conversions" /> | None yet |

## Event names, value and dedup

| Platform | `eventName` | Value | Dedup on |
|---|---|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | Sent as is | `value`, `currency`, `items` | `eventId` as `event_id` |
| <PlatformName slug="google-ads" page="conversions" /> | Ignored: the conversion action sets the type | `value`, `currency` | `eventId` as `transactionId` |
| <PlatformName slug="linkedin-ads" page="conversions" /> | Ignored: the conversion rule sets the type | Sent only above 0 and with `currency` | `eventId` |
| <PlatformName slug="tiktok-ads" page="conversions" /> | Sent as is | `value`, `currency`, `items` | `eventId` as `event_id` |
| <PlatformName slug="pinterest-ads" page="conversions" /> | Standard names mapped (`Purchase` to `checkout`, `Lead` to `lead`), others as is | `value`, `currency`, `items` | `eventId` as `event_id` |
| <PlatformName slug="reddit-ads" page="conversions" /> | Standard names mapped, others sent as a custom event | `value`, `currency`, `items` | `eventId` as `conversion_id` |
| <PlatformName slug="openai-ads" page="conversions" /> | Standard names mapped, others sent as a custom event | Sent only with `currency` | `eventId` |

Send the same `eventId` your pixel or tag sends for the event. The platform then counts the conversion once.

## Test mode and consent

| Platform | `testCode` | `consent` |
|---|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | Test Events tool (`test_event_code`) | Denied turns on Limited Data Use |
| <PlatformName slug="google-ads" page="conversions" /> | No test mode: the event is recorded | Sent as given |
| <PlatformName slug="linkedin-ads" page="conversions" /> | No test mode: the event is recorded | Not sent |
| <PlatformName slug="tiktok-ads" page="conversions" /> | No test mode: the event is recorded | Not sent |
| <PlatformName slug="pinterest-ads" page="conversions" /> | Validated, not recorded | Denied ad user data sends `opt_out` |
| <PlatformName slug="reddit-ads" page="conversions" /> | Test events (`test_id`) | Denied ad user data drops the user fields |
| <PlatformName slug="openai-ads" page="conversions" /> | No test mode: the event is recorded | Not sent |

Without a test mode, a test request reaches the live account. Test against Meta, Pinterest or Reddit, or a destination you do not bid on.

## Corrections

`PATCH` and `DELETE /v1/events/{eventId}` change an event you already sent. Zernio's count always changes; each platform's depends on its API.

| Platform | Restate a value or retract |
|---|---|
| <PlatformName slug="google-ads" page="conversions" /> | Yes: Google changes its count |
| <PlatformName slug="meta-ads" page="conversions" /> | No: Meta keeps its count |
| <PlatformName slug="linkedin-ads" page="conversions" /> | No: LinkedIn keeps its count |
| <PlatformName slug="tiktok-ads" page="conversions" /> | No: TikTok keeps its count |
| <PlatformName slug="pinterest-ads" page="conversions" /> | No: Pinterest keeps its count |
| <PlatformName slug="reddit-ads" page="conversions" /> | No: Reddit keeps its count |
| <PlatformName slug="openai-ads" page="conversions" /> | No: OpenAI keeps its count |

## Limits

| Platform | Events per platform call | Note |
|---|---|---|
| <PlatformName slug="meta-ads" page="conversions" /> | 1,000 | |
| <PlatformName slug="google-ads" page="conversions" /> | 2,000 | Retries a busy or failing API up to 3 times |
| <PlatformName slug="linkedin-ads" page="conversions" /> | 5,000 | |
| <PlatformName slug="tiktok-ads" page="conversions" /> | 1,000 | |
| <PlatformName slug="pinterest-ads" page="conversions" /> | 1,000 | |
| <PlatformName slug="reddit-ads" page="conversions" /> | 1,000 | Refuses events older than 7 days |
| <PlatformName slug="openai-ads" page="conversions" /> | 1,000 | |

A request of up to 1,000 events always fits one call on every platform.

## What each platform gets

You always call Zernio, and Zernio sends each platform only what it can use.

For each platform: what you must send, which of your fields are sent and how, and which are ignored. Each section links to that platform's setup guide.

## <PlatformMark slug="meta-ads" />Meta Ads

Setup, permissions and errors are in the [Meta Ads conversions guide](/platforms/meta-ads/conversions).

**You must send:** `eventName`, `eventTime`, `eventId` and at least one customer identifier. Website events need `sourceUrl`, `ipAddress` and `userAgent` too.

| Your field | How it is sent |
|---|---|
| `eventName` | As you name it: standard and custom names both work |
| `email`, `phone`, `firstName`, `lastName`, `country`, `externalId` | Hashed |
| `city`, `state`, `zip`, `dob`, `gender` | Hashed |
| `ipAddress`, `userAgent`, `fbc`, `fbp`, `leadId` | As is |
| `sourceUrl`, `value`, `currency` | As is |
| `items` | Ids, prices and quantities; names are dropped |

**Ignored:** `gclid`, `li_fat_id`, `epik`, `rdt_cid`.

**Dedup:** Meta keeps one copy when your pixel sends the same `eventId`. **Test mode:** `testCode` shows the events in Meta's Test Events tool.

With `consent` denied, Meta processes the event with Limited Data Use.

## <PlatformMark slug="google-ads" />Google Ads

Setup, permissions and errors are in the [Google Ads conversions guide](/platforms/google-ads/conversions).

**You must send:** A conversion action as the destination, and an `email`, a `phone` or a Google click id (`gclid`, `gbraid`, `wbraid`).

| Your field | How it is sent |
|---|---|
| `email` | Hashed. Gmail addresses lose dots and `+suffix` first |
| `phone` | Hashed |
| `firstName`, `lastName`, `country`, `zip` | One address: names hashed, country and zip as is. Sent only when all four are present |
| `ipAddress`, `userAgent` | As is |
| `gclid`, `gbraid`, `wbraid` | As is |
| `value`, `currency` | As is |
| `eventId` | As the order id |

**Ignored:** `eventName` (the conversion action sets what is counted), `externalId`, `sourceUrl`, `items` and the other click ids.

**Dedup:** Google keeps one conversion per `eventId` and conversion action. **Test mode:** none. A test event reaches the live account.

`consent` is passed on to Google as given.

## <PlatformMark slug="linkedin-ads" />LinkedIn Ads

Setup, permissions and errors are in the [LinkedIn Ads conversions guide](/platforms/linkedin-ads/conversions).

**You must send:** A conversion rule as the destination, and an `email` or a `li_fat_id`.

| Your field | How it is sent |
|---|---|
| `email` | Hashed |
| `li_fat_id` | As is |
| `firstName`, `lastName` | As is, lowercased, only when both are present |
| `country` | As is |
| `externalId` | As is: LinkedIn requires the raw value |
| `value`, `currency` | Only when the value is above 0 and has a currency |

**Ignored:** `eventName` (the conversion rule sets what is counted), `phone`, `ipAddress`, `userAgent`, `sourceUrl`, `items` and the other click ids.

**Dedup:** LinkedIn keeps the Insight Tag's copy when it sends the same `eventId`. **Test mode:** none. A test event reaches the live account.

## <PlatformMark slug="tiktok-ads" />TikTok Ads

Setup, permissions and errors are in the [TikTok Ads conversions guide](/platforms/tiktok-ads/conversions).

**You must send:** An offline event set as the destination, and an `email` or a `phone`.

| Your field | How it is sent |
|---|---|
| `eventName` | As you name it |
| `email`, `phone` | Hashed |
| `value`, `currency`, `items` | As is |

**Ignored:** `firstName`, `lastName`, `country`, `externalId`, `ipAddress`, `userAgent`, `sourceUrl` and every click id.

**Dedup:** TikTok keeps one copy per `eventId`. **Test mode:** none. A test event reaches the live account.

## <PlatformMark slug="pinterest-ads" />Pinterest Ads

Setup, permissions and errors are in the [Pinterest Ads conversions guide](/platforms/pinterest-ads/conversions).

**You must send:** The ad account as the destination, and an `email`, or an `ipAddress` with a `userAgent`.

| Your field | How it is sent |
|---|---|
| `eventName` | Standard names become Pinterest's (`Purchase` is `checkout`, `Lead` is `lead`); other names as you name them |
| `email`, `phone`, `firstName`, `lastName`, `country`, `externalId` | Hashed |
| `ipAddress`, `userAgent`, `epik` | As is |
| `sourceUrl`, `value`, `currency`, `items` | As is |

**Ignored:** The other click ids.

**Dedup:** Pinterest keeps one copy when its tag sends the same `eventId`. **Test mode:** `testCode` makes Pinterest check the events without recording them.

With `consent` denied, the event is marked as opted out. An `app` event needs `platformData.action_source`.

## <PlatformMark slug="reddit-ads" />Reddit Ads

Setup, permissions and errors are in the [Reddit Ads conversions guide](/platforms/reddit-ads/conversions).

**You must send:** A pixel as the destination, and an event from the last 7 days.

| Your field | How it is sent |
|---|---|
| `eventName` | Standard names become Reddit's; other names a custom event |
| `email` | Hashed, after dots and `+suffix` are removed |
| `phone`, `externalId` | Hashed |
| `ipAddress`, `userAgent`, `rdt_cid`, `rdt_uuid` | As is |
| `sourceUrl`, `value`, `currency` | As is |
| `items` | Ids and names; prices are dropped |

**Ignored:** `firstName`, `lastName`, `country` and the other click ids.

**Dedup:** Reddit keeps one copy when its pixel sends the same `eventId`. **Test mode:** `testCode` shows the events as Reddit test events.

With `consent` denied, the customer identifiers are left out.

## <PlatformMark slug="openai-ads" />OpenAI Ads

Setup, permissions and errors are in the [OpenAI Ads conversions guide](/platforms/openai-ads/conversions).

**You must send:** A pixel as the destination, on an account that has a tracking tag.

| Your field | How it is sent |
|---|---|
| `eventName` | Standard names become OpenAI's; other names a custom event |
| `email`, `externalId` | Hashed |
| `country`, `state`, `city`, `zip` | As is |
| `ipAddress`, `userAgent`, `sourceUrl` | As is |
| `value`, `currency` | As is, only together |
| `items` | Ids, names and quantities; prices are dropped |

**Ignored:** `phone`, `firstName`, `lastName` and every click id.

**Dedup:** OpenAI keeps one copy per `eventId`. **Test mode:** none. A test event reaches the live account.

## How it behaves

- **A refused event.** The platform's reason comes back in that destination's `failures`, by event.

## Related

- [Conversions API overview](/products/conversions-api)
- [Send events](/products/conversions-api/send)
- [List conversion destinations](/conversions/list-conversion-destinations)