Issue with CC Email Population in SoD Policy Violation Reminder Notifications

Which IIQ version are you inquiring about?

8.5P1

Please share any images or screenshots, if relevant.

Share all details about your problem, including any error messages you may have received.

Dear Team,

We are currently using Role SoD policies. As part of the violation detection process, we have configured reminder notifications to be sent to policy violation owners. Additionally, we would like to include the impacted users (identities) in the CC field of the same email.

However, we are facing an issue where:

  1. Violation owners are receiving the reminder notifications correctly.
  2. The users are not receiving the emails in CC.
  3. The CC variable ($pv.identity.email) is not resolving and is instead being printed as-is.

Current Setup

We configured the CC field as:
cc=“$pv.identity.email”

The $pv object is expected to be populated via a custom rule defined at the email template level. The rule execution snippet is as follows:

#set($spctx=$spTools.class.forName(“sailpoint.api.SailPointFactory”).getMethod(“getFactory”, null).invoke(null, null).getCurrentContext())

#set($rule = $spctx.getObjectByName($spTools.class.forName(“sailpoint.object.Rule”), “POlicy Violation Work Group Members”))

#set($ruleArgs=$spTools.class.forName(“java.util.HashMap”).newInstance())

#set($dummy = $ruleArgs.put(“policyViolation”,$workItem.targetId))

#set($ruleOutput = $spctx.runRule($rule, $ruleArgs))

## Extract returned data

#set($pv = $ruleOutput.get(“violation”)) ## PolicyViolation object

#set($emails = $ruleOutput.get(“emails”))

#set($displayNames = $ruleOutput.get(“displayNames”))

#set($leftRoleDisplayName = $ruleOutput.get(“leftRoleDisplayName”))

#set($rightRoleDisplayName = $ruleOutput.get(“rightRoleDisplayName”))

Observed Problem

It appears that the email template variables are resolved before the rule is executed. As a result, $pv is not yet populated when the CC field is evaluated, leading to null or unresolved values.

We also attempted to use the out-of-the-box $workItem object, but since $workItem.targetId only contains an ID (and not the full object), accessing $workItem.targetId.identity.email does not work.

Question

Is there a recommended approach to:

  • Dynamically include the policy violation identity’s email address in the CC field of reminder notifications?
  • Or properly resolve variables from a rule so they can be used in the email template header (CC field)?

We can force SailPoint to instantiate the context and look up the full PolicyViolation object dynamically within the CC field calculation string itself. Velocity allows inline logic execution in headers if it is formatted on a single line.

cc=“#set($spctx=$spTools.class.forName(‘sailpoint.api.SailPointFactory’).getMethod(‘getFactory’, null).invoke(null, null).getCurrentContext())#set($pvObj=$spctx.getObjectById($spTools.class.forName(‘sailpoint.object.PolicyViolation’), $workItem.targetId))$!{pvObj.identity.email}”

Can you please try this one

Hello Jagan,
thanks for the reply!
I was trying to save the template by adding belowline and it’s not letting me to do it.

cc=“#set($spctx=$spTools.class.forName(‘sailpoint.api.SailPointFactory’).getMethod(‘getFactory’, null).invoke(null, null).getCurrentContext())#set($pvObj=$spctx.getObjectById($spTools.class.forName(‘sailpoint.object.PolicyViolation’), $workItem.targetId))$!{pvObj.identity.email}”

FYI - I’ve tried from the debug pages and also from the impoting it.

org.hibernate.exception.GenericJDBCException: could not execute statement

@harishabn Could you please share your email template xml?

@harishabn , Please share the xml file

Hello @neel193 ,

PFB my email template details

```

<?xml version='1.0' encoding='UTF-8'?> #set($spctx=$spTools.class.forName("sailpoint.api.SailPointFactory").getMethod("getFactory", null).invoke(null, null).getCurrentContext()) #set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "Rule-SOD-WorkGroup-Members")) #set( $ruleArgs=$spTools.class.forName("java.util.HashMap").newInstance() )
#set( $dummy = $ruleArgs.put( "policyViolation",$workItem.targetId) )

#set( $ruleOutput = $spctx.runRule( $rule, $ruleArgs ) )


## Extract returned data
#set($pv = $ruleOutput.get("violation"))   ## PolicyViolation object
#set($emails = $ruleOutput.get("emails"))  ## List of email addresses
#set($displayNames = $ruleOutput.get("displayNames"))  ## List of displayNames
#set($leftRoleDisplayName = $ruleOutput.get("leftRoleDisplayName"))  ## Left Bundle
#set($rightRoleDisplayName = $ruleOutput.get("rightRoleDisplayName"))  ## Right Bundle


&lt;html>
&lt;body style="font-family:Arial,Helvetica,sans-serif;font-size:14px;">

&lt;p>Dear &lt;b>$pv.getOwner().getDisplayableName()&lt;/b>,&lt;/p>

&lt;p>
You are receiving this email because you are designated as a Segregation of Duties (SoD) policy remediator or notification recipient. To take action, please click on the following link: &lt;a href="https://sailpoint/identityiq/policyViolation/policyViolation.jsf#/policyViolationsAll">
Take action in SailPoint &lt;/a>
&lt;/p>

&lt;!-- Policy Violation Details (Simple Format) -->
&lt;table border="1" cellpadding="5" cellspacing="0" 
style="border-collapse:collapse; font-family:Segoe UI, Arial, sans-serif; font-size:14px;">

&lt;tr>
&lt;td>&lt;b>Employee:&lt;/b>&lt;/td>
&lt;td>$pv.getIdentity().getDisplayableName() ($pv.getIdentity().getName())&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Policy Name:&lt;/b>&lt;/td>
&lt;td>$pv.getPolicyName()&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Line Manager:&lt;/b>&lt;/td>
&lt;td>$pv.getIdentity().getManager().getDisplayableName() ($workItem.getOwner().getName())&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Policy Owners:&lt;/b>&lt;/td>
&lt;td>
#if($displayNames &amp;&amp; $displayNames.size() > 0)
#foreach($d in $displayNames)
$d &lt;br/>
#end
#else
&lt;i>No owner emails found&lt;/i>
#end
&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Conflicting Access:&lt;/b>&lt;/td>
&lt;td>
$leftRoleDisplayName&lt;br/>
$rightRoleDisplayName
&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Detection Since:&lt;/b>&lt;/td>
&lt;td>${workItem.getCreated().toString().substring(0,10)} ${workItem.getCreated().toString().substring(24)}&lt;/td>
&lt;/tr>

&lt;tr>
&lt;td>&lt;b>Policy Violation:&lt;/b>&lt;/td>
&lt;td>
&lt;a href="https://sailpoint/identityiq/policyViolation/policyViolation.jsf#/policyViolationsAll">
View in SailPoint
&lt;/a>
&lt;/td>
&lt;/tr>

&lt;/table>

&lt;p>

&lt;span style="background:#FFF3CD; padding:2px 4px; font-weight:700; color:#856404;">
Important:
&lt;/span>
If no action is taken within 28 days from the violation date (${pv.getCreated().toString().substring(0,10)} ${pv.getCreated().toString().substring(24)}), the system will automatically revoke both roles.
&lt;/p>

&lt;p>
&lt;i>
For changes, clarifications and questions regarding this policy, reach out to:
&lt;b>
#if($emails &amp;&amp; $emails.size() > 0)
#foreach($e in $emails)
$e#if($foreach.hasNext), #end
#end
#else
Policy Owners
#end
&lt;/b>
&lt;/i>
&lt;/p>

&lt;p>
&lt;i>
For technical assistance, contact the Platform Security Team at 
&lt;a href="mailto:dl-dummy">
DL-DUMMY
&lt;/a>.
&lt;/i>
&lt;/p>

&lt;p>Kind regards,&lt;br/>
SailPoint Team&lt;br/>
&lt;i>
This e-mail has been sent automatically by SailPoint
&lt;/i>
&lt;/p>

&lt;/body>
&lt;/html>
Email Template for reminding work item owners that their work items are overdue. NOTE: In addition to the declared arguments, all entries from the work item attributes map are also available by name in the template. The WorkItem object. The name of the WorkItem. The Certification object. The name of the Certification. The name of the Identity that owns the work item. The identity object that represents the creator of the work item. The number of this reminder, starting from 1. Passed as a String. The number of reminders remaining after this one. Passed as a String. The former due date, passed as a java.util.Date. The new due date, passed as a java.util.Date. The current date, passed as a java.util.Date. The date the work item was created, passed as a java.util.Date. The optional date the work item completely expires, passed as a java.util.Date. 🔔 [Action Required] – $ordinalNumReminders Reminder Segregation of Duties Policy Violations Identified

@harishabn This error is coming as it might be crossing the limit of 256 characters in any of the column attribute like subject, or anyother indexed attribute. Could you please share the complete xml, not just the html part.

Hello @neel193 ,

Please check the below full emial template details.

<?xml version='1.0' encoding='UTF-8'?>
<EmailTemplate bcc="dl-dummy.com" cc="$pv.identity.email" name="EmailTemplate-SOD-Reminder">
  <Body>
    #set($spctx=$spTools.class.forName("sailpoint.api.SailPointFactory").getMethod("getFactory", null).invoke(null, null).getCurrentContext()) 
    #set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "Rule-SOD-WorkGroup-Members"))
    #set( $ruleArgs=$spTools.class.forName("java.util.HashMap").newInstance() )

#set( $dummy = $ruleArgs.put( "policyViolation",$workItem.targetId) )

#set( $ruleOutput = $spctx.runRule( $rule, $ruleArgs ) )


## Extract returned data
#set($pv = $ruleOutput.get("violation"))   ## PolicyViolation object
#set($emails = $ruleOutput.get("emails"))  ## List of email addresses
#set($displayNames = $ruleOutput.get("displayNames"))  ## List of displayNames
#set($leftRoleDisplayName = $ruleOutput.get("leftRoleDisplayName"))  ## Left Bundle
#set($rightRoleDisplayName = $ruleOutput.get("rightRoleDisplayName"))  ## Right Bundle


<html>
<body style="font-family:Arial,Helvetica,sans-serif;font-size:14px;">

<p>Dear <b>$pv.getOwner().getDisplayableName()</b>,</p>

<p>
You are receiving this email because you are designated as a Segregation of Duties (SoD) policy remediator or notification recipient. To take action, please click on the following link: <a href="https://sailpoint/identityiq/policyViolation/policyViolation.jsf#/policyViolationsAll">
Take action in SailPoint </a>
</p>

<!-- Policy Violation Details (Simple Format) -->
<table border="1" cellpadding="5" cellspacing="0" 
style="border-collapse:collapse; font-family:Segoe UI, Arial, sans-serif; font-size:14px;">

<tr>
<td><b>Employee:</b></td>
<td>$pv.getIdentity().getDisplayableName() ($pv.getIdentity().getName())</td>
</tr>

<tr>
<td><b>Policy Name:</b></td>
<td>$pv.getPolicyName()</td>
</tr>

<tr>
<td><b>Line Manager:</b></td>
<td>$pv.getIdentity().getManager().getDisplayableName() ($workItem.getOwner().getName())</td>
</tr>

<tr>
<td><b>Policy Owners:</b></td>
<td>
#if($displayNames &amp;&amp; $displayNames.size() > 0)
#foreach($d in $displayNames)
$d <br/>
#end
#else
<i>No owner emails found</i>
#end
</td>
</tr>

<tr>
<td><b>Conflicting Access:</b></td>
<td>
$leftRoleDisplayName<br/>
$rightRoleDisplayName
</td>
</tr>

<tr>
<td><b>Detection Since:</b></td>
<td>${workItem.getCreated().toString().substring(0,10)} ${workItem.getCreated().toString().substring(24)}</td>
</tr>

<tr>
<td><b>Policy Violation:</b></td>
<td>
<a href="https://sailpoint/identityiq/policyViolation/policyViolation.jsf#/policyViolationsAll">
View in SailPoint
</a>
</td>
</tr>

</table>

<p>

<span style="background:#FFF3CD; padding:2px 4px; font-weight:700; color:#856404;">
Important:
</span>
If no action is taken within 28 days from the violation date (${pv.getCreated().toString().substring(0,10)} ${pv.getCreated().toString().substring(24)}), the system will automatically revoke both roles.
</p>

<p>
<i>
For changes, clarifications and questions regarding this policy, reach out to:
<b>
#if($emails &amp;&amp; $emails.size() > 0)
#foreach($e in $emails)
$e#if($foreach.hasNext), #end
#end
#else
Policy Owners
#end
</b>
</i>
</p>

<p>
<i>
For technical assistance, contact the Platform Security Team at 
<a href="mailto:dl-dummy">
DL-DUMMY
</a>.
</i>
</p>

<p>Kind regards,<br/>
SailPoint Team<br/>
<i>
This e-mail has been sent automatically by SailPoint
</i>
</p>

</body>
</html>

</Body>
  <Description>
    Email Template for reminding work item owners that their work
    items are overdue.
    NOTE: In addition to the declared arguments, all entries 
    from the work item attributes map are also available by name in the template.
  </Description>
  <Signature>
    <Inputs>
      <Argument name="workItem" type="WorkItem">
        <Description>The WorkItem object.</Description>
      </Argument>
      <Argument name="workItemName" type="string">
        <Description>The name of the WorkItem.</Description>
      </Argument>
      <Argument name="certification" type="Certification">
        <Description>The Certification object.</Description>
      </Argument>
      <Argument name="certificationName" type="string">
        <Description>The name of the Certification.</Description>
      </Argument>
      <Argument name="ownerName" type="string">
        <Description>The name of the Identity that owns the work item.</Description>
      </Argument>
      <Argument name="requester" type="Identity">
        <Description>The identity object that represents the creator of the work item.</Description>
      </Argument>
      <Argument name="ordinalNumReminders" type="string">
        <Description>The number of this reminder, starting from 1.  Passed as a String.</Description>
      </Argument>
      <Argument name="remindersRemaining" type="string">
        <Description>The number of reminders remaining after this one.  Passed as a String.</Description>
      </Argument>
      <Argument name="oldDueDate" type="date">
        <Description>The former due date, passed as a java.util.Date.</Description>
      </Argument>
      <Argument name="newDueDate" type="date">
        <Description>The new due date, passed as a java.util.Date.</Description>
      </Argument>
      <Argument name="nowDate" type="date">
        <Description>The current date, passed as a java.util.Date.</Description>
      </Argument>
      <Argument name="created" type="date">
        <Description>The date the work item was created, passed as a java.util.Date.</Description>
      </Argument>
      <Argument name="expiration" type="date">
        <Description>The optional date the work item completely expires, passed as a java.util.Date.</Description>
      </Argument>
    </Inputs>
  </Signature>
  <Subject>🔔 [Action Required] – $ordinalNumReminders Reminder Segregation of Duties Policy Violations Identified</Subject>
</EmailTemplate>

Yes, it’s 255 characters issue.

Caused by: oracle.jdbc.OracleDatabaseException: ORA-12899: value too large for column “IDENTITYIQ”.“SPT_EMAIL_TEMPLATE”.“CC_ADDRESS” (actual: 275, maximum: 255)

@harishabn can you try reducing the characters to less than 255?

I was trying to save with the below value suggested by JaganMohan.

cc=“#set($spctx=$spTools.class.forName(‘sailpoint.api.SailPointFactory’).getMethod(‘getFactory’, null).invoke(null, null).getCurrentContext())#set($pvObj=$spctx.getObjectById($spTools.class.forName(‘sailpoint.object.PolicyViolation’), $workItem.targetId))$!{pvObj.identity.email}”

This one is 280 characters, and will not be saved.

Yeah, We expanded the column length to 300 characters and able to save the value. When we start testing, the value is not populating and it’s throwing below warning. Any idea?

seeing this warning , 2026-07-01T12:26:23,992 WARN https-jsse-nio-18443-exec-186 org.apache.velocity.introspection:68 - Cannot retrieve method forName from object of class java.lang.Class due to security restrictions

I don’t think this method exists or can be used there.

Let me try this and share you the value.