From abf1c402a91d5fa5400cd3531d5048f7a7f16f6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:43:19 +0000 Subject: [PATCH 1/4] Initial plan From 632cb1d5e1a7bdec052e7963a5a2c35a4bb6097c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:51:12 +0000 Subject: [PATCH 2/4] Fix batching for editCustomerMatchMembers actions Co-authored-by: abhandage <174417010+abhandage@users.noreply.github.com> --- .../addToAudContactInfo/_tests_/index.test.ts | 150 ++++++++++++++++++ .../first-party-dv360/functions.ts | 56 ++++--- .../_tests_/index.test.ts | 90 ++++++++++- 3 files changed, 277 insertions(+), 19 deletions(-) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts b/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts index 2fe4bfe6a5f..a1905763836 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts @@ -25,6 +25,27 @@ const event = createTestEvent({ } }) +const event2 = createTestEvent({ + event: 'Audience Entered', + type: 'track', + properties: { + audience_key: 'personas_test_audience' + }, + context: { + device: { + advertisingId: '456' + }, + traits: { + email: 'testing2@testing.com', + phoneNumbers: '+0987654321', + zipCodes: '54321', + firstName: 'Jane', + lastName: 'Smith', + countryCode: 'CA' + } + } +}) + describe('First-Party-dv360.addToAudContactInfo', () => { it('should hash pii data if not already hashed', async () => { nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') @@ -77,4 +98,133 @@ describe('First-Party-dv360.addToAudContactInfo', () => { '"{\\"advertiserId\\":\\"1234567890\\",\\"addedContactInfoList\\":{\\"contactInfos\\":[{\\"hashedEmails\\":\\"584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777\\",\\"hashedPhoneNumbers\\":\\"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8\\",\\"zipCodes\\":\\"12345\\",\\"hashedFirstName\\":\\"96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a\\",\\"hashedLastName\\":\\"799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f\\",\\"countryCode\\":\\"US\\"}],\\"consent\\":{\\"adUserData\\":\\"CONSENT_STATUS_GRANTED\\",\\"adPersonalization\\":\\"CONSENT_STATUS_GRANTED\\"}}}"' ) }) + + it('should handle batch requests with multiple payloads', async () => { + nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') + .post('/1234567890:editCustomerMatchMembers') + .reply(200, { success: true }) + + const responses = await testDestination.testBatchAction('addToAudContactInfo', { + events: [event, event2], + mapping: { + emails: { + '@arrayPath': [ + '$.context.traits.email' + ] + }, + phoneNumbers: { + '@arrayPath': [ + '$.context.traits.phoneNumbers' + ] + }, + zipCodes: { + '@arrayPath': [ + '$.context.traits.zipCodes' + ] + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + }, + external_id: '1234567890', + advertiser_id: '1234567890', + enable_batching: true, + batch_size: 10 + } + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + + // Parse the request body to verify it contains both contact infos + const requestBody = JSON.parse(responses[0].options.body as string) + expect(requestBody.addedContactInfoList.contactInfos).toHaveLength(2) + + // Verify first contact info + expect(requestBody.addedContactInfoList.contactInfos[0]).toMatchObject({ + hashedEmails: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777', + hashedPhoneNumbers: '422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8', + zipCodes: '12345', + hashedFirstName: '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: '799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f', + countryCode: 'US' + }) + + // Verify second contact info + expect(requestBody.addedContactInfoList.contactInfos[1]).toMatchObject({ + hashedEmails: 'e67e72c0e95b2f8a00d4b8e5d7eb97c51b2ad442e1dc2dfbc0e82c11c16b1e42', + hashedPhoneNumbers: '1234a97e4a0ad5c36dc5dd79a5031639ee2ab65ae6a0b63e16c08a92b8088f50', + zipCodes: '54321', + hashedFirstName: '7ba34aa26aabe4b7d32a2d824ec2c5ff82daf7ad96e0f4fc7de2e06b8d7a7b72', + hashedLastName: '0c70e5c5c47c7e1257da04df3c2ca4ce05e3cbb6e5d5a3dbf8fbcae2b6e47e52', + countryCode: 'CA' + }) + }) + + it('should filter out payloads without required identifiers in batch', async () => { + nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') + .post('/1234567890:editCustomerMatchMembers') + .reply(200, { success: true }) + + const eventWithoutIdentifiers = createTestEvent({ + event: 'Audience Entered', + type: 'track', + properties: { + audience_key: 'personas_test_audience' + }, + context: { + traits: { + // No email, phone, firstName, or lastName + zipCodes: '99999', + countryCode: 'FR' + } + } + }) + + const responses = await testDestination.testBatchAction('addToAudContactInfo', { + events: [event, eventWithoutIdentifiers, event2], + mapping: { + emails: { + '@arrayPath': [ + '$.context.traits.email' + ] + }, + phoneNumbers: { + '@arrayPath': [ + '$.context.traits.phoneNumbers' + ] + }, + zipCodes: { + '@arrayPath': [ + '$.context.traits.zipCodes' + ] + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + }, + external_id: '1234567890', + advertiser_id: '1234567890', + enable_batching: true, + batch_size: 10 + } + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + + // Parse the request body to verify it only contains 2 contact infos (filtered out the invalid one) + const requestBody = JSON.parse(responses[0].options.body as string) + expect(requestBody.addedContactInfoList.contactInfos).toHaveLength(2) + }) }) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/functions.ts b/packages/destination-actions/src/destinations/first-party-dv360/functions.ts index 21be8a11430..6341d42621e 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/functions.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/functions.ts @@ -79,20 +79,30 @@ export async function editDeviceMobileIds( operation: 'add' | 'remove', statsContext?: StatsContext // Adjust type based on actual stats context ) { - const payload = payloads[0] - const audienceId = payload.external_id + if (payloads.length === 0) { + return + } + + const firstPayload = payloads[0] + const audienceId = firstPayload.external_id + const advertiserId = firstPayload.advertiser_id - //Check if mobile device id exists otherwise drop the event - if (payload.mobileDeviceIds === undefined) { + // Filter out payloads that don't have mobile device ids + const validPayloads = payloads.filter(payload => payload.mobileDeviceIds !== undefined) + + if (validPayloads.length === 0) { return } //Format the endpoint const endpoint = DV360API + '/' + audienceId + ':editCustomerMatchMembers' + // Collect all mobile device ids from valid payloads + const mobileDeviceIds = validPayloads.map(payload => payload.mobileDeviceIds).filter(Boolean) + // Prepare the request payload const mobileDeviceIdList = { - mobileDeviceIds: [payload.mobileDeviceIds], + mobileDeviceIds: mobileDeviceIds, consent: { adUserData: CONSENT_STATUS_GRANTED, adPersonalization: CONSENT_STATUS_GRANTED @@ -101,7 +111,7 @@ export async function editDeviceMobileIds( // Convert the payload to string if needed const requestPayload = JSON.stringify({ - advertiserId: payload.advertiser_id, + advertiserId: advertiserId, ...(operation === 'add' ? { addedMobileDeviceIdList: mobileDeviceIdList } : {}), ...(operation === 'remove' ? { removedMobileDeviceIdList: mobileDeviceIdList } : {}) }) @@ -131,25 +141,35 @@ export async function editContactInfo( operation: 'add' | 'remove', statsContext?: StatsContext ) { - const payload = payloads[0] - const audienceId = payloads[0].external_id - - //Check if one of the required identifiers exists otherwise drop the event - if ( - payload.emails === undefined && - payload.phoneNumbers === undefined && - payload.firstName === undefined && - payload.lastName === undefined - ) { + if (payloads.length === 0) { + return + } + + const firstPayload = payloads[0] + const audienceId = firstPayload.external_id + const advertiserId = firstPayload.advertiser_id + + // Filter out payloads that don't have any required identifiers + const validPayloads = payloads.filter(payload => + payload.emails !== undefined || + payload.phoneNumbers !== undefined || + payload.firstName !== undefined || + payload.lastName !== undefined + ) + + if (validPayloads.length === 0) { return } //Format the endpoint const endpoint = DV360API + '/' + audienceId + ':editCustomerMatchMembers' + // Process all valid payloads into contact infos + const contactInfos = validPayloads.map(payload => processPayload(payload)) + // Prepare the request payload const contactInfoList = { - contactInfos: [processPayload(payload)], + contactInfos: contactInfos, consent: { adUserData: CONSENT_STATUS_GRANTED, adPersonalization: CONSENT_STATUS_GRANTED @@ -158,7 +178,7 @@ export async function editContactInfo( // Convert the payload to string if needed const requestPayload = JSON.stringify({ - advertiserId: payload.advertiser_id, + advertiserId: advertiserId, ...(operation === 'add' ? { addedContactInfoList: contactInfoList } : {}), ...(operation === 'remove' ? { removedContactInfoList: contactInfoList } : {}) }) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts b/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts index 8dfbfec41e3..5d15e638f70 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts @@ -25,7 +25,28 @@ const event = createTestEvent({ } }) -describe('First-Party-dv360.removeToAudContactInfo', () => { +const event2 = createTestEvent({ + event: 'Audience Exited', + type: 'track', + properties: { + audience_key: 'personas_test_audience' + }, + context: { + device: { + advertisingId: '456' + }, + traits: { + email: 'testing2@testing.com', + phoneNumbers: '+0987654321', + zipCodes: '54321', + firstName: 'Jane', + lastName: 'Smith', + countryCode: 'CA' + } + } +}) + +describe('First-Party-dv360.removeFromAudContactInfo', () => { it('should hash pii data if not already hashed', async () => { nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') .post('/1234567890:editCustomerMatchMembers') @@ -77,4 +98,71 @@ describe('First-Party-dv360.removeToAudContactInfo', () => { '{"advertiserId":"1234567890","removedContactInfoList":{"contactInfos":[{"hashedEmails":"584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777","hashedPhoneNumbers":"422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8","zipCodes":"12345","hashedFirstName":"96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a","hashedLastName":"799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f","countryCode":"US"}],"consent":{"adUserData":"CONSENT_STATUS_GRANTED","adPersonalization":"CONSENT_STATUS_GRANTED"}}}' ) }) + + it('should handle batch requests with multiple payloads', async () => { + nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') + .post('/1234567890:editCustomerMatchMembers') + .reply(200, { success: true }) + + const responses = await testDestination.testBatchAction('removeFromAudContactInfo', { + events: [event, event2], + mapping: { + emails: { + '@arrayPath': [ + '$.context.traits.email' + ] + }, + phoneNumbers: { + '@arrayPath': [ + '$.context.traits.phoneNumbers' + ] + }, + zipCodes: { + '@arrayPath': [ + '$.context.traits.zipCodes' + ] + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + }, + external_id: '1234567890', + advertiser_id: '1234567890', + enable_batching: true, + batch_size: 10 + } + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + + // Parse the request body to verify it contains both contact infos + const requestBody = JSON.parse(responses[0].options.body as string) + expect(requestBody.removedContactInfoList.contactInfos).toHaveLength(2) + + // Verify first contact info + expect(requestBody.removedContactInfoList.contactInfos[0]).toMatchObject({ + hashedEmails: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777', + hashedPhoneNumbers: '422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8', + zipCodes: '12345', + hashedFirstName: '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: '799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f', + countryCode: 'US' + }) + + // Verify second contact info + expect(requestBody.removedContactInfoList.contactInfos[1]).toMatchObject({ + hashedEmails: 'e67e72c0e95b2f8a00d4b8e5d7eb97c51b2ad442e1dc2dfbc0e82c11c16b1e42', + hashedPhoneNumbers: '1234a97e4a0ad5c36dc5dd79a5031639ee2ab65ae6a0b63e16c08a92b8088f50', + zipCodes: '54321', + hashedFirstName: '7ba34aa26aabe4b7d32a2d824ec2c5ff82daf7ad96e0f4fc7de2e06b8d7a7b72', + hashedLastName: '0c70e5c5c47c7e1257da04df3c2ca4ce05e3cbb6e5d5a3dbf8fbcae2b6e47e52', + countryCode: 'CA' + }) + }) }) From 3b44b63bb637fe393b9caccd6a710adc91ec073a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:53:50 +0000 Subject: [PATCH 3/4] Update test hash values and add edge case tests Co-authored-by: abhandage <174417010+abhandage@users.noreply.github.com> --- .../addToAudContactInfo/_tests_/index.test.ts | 63 +++++++++++++++++-- .../_tests_/index.test.ts | 8 +-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts b/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts index a1905763836..3ba81f808af 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/addToAudContactInfo/_tests_/index.test.ts @@ -157,11 +157,11 @@ describe('First-Party-dv360.addToAudContactInfo', () => { // Verify second contact info expect(requestBody.addedContactInfoList.contactInfos[1]).toMatchObject({ - hashedEmails: 'e67e72c0e95b2f8a00d4b8e5d7eb97c51b2ad442e1dc2dfbc0e82c11c16b1e42', - hashedPhoneNumbers: '1234a97e4a0ad5c36dc5dd79a5031639ee2ab65ae6a0b63e16c08a92b8088f50', + hashedEmails: 'f9b0f73e2d723f122e24fddfebf37978c09a31b8530be10dccf51e6a4c49cbfa', + hashedPhoneNumbers: '75bfc57aed345daba0e4394b604a334c87ab5f7b1c04dfdb649bcc457c182fa9', zipCodes: '54321', - hashedFirstName: '7ba34aa26aabe4b7d32a2d824ec2c5ff82daf7ad96e0f4fc7de2e06b8d7a7b72', - hashedLastName: '0c70e5c5c47c7e1257da04df3c2ca4ce05e3cbb6e5d5a3dbf8fbcae2b6e47e52', + hashedFirstName: '81f8f6dde88365f3928796ec7aa53f72820b06db8664f5fe76a7eb13e24546a2', + hashedLastName: '6627835f988e2c5e50533d491163072d3f4f41f5c8b04630150debb3722ca2dd', countryCode: 'CA' }) }) @@ -227,4 +227,59 @@ describe('First-Party-dv360.addToAudContactInfo', () => { const requestBody = JSON.parse(responses[0].options.body as string) expect(requestBody.addedContactInfoList.contactInfos).toHaveLength(2) }) + + it('should handle single payload in batch properly', async () => { + nock('https://displayvideo.googleapis.com/v3/firstAndThirdPartyAudiences') + .post('/1234567890:editCustomerMatchMembers') + .reply(200, { success: true }) + + const responses = await testDestination.testBatchAction('addToAudContactInfo', { + events: [event], + mapping: { + emails: { + '@arrayPath': [ + '$.context.traits.email' + ] + }, + phoneNumbers: { + '@arrayPath': [ + '$.context.traits.phoneNumbers' + ] + }, + zipCodes: { + '@arrayPath': [ + '$.context.traits.zipCodes' + ] + }, + firstName: { + '@path': '$.context.traits.firstName' + }, + lastName: { + '@path': '$.context.traits.lastName' + }, + countryCode: { + '@path': '$.context.traits.countryCode' + }, + external_id: '1234567890', + advertiser_id: '1234567890', + enable_batching: true, + batch_size: 10 + } + }) + + expect(responses.length).toBe(1) + expect(responses[0].status).toBe(200) + + // Parse the request body to verify it contains 1 contact info + const requestBody = JSON.parse(responses[0].options.body as string) + expect(requestBody.addedContactInfoList.contactInfos).toHaveLength(1) + expect(requestBody.addedContactInfoList.contactInfos[0]).toMatchObject({ + hashedEmails: '584c4423c421df49955759498a71495aba49b8780eb9387dff333b6f0982c777', + hashedPhoneNumbers: '422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8', + zipCodes: '12345', + hashedFirstName: '96d9632f363564cc3032521409cf22a852f2032eec099ed5967c0d000cec607a', + hashedLastName: '799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f', + countryCode: 'US' + }) + }) }) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts b/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts index 5d15e638f70..dff26dcbdea 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/removeFromAudContactInfo/_tests_/index.test.ts @@ -157,11 +157,11 @@ describe('First-Party-dv360.removeFromAudContactInfo', () => { // Verify second contact info expect(requestBody.removedContactInfoList.contactInfos[1]).toMatchObject({ - hashedEmails: 'e67e72c0e95b2f8a00d4b8e5d7eb97c51b2ad442e1dc2dfbc0e82c11c16b1e42', - hashedPhoneNumbers: '1234a97e4a0ad5c36dc5dd79a5031639ee2ab65ae6a0b63e16c08a92b8088f50', + hashedEmails: 'f9b0f73e2d723f122e24fddfebf37978c09a31b8530be10dccf51e6a4c49cbfa', + hashedPhoneNumbers: '75bfc57aed345daba0e4394b604a334c87ab5f7b1c04dfdb649bcc457c182fa9', zipCodes: '54321', - hashedFirstName: '7ba34aa26aabe4b7d32a2d824ec2c5ff82daf7ad96e0f4fc7de2e06b8d7a7b72', - hashedLastName: '0c70e5c5c47c7e1257da04df3c2ca4ce05e3cbb6e5d5a3dbf8fbcae2b6e47e52', + hashedFirstName: '81f8f6dde88365f3928796ec7aa53f72820b06db8664f5fe76a7eb13e24546a2', + hashedLastName: '6627835f988e2c5e50533d491163072d3f4f41f5c8b04630150debb3722ca2dd', countryCode: 'CA' }) }) From 742574776325738134b99ea6947570147fca64df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 07:13:49 +0000 Subject: [PATCH 4/4] Remove unnecessary payload filtering as payloads are already validated Co-authored-by: abhandage <174417010+abhandage@users.noreply.github.com> --- .../first-party-dv360/functions.ts | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/packages/destination-actions/src/destinations/first-party-dv360/functions.ts b/packages/destination-actions/src/destinations/first-party-dv360/functions.ts index 6341d42621e..ebe8ceaf781 100644 --- a/packages/destination-actions/src/destinations/first-party-dv360/functions.ts +++ b/packages/destination-actions/src/destinations/first-party-dv360/functions.ts @@ -87,18 +87,11 @@ export async function editDeviceMobileIds( const audienceId = firstPayload.external_id const advertiserId = firstPayload.advertiser_id - // Filter out payloads that don't have mobile device ids - const validPayloads = payloads.filter(payload => payload.mobileDeviceIds !== undefined) - - if (validPayloads.length === 0) { - return - } - //Format the endpoint const endpoint = DV360API + '/' + audienceId + ':editCustomerMatchMembers' - // Collect all mobile device ids from valid payloads - const mobileDeviceIds = validPayloads.map(payload => payload.mobileDeviceIds).filter(Boolean) + // Collect all mobile device ids from payloads + const mobileDeviceIds = payloads.map(payload => payload.mobileDeviceIds) // Prepare the request payload const mobileDeviceIdList = { @@ -149,23 +142,11 @@ export async function editContactInfo( const audienceId = firstPayload.external_id const advertiserId = firstPayload.advertiser_id - // Filter out payloads that don't have any required identifiers - const validPayloads = payloads.filter(payload => - payload.emails !== undefined || - payload.phoneNumbers !== undefined || - payload.firstName !== undefined || - payload.lastName !== undefined - ) - - if (validPayloads.length === 0) { - return - } - //Format the endpoint const endpoint = DV360API + '/' + audienceId + ':editCustomerMatchMembers' - // Process all valid payloads into contact infos - const contactInfos = validPayloads.map(payload => processPayload(payload)) + // Process all payloads into contact infos + const contactInfos = payloads.map(payload => processPayload(payload)) // Prepare the request payload const contactInfoList = {