LinkedIn Conversions API
Offline and web conversions attributed to LinkedIn ads by hashed email or the
first-party li_fat_id tracking identifier.
Verified against the Microsoft Learn documentation on 2026-09-03: Conversions API, API versioning.
Prerequisites
- A LinkedIn Campaign Manager ad account.
- A conversion rule of type "Conversions API" created in Campaign Manager and associated with the campaigns that should get credit.
- A LinkedIn developer app with the
rw_conversionsandr_adsscopes, and an ad-account role other than Viewer for the authorising member.
What to collect
| Value | Where to find it | Stored as |
|---|---|---|
| Conversion rule ID | Campaign Manager → Analyze → Conversion tracking → your rule. The numeric id in the URL, or the tail of the urn:lla:llaPartnerConversion: URN. |
Config |
| Access token | LinkedIn developer portal → your app → Auth, then the OAuth flow for rw_conversions r_ads. Member tokens last 60 days; refresh tokens are available to approved apps. |
Vault |
| LinkedIn-Version | The YYYYMM moniker, defaulting to 202608. |
Config |
The version deserves attention: LinkedIn supports each moniker for at least a year and then rejects it outright. A destination that starts failing with a version error needs this field bumped, not a redeploy.
What SaaS Pro Max sends
POST https://api.linkedin.com/rest/conversionEvents
Authorization: Bearer <access token>
Content-Type: application/json
Linkedin-Version: 202608
X-Restli-Protocol-Version: 2.0.0
{
"conversion": "urn:lla:llaPartnerConversion:1234567",
"conversionHappenedAt": 1786998896000,
"conversionValue": { "currencyCode": "USD", "amount": "49.00" },
"eventId": "<the SaaS Pro Max event id>",
"user": {
"userIds": [
{ "idType": "SHA256_EMAIL", "idValue": "<sha256>" },
{ "idType": "LINKEDIN_FIRST_PARTY_ADS_TRACKING_UUID", "idValue": "<li_fat_id>" }
],
"userInfo": {
"firstName": "Jane", "lastName": "Doe",
"companyName": "Acme", "title": "Head of Growth", "countryCode": "US"
}
}
}
Notes that matter:
- The header is spelled
Linkedin-Versionin LinkedIn's own documentation (lowercasek), alongsideX-Restli-Protocol-Version. HTTP header names are case-insensitive, but matching the docs keeps support conversations short. conversionHappenedAtis milliseconds since the epoch, not seconds.conversionValue.amountis a decimal string, not a number.- The email is lowercased with whitespace removed, then SHA-256, hex encoded. LinkedIn does not fold Gmail dots or plus-addressing — Google Ads does, so the two adapters produce different hashes of the same Gmail address.
userIdsis always present, even when empty, because LinkedIn answers422 ERROR :: /user/userIds :: field is required but not foundotherwise. SaaS Pro Max declines to send a conversion with no identifier at all: LinkedIn would accept it and attribute nothing.firstNameandlastNameare only sent together, as LinkedIn requires.- The
li_fat_idcomes from the first-party tracking parameter the browser SDK captures on the landing page.
Mapping
The "Conversion rule" column takes either the numeric conversion rule ID or a
full urn:lla:llaPartnerConversion:… URN. A rule with neither, and no default
configured, is an error rather than a guess.
Different SPM events usually map to different conversion rules — that is how LinkedIn distinguishes a lead from a purchase.
Verifying
- Press Send test event.
- A successful create answers 201 Created with no body; the delivery log
shows
sent. - In Campaign Manager → Analyze → Conversion tracking, open the rule. Recent conversions appear there, though attribution can lag by several hours.
Common rejections:
401 EMPTY_ACCESS_TOKEN— the token expired (member tokens last 60 days).403 USER_NOT_AUTHORIZED— the authorising member has only the Viewer role.422on/conversionHappenedAt— the timestamp is outside the accepted window, or was sent in seconds.
Limitations
- A conversion must have happened within the last 90 days. SaaS Pro Max does
not even attempt an older one; it records the delivery as
skipped. - One conversion per request. LinkedIn's batch endpoint accepts 5,000, but a batch fails atomically — one bad row rejects the whole payload — which is a poor trade for a stream of independent conversions.
- Rate limits are 600 requests per minute and 500,000 per day per token. Throttling is retried; a rejected payload is not.