pixellint

Blog · X ads

X Conversion API event_id is the Ads Manager UUID. conversion_id is the dedup key.

On X, event_id is not your order id. It is the conversion event created in Ads Manager. Dedup with the pixel uses conversion_id. OAuth 1.0a. Need at least twclid, hashed email, or hashed phone. IP plus UA alone is not enough.

Every other CAPI you already shipped taught the opposite habit. Meta event_id is the dedup key you generate per firing. OpenAI id is the same idea. X puts that job on conversion_id. event_id is the static Event ID Ads Manager shows when you create the conversion under the pixel. Paste your order number into event_id and you have not created a Purchase. You have posted an unknown event type to a pixel that still 200s.

developer.x.com still 403s or 404s for a lot of crawlers. The working official writeup is the X Ads API web conversions page (the docs.x.com Ads API set). It is not Graph. It is not a Bearer token. The first worker that copies Meta user_data onto ads-api.x.com will fail auth, matching, and dedup in three different ways and still look like a networking ticket.

Create the event in Ads Manager, then POST it

The Conversion API needs Ads API access: a developer account, an app approved for Ads API, and user tokens. Tokens used with Conversion API must be for users with AD_MANAGER or ACCOUNT_ADMIN, which you can check via authenticated_user_access. Existing Ads API apps and tokens can be reused. The tokens can be handed to operators who do not themselves hold those roles.

Create an Event Source in Events Manager if you do not already have an X Pixel. That pixel's ID is the pixel_id on the path. Inside that source, Add events, Install with Conversion API. Ads Manager then shows the Pixel ID and the Event ID. The ID of the event is your Event ID. If you want pixel plus API dedup, reuse the existing pixel event rather than creating a second Purchase.

The request is POST /{version}/measurement/conversions/:pixel_id on ads-api.x.com. Current examples use version 12. The body is conversions[], each with conversion_time as an ISO 8601 timestamp, event_id set to that Ads Manager Event ID, identifiers[], and conversion_id when you are deduping. Check for HTTP 200. The example response reports conversions_processed and a debug_id. A retry loop belongs in your worker. A Meta-style test_event_code does not.

conversion_id is the join with the pixel

Official setup language: if you use both pixel and Conversion API for the same event, use the deduplication key in the pixel snippet and on the Conversion API request as conversion_id. Tealium's connector guide repeats that and notes conversion_id is an identifier for a conversion event used to de-duplicate Web Pixel and Conversion API conversions in the same event tag. Your order id, UUID, or event_id from the data layer belongs there.

event_id stays the Ads Manager value. Tealium describes it as the base36 ID of a specific pre-configured event, matching the ID in Events Manager. Official examples look like ol288, not ORDER-8891. Swapping the two fields is the silent double-count or the silent drop. Meta-trained sGTM variables named Event ID will land in the wrong JSON key unless you rename them.

Do not take secondary blogs' 24-hour windows as X's contract unless X writes it. The primary instruction is: same event in Ads Manager, same conversion_id on both pipes. Page-view style events have extra connector notes in partner docs. Stick to the Ads API sentence for Purchase until you have a primary in front of you.

Identifiers, hashing, and the OAuth header

X currently needs at least one of Click ID (twclid), email address, or phone number. If you send IP address or user agent, a second identifier must be sent. IP plus UA by themselves are not enough. Passing more identifiers raises match rate. twclid is parsed from the twclid query parameter after the click. Do not hash it. Store it with the lead or the order so batch jobs still have it.

Email: trim leading and trailing spaces, then SHA-256 without salt. The docs hash test@x.com to d360d510a224510f373931ce2d6215a799f5a9c1cef221b0149b6b6b50cced62. Phone: E.164, then SHA-256 without salt. +11234567890 hashes to 1fa6b8d986d9b9cd01bf36951815158bbde9f520c0567c835dfe34783d0a4231. IP and user agent stay plaintext, spaces trimmed. Hashing the IP because a Meta helper hashed em is how the event becomes unmatchable.

All Ads API calls need an OAuth 1.0a Authorization header over HTTPS to ads-api.x.com. You generate a new header per request. X tells partners to use a known OAuth library; they will not debug a custom signer. Twurl is the CLI they maintain for exploration. A Graph Bearer token on this host is a 401 that looks like a expired secret. It is the wrong protocol.

The documented rate limit is 60,000 events per account per 15 minute interval. Your server still has to log the user action, filter opted-out users, and attach identifiers. Optional commerce fields in the example are value, number_items, description, and contents[] with content_id, content_name, content_type, content_price, num_items, content_group_id. None of those replace event_id or a match key.

The body a Meta worker will emit

Order id in event_id, Ads Manager Event ID in conversion_id, IP plus UA only. Matching and dedup are both inverted.

{
  "conversions": [{
    "conversion_time": "2026-03-18T16:00:00.000Z",
    "event_id": "order_8891",
    "identifiers": [{
      "ip_address": "203.0.113.9",
      "user_agent": "Mozilla/5.0"
    }],
    "conversion_id": "ol288"
  }]
}

Posted to /12/measurement/conversions/{pixel_id} that object can still 200 if the OAuth header is valid. conversions_processed is not Ads Manager attribution. event_id does not name a conversion event the account knows. conversion_id is not doing pixel dedup because the pixel snippet was not given order_8891. identifiers has no twclid, no hashed_email, and no hashed_phone_number.

The documented shape puts the Ads Manager Event ID in event_id, your per-firing key in conversion_id, and at least one of twclid, hashed_email, or hashed_phone_number in identifiers. IP and user_agent may ride along with one of those, not instead of them.

Names that do not translate

A shared CAPI JSON cannot serve X

The click id is twclid, stored from the landing query string. It is not fbclid, ttclid, or gclid. If checkout is a different host, copy twclid through the redirect the same way you copy every other click id. The API will not read the landing URL for you.

Do not point a Meta conversions[] mapper at ads-api.x.com by rewriting the host. The array name happens to match some other vendors. The keys inside do not. conversion_time, event_id, identifiers, conversion_id, hashed_email: those are the dialect. user_data.em and event_name Purchase are a different language.

Partner connectors that expose Event ID Override and Conversion ID as two settings are documenting the same split. Fill them from different columns. If you only have one internal event_id column today, add x_event_id for the Ads Manager value and keep your own id for conversion_id.

We currently need at least one identifier to be passed such as Click ID (twclid), email address, or phone number. If using IP address or user agent, a second identifier must be sent for proper conversion matching.

X Ads API web conversions

What to do

Create or reuse the conversion event in Events Manager. Put that Event ID in every CAPI row's event_id. Generate a per-order conversion_id and send the same value on the pixel. Capture twclid. Hash email and E.164 phone with SHA-256. Sign every request with OAuth 1.0a. Stay under 60,000 events per account per 15 minutes.

Keep one internal event. At the edge, emit the X envelope. Persist x_event_id separately from the Meta event_id column. Do not send IP and UA as the only identifiers. Filter users who opted out before the POST.

The X pack is vendor/x-conversions-api. The Meta pack is vendor/meta-conversions-api. One fixture cannot serve both. Pixellint is not affiliated with X. Passing a linter means the artifact matches the published envelope. It does not mean Ads Manager attributed the conversion, and it does not detect fraud.

Checklist

Sources

Contract pages

The dated argument is above. These pages are the field lists.

Open the X CAPI pack Docs