Skip to content

Add VoiceOps name mapping fields - #3870

Open
toddtarsi wants to merge 6 commits into
segmentio:mainfrom
voiceopshq:agent/voiceops-name-mappings
Open

Add VoiceOps name mapping fields#3870
toddtarsi wants to merge 6 commits into
segmentio:mainfrom
voiceopshq:agent/voiceops-name-mappings

Conversation

@toddtarsi

Copy link
Copy Markdown

What changed

Adds explicit optional name mapping fields to the VoiceOps Send Call Completed action:

  • customer_first_name
  • customer_last_name
  • agent_first_name
  • agent_last_name
  • assign_first_last_name_by_splitting_email

When email splitting is enabled, missing primary agent and agent-leg names are derived from the email local-part before the request is sent to VoiceOps. For example, ava-agent@voiceops.com derives Ava / Agent.

Why

VoiceOps customers need clearer mapping controls for customer and agent names, plus a fallback for deriving agent names from email addresses when names are not provided by the source event.

Compatibility

  • No new required fields.
  • Scope is limited to the VoiceOps integration.
  • Existing required call fields and validation remain unchanged.

Validation

  • corepack yarn build
  • TZ=UTC ./node_modules/.bin/jest --testPathPattern=voiceops --runInBand -u
  • git diff --check

Note: the local pre-commit hook could not run because it invokes yarn directly and yarn is not on PATH in this shell; the equivalent build/test checks above were run manually.

Segment review note

Before marking this ready for review, attach an Action Tester video showing the new mapping fields and email-splitting behavior with a realistic Call Completed payload.

Copilot AI lite review requested due to automatic review settings July 9, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds explicit name-mapping fields and an optional “split agent names from email” fallback for the VoiceOps Send Call Completed action, so customers can control customer/agent naming and fill missing agent names when upstream events only contain emails.

Changes:

  • Introduces customer_* and agent_* mapping fields plus assign_first_last_name_by_splitting_email.
  • Implements derivation of missing agent + agent-leg names from email local-part before validation/request.
  • Updates generated payload types and expands Jest coverage + snapshots for the new behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/destination-actions/src/destinations/voiceops/sendCallCompleted/index.ts Adds new mapping fields, email-splitting name derivation, and uses derived payload during perform.
packages/destination-actions/src/destinations/voiceops/sendCallCompleted/generated-types.ts Updates Payload typings to include new fields and makes agent-leg names optional.
packages/destination-actions/src/destinations/voiceops/sendCallCompleted/tests/index.test.ts Adds tests for name forwarding and email-derived names; updates prior test naming/expectations.
packages/destination-actions/src/destinations/voiceops/sendCallCompleted/tests/snapshots/snapshot.test.ts.snap Updates action snapshot to reflect new fields.
packages/destination-actions/src/destinations/voiceops/tests/snapshots/snapshot.test.ts.snap Updates destination snapshot to reflect new fields.

Comment on lines +23 to +26
return {
first_name: titleCase(nameParts[0]),
last_name: nameParts.length > 1 ? nameParts.slice(1).map(titleCase).join(' ') : undefined
}
Comment on lines 279 to 288
first_name: {
label: 'First Name',
description: 'The first name of the agent for this call leg.',
type: 'string',
required: true
type: 'string'
},
last_name: {
label: 'Last Name',
description: 'The last name of the agent for this call leg.',
type: 'string',
required: true
type: 'string'
}
Copilot AI review requested due to automatic review settings July 10, 2026 11:44
@toddtarsi
toddtarsi force-pushed the agent/voiceops-name-mappings branch from 798e726 to 5362ee8 Compare July 10, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

packages/destination-actions/src/destinations/voiceops/sendCallCompleted/index.ts:84

  • With the new assign_first_last_name_by_splitting_email feature, these validation errors can become less actionable when names are omitted intentionally (expecting derivation) but derivation fails (e.g., missing/invalid agent_email on a leg). Consider updating the error text to mention the derivation option and the requirement for a valid agent_email when names are not provided (e.g., 'Provide agentLegs.first_name or enable email splitting and supply agentLegs.agent_email').
  for (const agentLeg of payload.agentLegs ?? []) {
    if (!agentLeg.first_name?.trim()) {
      throw new PayloadValidationError('agentLegs.first_name is required for every agent leg entry.')
    }

    if (!agentLeg.last_name?.trim()) {
      throw new PayloadValidationError('agentLegs.last_name is required for every agent leg entry.')
    }
  }

Comment on lines +130 to +137
customer_first_name: {
label: 'Customer First Name',
description: 'The first name for the customer.',
type: 'string',
default: {
'@path': '$.properties.customer_first_name'
}
},
Comment on lines +146 to +161
agent_first_name: {
label: 'Agent First Name',
description: 'The first name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.first_name'
'@path': '$.properties.agent_first_name'
}
},
last_name: {
label: 'Last Name',
agent_last_name: {
label: 'Agent Last Name',
description: 'The last name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.last_name'
'@path': '$.properties.agent_last_name'
}
},
Comment on lines +15 to +21
function splitNameFromEmail(email?: string): { first_name?: string; last_name?: string } {
const localPart = email?.split('@')[0]?.split('+')[0]
const nameParts = localPart?.split(/[^a-zA-Z0-9]+/).filter(Boolean) ?? []

if (nameParts.length === 0) {
return {}
}
Comment on lines +11 to +13
function titleCase(value: string): string {
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase()
}
Copilot AI review requested due to automatic review settings July 10, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +150 to +165
agent_first_name: {
label: 'Agent First Name',
description: 'The first name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.first_name'
'@path': '$.properties.agent_first_name'
}
},
last_name: {
label: 'Last Name',
agent_last_name: {
label: 'Agent Last Name',
description: 'The last name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.last_name'
'@path': '$.properties.agent_last_name'
}
},
Comment on lines 83 to 87
if (agentLeg.last_name === undefined || agentLeg.last_name === null) {
throw new PayloadValidationError(
'agentLegs.last_name is required for every agent leg entry. Provide first_name and last_name, or enable assign_first_last_name_by_splitting_email and provide an agent_email that can be split. Single-token email local-parts derive an empty last_name.'
)
}
Comment on lines +36 to +39
/**
* When enabled, missing agent first and last names are derived from agent email addresses by splitting the email local-part.
*/
assign_first_last_name_by_splitting_email?: boolean
@toddtarsi
toddtarsi marked this pull request as ready for review July 10, 2026 12:38
@toddtarsi
toddtarsi requested a review from a team as a code owner July 10, 2026 12:38
@toddtarsi

Copy link
Copy Markdown
Author

Video of test:

bf1496384aac109e26b89d1ab0529d13.webm

@joe-ayoub-segment

Copy link
Copy Markdown
Contributor

Hi @toddtarsi thanks for the PR.

Is this destination in use by customers yet?
If so, then changing the name of the fields first_name -> agent_first_name, and last_name -> agent_last_name is essentially doing a delete of first_name and last_name fields, and an addition of new fields for agent_first_name and agent_last_name.

If any customers are already using the first_name and last_name fields, the data from these fields will not be collected any more.

I would advise leaving the names of the fields as they were, and simply change the field labels and descriptions - doing this is not a breaking change.

Please let me know how you'd like to proceed.

@toddtarsi

Copy link
Copy Markdown
Author

@joe-ayoub-segment - That sounds good, will make that tweak instead. Thank you

Copilot AI review requested due to automatic review settings July 15, 2026 13:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +150 to +165
agent_first_name: {
label: 'Agent First Name',
description: 'The first name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.first_name'
'@path': '$.properties.agent_first_name'
}
},
last_name: {
label: 'Last Name',
agent_last_name: {
label: 'Agent Last Name',
description: 'The last name for the primary handling agent.',
type: 'string',
default: {
'@path': '$.properties.last_name'
'@path': '$.properties.agent_last_name'
}
},
Comment on lines 83 to 87
if (agentLeg.last_name === undefined || agentLeg.last_name === null) {
throw new PayloadValidationError(
'agentLegs.last_name is required for every agent leg entry. Provide first_name and last_name, or enable assign_first_last_name_by_splitting_email and provide an agent_email that can be split. Single-token email local-parts derive an empty last_name.'
)
}
Comment on lines +38 to +48
agent_first_name: payload.agent_first_name || primaryAgentName.first_name,
agent_last_name: payload.agent_last_name || primaryAgentName.last_name,
agentLegs: payload.agentLegs?.map((agentLeg) => {
const splitName = splitNameFromEmail(agentLeg.agent_email)

return {
...agentLeg,
first_name: agentLeg.first_name || splitName.first_name,
last_name: agentLeg.last_name || splitName.last_name
}
})
@joe-ayoub-segment

Copy link
Copy Markdown
Contributor

@joe-ayoub-segment - That sounds good, will make that tweak instead. Thank you

Thanks @toddtarsi .

You also need to revert the name for the new agent_last_name field.
Then make sure you to run yarn types and commit the changes.

Copilot AI review requested due to automatic review settings July 21, 2026 19:39
@toddtarsi
toddtarsi force-pushed the agent/voiceops-name-mappings branch from d3551a2 to 4242bfc Compare July 21, 2026 19:51
@toddtarsi
toddtarsi force-pushed the agent/voiceops-name-mappings branch from 4242bfc to 9afda04 Compare July 21, 2026 19:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Comment on lines +48 to +63
return {
...payload,
first_name: shouldDeriveFirstName(payload.first_name) ? primaryAgentName.first_name : payload.first_name,
last_name: shouldDeriveLastName(payload.last_name) ? primaryAgentName.last_name : payload.last_name,
agentLegs: payload.agentLegs?.map((agentLeg) => {
const splitName = splitNameFromEmail(agentLeg.agent_email)

return {
...agentLeg,
first_name: shouldDeriveFirstName(agentLeg.first_name) ? splitName.first_name : agentLeg.first_name,
last_name: shouldDeriveLastName(agentLeg.last_name) ? splitName.last_name : agentLeg.last_name
}
})
}
}

Comment on lines +37 to +39
function shouldDeriveLastName(value?: string): boolean {
return value === undefined || value === null || (value !== '' && value.trim() === '')
}
Comment on lines +11 to +13
function titleCase(value: string): string {
return value.charAt(0).toUpperCase() + value.slice(1)
}
Copilot AI review requested due to automatic review settings July 21, 2026 19:56
@toddtarsi

Copy link
Copy Markdown
Author

@joe-ayoub-segment - Updated the fields to support the current customer and gives a path forward for better field naming going forward. Also, regenerated types

@toddtarsi

Copy link
Copy Markdown
Author

@joe-ayoub-segment - Thank you for the review!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment on lines +97 to 105
if (
agentLeg.last_name === undefined ||
agentLeg.last_name === null ||
(agentLeg.last_name !== '' && agentLeg.last_name.trim() === '')
) {
throw new PayloadValidationError(
'agentLegs.last_name is required for every agent leg entry. Provide first_name and last_name, or enable assign_first_last_name_by_splitting_email and provide an agent_email that can be split. Single-token email local-parts derive an empty last_name.'
)
}
Comment on lines +11 to +13
function titleCase(value: string): string {
return value.charAt(0).toUpperCase() + value.slice(1)
}
Copilot AI review requested due to automatic review settings July 21, 2026 20:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment on lines +97 to 105
if (
agentLeg.last_name === undefined ||
agentLeg.last_name === null ||
(agentLeg.last_name !== '' && agentLeg.last_name.trim() === '')
) {
throw new PayloadValidationError(
'agentLegs.last_name is required for every agent leg entry. Provide first_name and last_name, or enable assign_first_last_name_by_splitting_email and provide an agent_email that can be split. Single-token email local-parts derive an empty last_name.'
)
}
@joe-ayoub-segment

Copy link
Copy Markdown
Contributor

Hi @toddtarsi ,

Apologies but I think you misunderstood me.

there should be no need to mark some of the fields as legacy. instead, you could keep the fields named as they are, but give them different labels and descriptions. then when you are building the JSON to send to your platform you compose the JSON in the new shape you need it in.

e.g.

Keep the field named first_name then when building your JSON do something like this (but more defensively):

{
  customer_first_name: first_name
}

Also, I think you should remove the default, as this field is a simple boolean toggle, so the customer will probably not map payload data to it:

      default: {
        '@path': '$.properties.assign_first_last_name_by_splitting_email'
      }

You could set the default to true or false if you like:

default: true

then the customer can change if if they want.

Copilot AI review requested due to automatic review settings August 7, 2026 13:27
@joe-ayoub-segment

Copy link
Copy Markdown
Contributor

Hi @toddtarsi just following up to see if this PR is still something you want to proceed with. If not I'll close it.
Best regards,
Joe

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

packages/destination-actions/src/destinations/voiceops/sendCallCompleted/index.ts:39

  • Derivation rules for last name are asymmetric with first name: '' is treated as “present” (so derivation won’t run), while whitespace-only triggers derivation. This means a mapped empty string (common when upstream fields are missing) will block email-based derivation even when enabled. Consider deriving when value is undefined | null or value.trim() === '' (including empty string), and rely on the post-derivation validation logic to allow '' specifically when it comes from single-token email local-parts.
function shouldDeriveLastName(value?: string): boolean {
  return value === undefined || value === null || (value !== '' && value.trim() === '')
}

packages/destination-actions/src/destinations/voiceops/sendCallCompleted/index.ts:21

  • email is already guarded (if (!email || !EMAIL_REGEX.test(email)) return {}), so the optional chaining here is redundant and slightly obscures the invariants. Simplifying to non-optional calls (and avoiding ?? [] if localPart is always a string) would make the flow easier to follow.
  const localPart = email?.split('@')[0]?.split('+')[0]
  const nameParts = localPart?.split(/[^a-zA-Z0-9]+/).filter(Boolean) ?? []

Comment on lines +97 to 105
if (
agentLeg.last_name === undefined ||
agentLeg.last_name === null ||
(agentLeg.last_name !== '' && agentLeg.last_name.trim() === '')
) {
throw new PayloadValidationError(
'agentLegs.last_name is required for every agent leg entry. Provide first_name and last_name, or enable assign_first_last_name_by_splitting_email and provide an agent_email that can be split. Single-token email local-parts derive an empty last_name.'
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants