Skip to content

Commit 1f806a9

Browse files
feat(linkedin-conversions): upgrade API to 202603 behind feature flag (#3709)
- Add LINKEDIN_CONVERSIONS_CANARY_API_VERSION = '202603' to versioning-info.ts - Add FLAGON_NAME and getApiVersion() helper to constants.ts - Update extendRequest in index.ts to use getApiVersion(features) - Add feature flag tests for both stable (202505) and canary (202603) versions - All 45 tests passing Version 202505 sunsets May 15, 2026. No breaking changes between 202505 and 202603. Co-authored-by: Claude Sonnet 4.6 <[email protected]>
1 parent 4f06d9a commit 1f806a9

4 files changed

Lines changed: 95 additions & 6 deletions

File tree

packages/destination-actions/src/destinations/linkedin-conversions/constants.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { DependsOnConditions } from '@segment/actions-core/destination-kit/types'
2-
import { LINKEDIN_CONVERSIONS_API_VERSION } from './versioning-info'
2+
import { Features } from '@segment/actions-core'
3+
import { LINKEDIN_CONVERSIONS_API_VERSION, LINKEDIN_CONVERSIONS_CANARY_API_VERSION } from './versioning-info'
34

45
export const LINKEDIN_API_VERSION = LINKEDIN_CONVERSIONS_API_VERSION
6+
export const LINKEDIN_CANARY_API_VERSION = LINKEDIN_CONVERSIONS_CANARY_API_VERSION
7+
export const FLAGON_NAME = 'linkedin-conversions-canary-version'
8+
9+
export function getApiVersion(features?: Features): string {
10+
return features && features[FLAGON_NAME] ? LINKEDIN_CANARY_API_VERSION : LINKEDIN_API_VERSION
11+
}
512
export const BASE_URL = 'https://api.linkedin.com/rest'
613
export const LINKEDIN_SOURCE_PLATFORM = 'SEGMENT'
714

packages/destination-actions/src/destinations/linkedin-conversions/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { InvalidAuthenticationError, IntegrationError, ErrorCodes } from '@segme
33
import type { Settings } from './generated-types'
44
import { LinkedInConversions } from './api'
55
import type { LinkedInTestAuthenticationError, RefreshTokenResponse, LinkedInRefreshTokenError } from './types'
6-
import { LINKEDIN_API_VERSION } from './constants'
6+
import { getApiVersion } from './constants'
77
import https from 'https'
88
import streamConversion from './streamConversion'
99

@@ -88,7 +88,7 @@ const destination: DestinationDefinition<Settings> = {
8888
return { accessToken: res?.data?.access_token }
8989
}
9090
},
91-
extendRequest({ auth }) {
91+
extendRequest({ auth, features }) {
9292
// Repeat calls to the same LinkedIn API endpoint were failing due to a `socket hang up`.
9393
// This seems to fix it: https://stackoverflow.com/questions/62500011/reuse-tcp-connection-with-node-fetch-in-node-js
9494
// Copied from LinkedIn Audiences extendRequest, which also ran into this issue.
@@ -97,7 +97,7 @@ const destination: DestinationDefinition<Settings> = {
9797
return {
9898
headers: {
9999
authorization: `Bearer ${auth?.accessToken}`,
100-
'LinkedIn-Version': LINKEDIN_API_VERSION,
100+
'LinkedIn-Version': getApiVersion(features),
101101
'X-Restli-Protocol-Version': `2.0.0`
102102
},
103103
agent

packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/__tests__/index.test.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import nock from 'nock'
22
import { createTestEvent, createTestIntegration } from '@segment/actions-core'
33
import { DynamicFieldResponse } from '@segment/actions-core'
4-
import { BASE_URL } from '../../constants'
4+
import { BASE_URL, FLAGON_NAME, LINKEDIN_API_VERSION, LINKEDIN_CANARY_API_VERSION } from '../../constants'
55
import Destination from '../../index'
66

77
const testDestination = createTestIntegration(Destination)
@@ -709,6 +709,80 @@ describe('LinkedinConversions.dynamicField', () => {
709709
})
710710
})
711711

712+
describe('LinkedinConversions.apiVersion feature flag', () => {
713+
it('should use stable API version (202505) by default', async () => {
714+
nock(`${BASE_URL}/conversionEvents`)
715+
.post('', {
716+
conversion: 'urn:lla:llaPartnerConversion:789123',
717+
conversionHappenedAt: currentTimestamp,
718+
user: {
719+
userIds: [
720+
{
721+
idType: 'SHA256_EMAIL',
722+
idValue: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777'
723+
}
724+
]
725+
}
726+
})
727+
.matchHeader('LinkedIn-Version', LINKEDIN_API_VERSION)
728+
.reply(201)
729+
730+
await expect(
731+
testDestination.testAction('streamConversion', {
732+
event,
733+
settings,
734+
mapping: {
735+
email: { '@path': '$.context.traits.email' },
736+
conversionHappenedAt: { '@path': '$.timestamp' },
737+
onMappingSave: {
738+
inputs: {},
739+
outputs: { id: payload.conversionId }
740+
},
741+
enable_batching: true,
742+
batch_size: 5000
743+
}
744+
// no features = stable version
745+
})
746+
).resolves.not.toThrowError()
747+
})
748+
749+
it('should use canary API version (202603) when feature flag is enabled', async () => {
750+
nock(`${BASE_URL}/conversionEvents`)
751+
.post('', {
752+
conversion: 'urn:lla:llaPartnerConversion:789123',
753+
conversionHappenedAt: currentTimestamp,
754+
user: {
755+
userIds: [
756+
{
757+
idType: 'SHA256_EMAIL',
758+
idValue: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777'
759+
}
760+
]
761+
}
762+
})
763+
.matchHeader('LinkedIn-Version', LINKEDIN_CANARY_API_VERSION)
764+
.reply(201)
765+
766+
await expect(
767+
testDestination.testAction('streamConversion', {
768+
event,
769+
settings,
770+
mapping: {
771+
email: { '@path': '$.context.traits.email' },
772+
conversionHappenedAt: { '@path': '$.timestamp' },
773+
onMappingSave: {
774+
inputs: {},
775+
outputs: { id: payload.conversionId }
776+
},
777+
enable_batching: true,
778+
batch_size: 5000
779+
},
780+
features: { [FLAGON_NAME]: true }
781+
})
782+
).resolves.not.toThrowError()
783+
})
784+
})
785+
712786
describe('LinkedinConversions.timestamp', () => {
713787
it('should convert a human readable date to a unix timestamp', async () => {
714788
event.timestamp = currentTimestamp.toString()
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
/** LINKEDIN_CONVERSIONS_API_VERSION
2-
* LinkedIn Conversions API version.
2+
* LinkedIn Conversions API version (stable/production).
33
* API reference: https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/conversions-api?view=li-lms-2025-05&tabs=curl
4+
* Changelog: https://learn.microsoft.com/en-us/linkedin/marketing/changelog
45
*/
56
export const LINKEDIN_CONVERSIONS_API_VERSION = '202505'
7+
8+
/** LINKEDIN_CONVERSIONS_CANARY_API_VERSION
9+
* LinkedIn Conversions API version (canary/feature-flagged).
10+
* Testing new version 202603 behind feature flag.
11+
* Version 202505 sunsets May 15, 2026.
12+
*/
13+
export const LINKEDIN_CONVERSIONS_CANARY_API_VERSION = '202603'

0 commit comments

Comments
 (0)