# Site pixels

Install Meta, TikTok, Google, LinkedIn, Pinterest, X and ChatGPT tags on a connected Shopify, WordPress, Wix or Webflow site with one call, then check and remove them.

Ad platforms measure conversions with a tag on the website. Pozy puts that tag on a connected store or site for you, with no theme edits and no copy-paste. You pick a tracking tag on a connected ads account, name the site, and Pozy installs it. On Shopify the tag also receives storefront events such as add to cart and purchase. On WordPress, Wix and Webflow it records page views.

## What you need

| Piece | Where it comes from |
| --- | --- |
| An ads account | A connected Meta, TikTok, Google, LinkedIn, Pinterest, X or ChatGPT Ads account. See [Ads](https://pozy.ai/docs/ads) |
| A tracking tag on it | `GET /v1/accounts/{accountId}/tracking-tags`, or create one. See [Pixels](https://pozy.ai/docs/ads/pixels) |
| A site | A connected `shopify`, `wordpress`, `wix` or `webflow` account, passed as `storeAccountId` |

## Install a tag

`accountId` in the path is the ads account. `tagId` is the tag's API id.

```bash cURL
curl -X POST https://api.pozy.ai/v1/accounts/6710c537e4b0d2f1a8c95ea0/tracking-tags/1729525464415281/install \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "storeAccountId": "6710c4b2e4b0d2f1a8c95e3a" }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/6710c537e4b0d2f1a8c95ea0/tracking-tags/1729525464415281/install", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ storeAccountId: "6710c4b2e4b0d2f1a8c95e3a" }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.install.installed, data.install.tags);
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/accounts/6710c537e4b0d2f1a8c95ea0/tracking-tags/1729525464415281/install",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={"storeAccountId": "6710c4b2e4b0d2f1a8c95e3a"},
)
print(resp.json()["install"])
```

```json
{
  "platform": "tiktokads",
  "install": {
    "storeAccountId": "6710c4b2e4b0d2f1a8c95e3a",
    "platform": "shopify",
    "shopDomain": "acme.myshopify.com",
    "tagPlatform": "tiktokads",
    "siteTagId": "D00IKHRC77UE0J0RTNHG",
    "installed": true,
    "installedTagId": "D00IKHRC77UE0J0RTNHG",
    "tags": [
      { "platform": "metaads", "siteTagId": "1729525464415281" },
      { "platform": "tiktokads", "siteTagId": "D00IKHRC77UE0J0RTNHG" }
    ],
    "replacedTagId": null
  }
}
```

`siteTagId`, `installedTagId`, `replacedTagId` and `tags` carry the id the site fires (the TikTok pixel code, the Google `AW-` id, the X or ChatGPT pixel id), not the API `tagId`.

## Check and remove

| Call | What it does |
| --- | --- |
| `GET .../tracking-tags/{tagId}/install?storeAccountId=...` | Whether this tag is the one the site fires, which tag of that platform it fires now (`installedTagId`), and every Pozy tag on the site (`tags`) |
| `DELETE .../tracking-tags/{tagId}/install?storeAccountId=...` | Removes this tag and keeps other platforms' tags |

Removing a tag that is not installed does nothing. On Shopify, Wix and Webflow, removing a tag while the site fires a different tag of the same platform changes nothing and returns `409 invalid_resource_state`: read the status and remove the tag named in `installedTagId`.

## How it works on each platform

### Shopify

Shopify allows an app only one web pixel in each store. Pozy's pixel therefore holds all your tags, one per ad platform. Repeating an install changes nothing. Installing another tag for a platform that already has one swaps it in, and `replacedTagId` tells you which tag it replaced.

The pixel listens to seven Shopify customer events and sends each to every installed platform:

| Shopify event | Site event | Meta, TikTok, Pinterest, ChatGPT | Google, LinkedIn, X |
| --- | --- | --- | --- |
| `page_viewed` | `page_view` | Always | Always (Google only with a page-view conversion action) |
| `product_viewed` | `view_content` | Always | LinkedIn and X when a matching conversion exists. Not sent to Google |
| `product_added_to_cart` | `add_to_cart` | Always | When a matching conversion exists |
| `search_submitted` | `search` | Always (ChatGPT as a custom `search` event, without the search text) | LinkedIn and X when a matching conversion exists. Not sent to Google |
| `checkout_started` | `initiate_checkout` | Always | When a matching conversion exists |
| `payment_info_submitted` | `add_payment_info` | Always (ChatGPT as a custom event) | LinkedIn and X when a matching conversion exists. Not sent to Google |
| `checkout_completed` | `purchase` | Always | When a matching conversion exists |

Product and cart events include the variant id, the value and the currency. Checkout events include each line item, the total and the currency, and purchases also include the order id. For Google, LinkedIn and X, Pozy saves the ids of their conversion events at install time, so reinstall whenever you add or edit conversion events there.

Each event has an event id, `shopify_order_<orderId>` in the case of a purchase. If you also report the purchase from your server with [`POST /v1/ads/conversions`](https://pozy.ai/docs/api/sendConversions), reuse that `eventId` and the platforms will count the order only once. The pixel respects the store's customer privacy settings: in places where consent is needed, it only fires for visitors who accepted marketing.

Stores connected through a custom-app token, or connected before they granted `write_pixels` and `read_customer_events`, get `409 reconnect_required`. `details.authUrl` holds a link for the merchant to approve, and `details.missingScopes` lists what is missing. The account id does not change, so just repeat the install once they approve.

### WordPress

Pozy places the platform's base code in a Custom HTML widget inside one of the active theme's widget areas, preferring a footer area and otherwise using the first area that is active; `sidebarId` lets you pick. It then reads the widget again to make sure WordPress did not strip the script. With `verifyHomepage`, which is on by default, it also loads the homepage and reports `homepageCheck` as `found`, `not_found`, `unreachable` or `skipped`. A page cache can cause `not_found`; trust the widget read-back.

Each tag gets its own widget, and several tags run side by side. Installing again updates the same widget (`created: false`). Code the owner pasted by hand is never touched. The status call also returns a `preflight` (`ready`, `reason`, available `sidebars`) to check a site before installing.

WordPress strips `<script>` tags unless the user may publish unfiltered HTML. If the tag cannot run on the site, Pozy undoes its change and answers `422 tracking_tag_install_blocked`, with the reason in `details.reason`:

| `details.reason` | Why |
| --- | --- |
| `insufficient_permissions` | The WordPress user you connected with is not an Administrator |
| `wordpress_com_plan` | The WordPress.com plan strips scripts |
| `scripts_stripped` | The site removed the script (no `unfiltered_html`, or `DISALLOW_UNFILTERED_HTML` is set) |
| `no_widget_areas` | A block theme with no widget areas |
| `widgets_api_unavailable` | WordPress older than 5.8, or a security plugin disabled the widgets API |

The error message always names a manual fallback: the platform's official WordPress plugin where one exists, or pasting the tag's `code` (from `GET .../tracking-tags/{tagId}`) into the theme header.

### Wix

The installed app adds one script to the site, registered with Wix as an advertising script, that loads every installed tag. It runs only for visitors who accept advertising cookies on sites with Wix's cookie banner. Page changes without a reload are counted again for Meta, TikTok, Google, Pinterest and ChatGPT; LinkedIn and X count the first page of a visit. One tag per platform, as on Shopify. Sites that installed the app before script permission was needed return `409 reconnect_required`.

### Webflow

Needs the OAuth connection (not a site token). Pozy adds the tag to the site's custom code, which Webflow publishes only with the site, so the install always returns `publishRequired: true`. The tag fires after the site's next publish. Pozy never publishes for you, because that would also push every other unpublished Designer change. One tag per platform. The status call reads the custom code, so it shows the install before it is live.

## What feeds where

| Data | Goes to | Use it for |
| --- | --- | --- |
| Page views (all sites) | Each installed ad platform | Retargeting audiences, landing page views |
| Storefront events (Shopify) | Each installed ad platform | Conversion campaigns, catalog ads, purchase optimisation |
| Server-side conversions | `POST /v1/ads/conversions` | Deduplicated purchases, offline sales |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/accounts/{accountId}/tracking-tags`](https://pozy.ai/docs/api/listTrackingTags) | List tracking tags |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}`](https://pozy.ai/docs/api/getTrackingTag) | Get a tracking tag |
| [`POST /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/installTrackingTagOnStore) | Install on a Shopify, WordPress, Wix or Webflow site |
| [`GET /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/getTrackingTagStoreInstall) | Get store install status |
| [`DELETE /v1/accounts/{accountId}/tracking-tags/{tagId}/install`](https://pozy.ai/docs/api/removeTrackingTagFromStore) | Remove from a Shopify, WordPress, Wix or Webflow site |
| [`POST /v1/ads/conversions`](https://pozy.ai/docs/api/sendConversions) | Send conversion events |
