pixellint

Blog · LinkedIn and Meta

LinkedIn CAPI vs Meta CAPI wants conversionHappenedAt in milliseconds, inside 90 days.

LinkedIn CAPI vs Meta CAPI is a clock that is right for Graph and illegal for LinkedIn. Meta event_time is Unix seconds, ten digits, and an event older than about seven days fails the whole batch. LinkedIn posts conversion events to api.linkedin.com/rest/conversionEvents. conversionHappenedAt is required and is exactly thirteen digits, milliseconds. A ten-digit seconds value is vendor.linkedin-conversions-api.body.conversionHappenedAt.invalid. It reads as 1970, which is older than LinkedIn's 90-day window, and the API returns INVALID_CONVERSION_TIME_FIELD_VALUE.

If the source of truth is already seconds, append 000. Date.now() is already milliseconds, so the LinkedIn edge should send it through. The Meta edge should floor it. Dividing first because the Meta snippet divided, then posting that integer to LinkedIn, is the bug. Do not store one number and hope the vendor infers the unit.

The browser pipe is the Insight Tag, an image on px.ads.linkedin.com/collect. pid is the partner id. Fast leads can fire that image on the thank-you page. Slow CRM wins upload later with conversionHappenedAt at the CRM time, in milliseconds. Those are two pipes. They are not a Meta pixel plus a Graph event with a shared event_id field name.

The key is a conversion URN, not event_id

LinkedIn requires conversion in the form urn:lla:llaPartnerConversion:ID. You create the rule with conversionMethod CONVERSIONS_API and associate it with campaigns before reporting moves. A Meta event_name of Purchase does not select that rule. A Meta event_id does not either. The Insight Tag conversion id and the CAPI URN have to refer to the same rule.

Reuse the Insight Tag event id as eventId when both pipes fire. amount on conversionValue is a decimal string, not a JSON number copied from Meta custom_data.value. user.userIds carries SHA256_EMAIL and li_fat_id when you have them. li_fat_id is the LinkedIn click id from the landing URL. fbclid and gclid do not fill it.

IPv4 only for IP identifier types. A hashed IP is the same miss as on Meta: the field that should be plaintext becomes a digest nobody stored. Do not hash li_fat_id. Click ids stay in the clear on every vendor that issued them.

Seven days on Meta, ninety days on LinkedIn

A closed-won that lands twelve days after the click can still be inside LinkedIn's 90-day conversionHappenedAt window and outside Meta's seven-day event_time window. One stale Meta event fails the Graph batch, so the other events in that batch miss too. LinkedIn will still reject the ten-digit form of a timestamp that would have been in range if you had sent milliseconds.

Do not rewrite the CRM time to now so the Meta batch accepts it. That attributes the deal to the upload hour. Serialize the original instant in each vendor's unit, and drop the Meta copy when it is older than seven days rather than moving the clock. The LinkedIn copy can keep the original milliseconds for the 90 days LinkedIn documents.

Lead gen plus a CRM upload is a third path on LinkedIn. It is not Meta's offline action_source with a Purchase body. The image can fire while the lead is still a form fill. The CAPI row is the later conversion, with its own URN and its own time.

Seconds in a millisecond field

Appending 000 is legal only when the source integer was seconds. Date.now() is already thirteen digits.

meta:     event_time 1759010400
linkedin: conversionHappenedAt 1759010400000
urn:      urn:lla:llaPartnerConversion:123456

Treat INVALID_CONVERSION_TIME_FIELD_VALUE as a unit bug before you treat it as a missing email. The address can be a correct SHA-256 and the row still dies on the clock. Meta's equivalent is a 13-digit event_time, which is the future, not 1970. The same helper cannot be right on both.

Pixellint is not affiliated with LinkedIn or Meta. Passing the LinkedIn pack means the artifact has a thirteen-digit conversionHappenedAt and a URN-shaped conversion. It does not mean Campaign Manager credited the campaign.

The rule has to exist before the timestamp can matter

conversionHappenedAt is checked after the conversion URN is a real rule. Create the rule with conversionMethod CONVERSIONS_API, then associate it with the campaigns you expect to report. A perfect thirteen-digit timestamp on urn:lla:llaPartnerConversion:ID that was never tied to a campaign is a valid-looking event with nowhere to show up. Meta Purchase does not need a pre-created URN. The event name is the contract. LinkedIn's contract is the rule you made.

Stream one event or a batch under elements. The Insight Tag on the thank-you page can fire while the lead is still a form. The CAPI row for the closed-won uses the CRM time. Those rows share a rule, not a Meta event_name. Reuse the Insight Tag event id as eventId when both describe the same conversion. A new UUID on the CRM upload is a second conversion against that rule.

SHA256_EMAIL is the hashed email id type. li_fat_id is the click id, plaintext, from the landing URL. Store it server-side. Safari will drop the cookie before a long B2B cycle, and LinkedIn's 90-day window is long enough that the click id has to outlive the browser. fbclid stored in that column will not match.

Append 000 only when the integer was seconds

The schema example is 1590739275000. Validation requires a millisecond epoch inside the past 90 days. HTTP 400 INVALID_CONVERSION_TIME_FIELD_VALUE means the time, including a perfect email hash beside it. 1740000000 as milliseconds is January 1970. Appending 000 repairs a seconds source. Appending 000 to Date.now() produces sixteen digits and is a different invalid value.

Meta's seven-day cap and LinkedIn's ninety-day cap describe the same CRM export. Split the export. Events inside seven days can go to both, in each vendor's unit. Events between seven and ninety days go to LinkedIn only. Do not move event_time to now to sneak the old deal into Graph. That trains the upload hour.

The Insight Tag image uses pid. It does not carry conversionHappenedAt. A green network row for px.ads.linkedin.com/collect is not a CAPI pass. Paste the JSON, or validate it with the LinkedIn pack, when the question is the server clock.

Batch responses still need the per-element error read. A 200 on the request can carry INVALID_CONVERSION_TIME_FIELD_VALUE on one element and accept the next. Log the URN, the digit count of conversionHappenedAt, and the error type. A dashboard that only charts HTTP status will show LinkedIn as healthy while every ten-digit row was rejected.

What does not transfer

Sources

Contract pages

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

Read LinkedIn Insight and CAPI Docs