> ## Documentation Index
> Fetch the complete documentation index at: https://docs.datalyr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# X Ads

> Connect an X pixel and Conversion API token, and deliver conversions to X.

When you finish, DATALYR sends conversions to X's Conversion API and runs the X Pixel. When both send one conversion, X keeps one copy.

## Before you start

| Requirement | Detail |
| - | - |
| Pixel ID | **Tools → Events Manager** in X Ads Manager. A short code such as `o8z6j` |
| Conversion API access token | Events Manager, in your pixel's Conversion API setup. Select **Generate access token** |
| One X event per conversion | Each event's ID is under **Events Manager → the event → Install event → Conversion API**, as `tw-<pixel>-<id>` |
| Tracking script | On every page your X ads send traffic to. See [Install](/installation/index). With npm, use `@datalyr/web` 1.9.10 or later |
| A connected revenue or lead source | The **X events** step creates rules for the sources you've connected |

X's Conversion API takes website events only. DATALYR doesn't import X spend or ad stats.

<Warning>
  Treat the access token as a secret. It can send conversions to your pixel. Don't paste it into a screenshot or a public issue.
</Warning>

## Connect X

<Steps>
  <Step title="Open the form">
    Open **Sources**. Select **Connect** on the **X Ads** row.
  </Step>

  <Step title="Paste the credentials">
    Fill **Pixel ID** and **Conversion API access token**.
  </Step>

  <Step title="Save">
    Select **Connect**. The **X events** step opens.
  </Step>
</Steps>

DATALYR checks the token with a request X can't record as a conversion.

| Message on save | Meaning |
| - | - |
| `Token doesn't match this Pixel ID` | The token is wrong, or belongs to another pixel |
| `Pixel ID is not valid` | The Pixel ID isn't 1–32 letters and digits |
| `X refused this connection: …` | X rejected the request. Its reason follows |
| `Token not verified` | X didn't answer. The connection saved anyway |

## Add your X events

| Row in the step | Shown when a connected source sends |
| - | - |
| **Purchase Tracker** | Purchases |
| **Subscribe** | Paid subscriptions |
| **Start trial** | Trial starts |
| **Lead Generation Tracker** | Leads |
| **Add To Cart Tracker** | Add to cart |

<Steps>
  <Step title="Copy an Event ID">
    Select **Open X Events Manager**. Copy the event's ID.
  </Step>

  <Step title="Paste it">
    Paste the ID into the matching row. Leave a row blank to skip it.
  </Step>

  <Step title="Save">
    Select **Save**. DATALYR creates one rule per trigger, such as each Stripe payment event under **Purchase Tracker**.
  </Step>
</Steps>

To come back later, select **Manage** on **X Ads**, then **X events**.

| Message in the step | Meaning |
| - | - |
| `This Event ID belongs to pixel …, not …` | The ID is from another pixel |
| `Left as is: “…”` | Another X rule on that trigger has another event type, is paused, has conditions, or is limited to one origin. DATALYR doesn't change it |
| `<n> X rules saved` | The rules exist in **Conversions** |

Renewals are never created here. To build a rule by hand, pick **X** as **target platform** in **Conversions**. Paste the ID into **X Event ID**.

## Tag your ads

X adds its click ID, `twclid`, to every website click. Keep it on the landing URL through every redirect.

1. Open **Track**. Create a link.
2. Under **paste into your ads**, find **X Ads**. Select **copy parameters**.
3. In X Ads Manager, paste them into the ad's **Add URL tracking**.

```text theme={null}
utm_source=x&utm_medium=paid_social&utm_campaign={{campaign_name}}&utm_content={{adgroup_name}}&utm_term={{ad_name}}&campaign_id={{campaign_id}}&adset_id={{adgroup_id}}&ad_id={{ad_id}}
```

X publishes no macro reference. Check that each macro is in the variable list in X Ads Manager before you launch.

## Reference

### Events

| DATALYR trigger event | X event type |
| - | - |
| `purchase`, `checkout.session.completed`, `invoice.payment_succeeded` | `PURCHASE` |
| `subscription_started`, `trial_converted`, `subscribe` | `SUBSCRIBE` |
| `trial_started` | `START_TRIAL` |
| `signup`, `form_submit` | `SIGN_UP` |
| `checkout_started` | `CHECKOUT_INITIATED` |
| `add_to_cart` | `ADD_TO_CART` |
| `add_payment_info` | `ADDED_PAYMENT_INFO` |
| `view_item` | `CONTENT_VIEW` |
| `search` | `SEARCH` |

The X Event ID decides what X counts. The type only picks the value rules below.

| Field | Rule |
| - | - |
| `value` on a purchase | The rule's value, else a Stripe payment's amount paid. A \$0 purchase isn't sent |
| `value` on a trial | Only a fixed value or value path set on the rule |
| `price_currency` | The event's, else the rule's, else `USD` |
| `contents`, `number_items` | Line items, up to 100 |
| `event_source_url` | The page, keeping only `twclid` and `utm_*` |

### Matching fields in `identifiers`

| Field | Source | Normalization |
| - | - | - |
| `twclid` | The landing URL, the event, the visitor's earlier visits, then the `_twclid` cookie | not hashed |
| `twpid` | The X Pixel's `_twpid` cookie | not hashed |
| `hashed_email` | Email in the event | trim, lowercase, SHA-256 |
| `hashed_phone_number` | Phone in the event | E.164 with the leading `+`, SHA-256 |
| `ip_address` | The browser behind the event | public IPv4 only, not hashed |
| `user_agent` | The browser behind the event | not hashed |

X needs `twclid`, `hashed_email`, or `hashed_phone_number`. `twpid`, IP, and user agent only add to one of them.

### Deduplication

The pixel copy and the server copy carry the same Event ID and `conversion_id`: a purchase's order ID, else the DATALYR event ID. X keeps one when both arrive within 48 hours. Retries reuse the `conversion_id`.

If your own code fires X events, remove those calls. They carry other IDs, so X counts them again.

### The X Pixel

DATALYR loads X's pixel through your tracking script as soon as X is connected. It sends no email or phone.

| The pixel loads only when | Detail |
| - | - |
| The workspace has an active X connection | |
| The visitor gave marketing consent | Never before, and never with `privacyMode: 'strict'` |
| The browser sends no opt-out | No Do Not Track, Global Privacy Control, or US privacy opt-out, whatever your site settings say |
| No other X Pixel is on the page | Theme code, a tag manager, or a Shopify app |
| **Filter health-related fields** is off | **Settings → Privacy & redaction** |

| What the pixel sends | When |
| - | - |
| Page views | From the moment X is connected, before any rule |
| Events your website X rules cover | Once the rules exist |
| Purchases | Only with a value above 0 and no verified revenue source connected. Otherwise the server sends them |

| Also know | Detail |
| - | - |
| Automatic advanced matching | X's pixel reads emails and phone numbers typed into forms and hashes them. X documents no switch for it. Contact X Ads support to turn it off |

| Content Security Policy directive | Allow |
| - | - |
| `script-src` | `https://static.ads-twitter.com` |
| `img-src`, `connect-src` | `https://ads-twitter.com`, `https://ads-api.twitter.com`, `https://analytics.twitter.com`, `https://analytics.x.com`, `https://t.co` |

### Consent and age

| Event | Result |
| - | - |
| Marketing consent denied, or a sale opt-out | Not sent |
| Do Not Track, Global Privacy Control, or a `us_privacy` opt-out | Not sent |
| Older than 30 days | Not sent |
| From an app rule | Not sent |

### Why X shows different numbers

| X counts | Default window |
| - | - |
| Conversions after any engagement: a like, repost, follow, reply, or URL click | 30 days |
| Conversions after a view | 1 day |

DATALYR credits clicks. X also settles its reports 24–48 hours after the impression, so recent days move.

## Verify

1. Open **Conversions**. Select **manage rules**. Open an X rule.
2. Select **send test event**. X has no test mode, so DATALYR checks the event and sends nothing.
3. Trigger one real event.
4. Confirm the rule's `sent` count went up by 1.
5. In X Ads Manager, open **Tools → Events Manager**.
6. Open the event. Confirm X recorded your conversion.

## When it does not work

| Reason in **Health** | `error_type` | Fix |
| - | - | - |
| `X event ID missing` | `x_event_id_not_configured` | Open **X events**. Paste the Event ID |
| `X event ID not found in this pixel` | `x_event_id_invalid` | Copy the ID again from the same pixel's Events Manager |
| `X rejected a field` | `x_invalid_parameter` | Read the field named in the delivery row's response |
| `X accepted but didn't count it` | `x_conversion_not_processed` | Check the event's time and identifiers in the request payload |
| `No match key the platform accepts` | `no_strong_identifier` | Keep `twclid` on landing URLs. Send email or phone with the event |
| `Visitor declined tracking` | `consent_blocked` | Nothing. The visitor opted out |
| `Older than the platform's window` | `too_old` | Nothing. DATALYR stops at 30 days |
| `App events this platform cannot take` | `native_app_destination_unsupported` | Nothing. X takes website events only |
| `Zero-value purchase` | `zero_value_purchase` | Point the rule's value at the amount field |
| `x pixel invalid` | `x_pixel_invalid` | X rejected the Pixel ID. Select **Manage**. Select **Edit setup**. Paste the Pixel ID again |
| `Access token expired or revoked` | `AUTH_ERROR` | Generate a new token in X. Select **Manage**. Select **Update credentials**. Paste it |
| X counts a purchase twice | none | Remove your own X event calls for that event |

## Next

* [Conversion delivery](/advanced/conversion-delivery): retry, dedup, and status rules for every platform.
* [Health](/product/health): read the delivery and match-quality tabs.
* [Tracking links](/product/tracking-links): build the link your ads point to.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.