Skip to content

Commit 0cf3be4

Browse files
[LinkedIn CAPI] - new externalIds field (#3365)
1 parent e5530b4 commit 0cf3be4

File tree

4 files changed

+203
-2
lines changed

4 files changed

+203
-2
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,9 @@ export class LinkedInConversions {
461461
eventId: payload.eventId,
462462
user: {
463463
userIds,
464-
userInfo: payload.userInfo
464+
userInfo: payload.userInfo,
465+
// only 1 externalId value allowed currently in the externalIds array by LinkedIn currently Oct 2025
466+
...(Array.isArray(payload?.externalIds) && payload.externalIds.length > 0 ? { externalIds: [payload.externalIds[0]] } : {})
465467
}
466468
}
467469
})
@@ -489,7 +491,9 @@ export class LinkedInConversions {
489491
eventId: payload.eventId,
490492
user: {
491493
userIds,
492-
userInfo: payload.userInfo
494+
userInfo: payload.userInfo,
495+
// only 1 externalId value allowed currently in the externalIds array by LinkedIn currently Oct 2025
496+
...(Array.isArray(payload?.externalIds) && payload.externalIds.length > 0 ? { externalIds: [payload.externalIds[0]] } : {})
493497
}
494498
}
495499
})

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

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,192 @@ describe('LinkedinConversions.streamConversion', () => {
166166
).resolves.not.toThrowError()
167167
})
168168

169+
it('should successfully send the event with externalIds field as an array', async () => {
170+
nock(`${BASE_URL}/conversionEvents`)
171+
.post('', {
172+
conversion: 'urn:lla:llaPartnerConversion:789123',
173+
conversionHappenedAt: currentTimestamp,
174+
conversionValue: {
175+
currencyCode: 'USD',
176+
amount: '100'
177+
},
178+
user: {
179+
userIds: [
180+
{
181+
idType: 'SHA256_EMAIL',
182+
idValue: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777'
183+
}
184+
],
185+
userInfo: {
186+
firstName: 'mike',
187+
lastName: 'smith',
188+
title: 'software engineer',
189+
companyName: 'microsoft',
190+
countryCode: 'US'
191+
},
192+
externalIds: ['external_id_12345']
193+
}
194+
})
195+
.reply(201)
196+
197+
await expect(
198+
testDestination.testAction('streamConversion', {
199+
event,
200+
settings,
201+
mapping: {
202+
email: { '@path': '$.context.traits.email' },
203+
conversionHappenedAt: {
204+
'@path': '$.timestamp'
205+
},
206+
conversionValue: {
207+
currencyCode: 'USD',
208+
amount: { '@path': '$.context.traits.value' }
209+
},
210+
userInfo: {
211+
firstName: { '@path': '$.context.traits.first_name' },
212+
lastName: { '@path': '$.context.traits.last_name' },
213+
title: { '@path': '$.context.traits.title' },
214+
companyName: { '@path': '$.context.traits.companyName' },
215+
countryCode: { '@path': '$.context.traits.countryCode' }
216+
},
217+
externalIds: ['external_id_12345'],
218+
onMappingSave: {
219+
inputs: {},
220+
outputs: {
221+
id: payload.conversionId
222+
}
223+
},
224+
enable_batching: true,
225+
batch_size: 5000
226+
}
227+
})
228+
).resolves.not.toThrowError()
229+
})
230+
231+
it('should successfully send the event with externalIds field as a string', async () => {
232+
nock(`${BASE_URL}/conversionEvents`)
233+
.post('', {
234+
conversion: 'urn:lla:llaPartnerConversion:789123',
235+
conversionHappenedAt: currentTimestamp,
236+
conversionValue: {
237+
currencyCode: 'USD',
238+
amount: '100'
239+
},
240+
user: {
241+
userIds: [
242+
{
243+
idType: 'SHA256_EMAIL',
244+
idValue: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777'
245+
}
246+
],
247+
userInfo: {
248+
firstName: 'mike',
249+
lastName: 'smith',
250+
title: 'software engineer',
251+
companyName: 'microsoft',
252+
countryCode: 'US'
253+
},
254+
externalIds: ['external_id_12345']
255+
}
256+
})
257+
.reply(201)
258+
259+
await expect(
260+
testDestination.testAction('streamConversion', {
261+
event,
262+
settings,
263+
mapping: {
264+
email: { '@path': '$.context.traits.email' },
265+
conversionHappenedAt: {
266+
'@path': '$.timestamp'
267+
},
268+
conversionValue: {
269+
currencyCode: 'USD',
270+
amount: { '@path': '$.context.traits.value' }
271+
},
272+
userInfo: {
273+
firstName: { '@path': '$.context.traits.first_name' },
274+
lastName: { '@path': '$.context.traits.last_name' },
275+
title: { '@path': '$.context.traits.title' },
276+
companyName: { '@path': '$.context.traits.companyName' },
277+
countryCode: { '@path': '$.context.traits.countryCode' }
278+
},
279+
externalIds: 'external_id_12345',
280+
onMappingSave: {
281+
inputs: {},
282+
outputs: {
283+
id: payload.conversionId
284+
}
285+
},
286+
enable_batching: true,
287+
batch_size: 5000
288+
}
289+
})
290+
).resolves.not.toThrowError()
291+
})
292+
293+
it('should successfully send the event when externalIds array contains more than 1 item', async () => {
294+
nock(`${BASE_URL}/conversionEvents`)
295+
.post('', {
296+
conversion: 'urn:lla:llaPartnerConversion:789123',
297+
conversionHappenedAt: currentTimestamp,
298+
conversionValue: {
299+
currencyCode: 'USD',
300+
amount: '100'
301+
},
302+
user: {
303+
userIds: [
304+
{
305+
idType: 'SHA256_EMAIL',
306+
idValue: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777'
307+
}
308+
],
309+
userInfo: {
310+
firstName: 'mike',
311+
lastName: 'smith',
312+
title: 'software engineer',
313+
companyName: 'microsoft',
314+
countryCode: 'US'
315+
},
316+
externalIds: ['external_id_12345']
317+
}
318+
})
319+
.reply(201)
320+
321+
await expect(
322+
testDestination.testAction('streamConversion', {
323+
event,
324+
settings,
325+
mapping: {
326+
email: { '@path': '$.context.traits.email' },
327+
conversionHappenedAt: {
328+
'@path': '$.timestamp'
329+
},
330+
conversionValue: {
331+
currencyCode: 'USD',
332+
amount: { '@path': '$.context.traits.value' }
333+
},
334+
userInfo: {
335+
firstName: { '@path': '$.context.traits.first_name' },
336+
lastName: { '@path': '$.context.traits.last_name' },
337+
title: { '@path': '$.context.traits.title' },
338+
companyName: { '@path': '$.context.traits.companyName' },
339+
countryCode: { '@path': '$.context.traits.countryCode' }
340+
},
341+
externalIds: ['external_id_12345', 'external_id_67890'], // second item will be dropped
342+
onMappingSave: {
343+
inputs: {},
344+
outputs: {
345+
id: payload.conversionId
346+
}
347+
},
348+
enable_batching: true,
349+
batch_size: 5000
350+
}
351+
})
352+
).resolves.not.toThrowError()
353+
})
354+
169355
it('should successfully send a batch request with all fields', async () => {
170356
nock(`${BASE_URL}/conversionEvents`)
171357
.post('', {

packages/destination-actions/src/destinations/linkedin-conversions/streamConversion/generated-types.ts

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,13 @@ const action: ActionDefinition<Settings, Payload, undefined, OnMappingSaveInputs
282282
}
283283
}
284284
},
285+
externalIds: {
286+
label: 'External ID',
287+
description: "An identifier your organization uses for the user. See [LinkedIn's documentation](https://learn.microsoft.com/en-us/linkedin/marketing/conversions/custom-matching-identifiers?view=li-lms-2025-08) for more details.",
288+
type: 'string',
289+
multiple: true,
290+
required: false
291+
},
285292
enable_batching: {
286293
label: 'Enable Batching',
287294
description: 'Enable batching of requests.',

0 commit comments

Comments
 (0)