TL;DR
Pattern 1 — Hierarchical plan approval: Plan submitted → manager approves → director approves (for high-value plans) → notification. Edge cases: circular manager relationships, missing hierarchy, manager out of office.
Pattern 2 — Quota dispute resolution: Participant disputes quota → analyst reviews → approves and updates quota, or rejects with explanation. Challenge: quota update must write back to the system via REST API or Groovy.
Pattern 3 — Exception incentive approval: Outlier flag detected → compensation analyst reviews → approves as-is or overrides with correction. Challenge: defining "outlier" per plan type is difficult.
Pattern 4 — Automated statement release: Calculation approved → check approvals complete → generate statements → send notification to all participants. Pure automation, no human gate.

Introduction: Why Patterns Matter

In every IM implementation, the same business scenarios appear across different organizations. A compensation team needs to approve plans before they go live. Participants need to dispute quotas or commission amounts. Outlier calculations need human review. Statements need to be automatically published when ready. These scenarios are so common that they form patterns — reusable workflow configurations that you adapt to your specific business context.

Understanding patterns accelerates your design phase. Instead of building workflows from scratch, you recognize the pattern, apply the standard configuration with your customizations, and move on. Patterns also provide guidance for edge cases — the most common failure modes are well-documented in each pattern.

Pattern 1: Hierarchical Plan Approval

Overview

A compensation plan is submitted for approval. The workflow routes it through the participant's reporting hierarchy: manager → director → VP (if amount exceeds threshold) → final approval. At each step, the approver can approve, reject, or return for revision. Once all approvals are obtained, the plan is activated and a notification is sent to the participant.

This is the most common pattern in IM. Almost every implementation requires it. Variations include the number of approval levels, the value thresholds that trigger additional approvers, and the escalation rules.

Configuration Walkthrough

Trigger Event: plan.submitted_for_approval

Conditions:

  • Only trigger for plan.type = "INCENTIVE" (exclude admin plans).
  • Only trigger if plan.status = "SUBMITTED".

Step 1 — Manager Review:

  • Assignee: participant.manager.id
  • SLA: 3 business days
  • Task subject: "Plan Approval: ${plan.name} ($${formatCurrency(plan.annualValue)})"
  • Task body: Include plan details and request approval/rejection.
  • Escalation: After 3 days, escalate to participant.manager.manager.id (director).
  • On Approve: Advance to Step 2 (value check).
  • On Reject: End workflow. Notify participant that manager rejected the plan.

Step 2 — Conditional Value Check (Groovy Script):

// Check plan value to determine if director approval is needed
if (plan?.annualValue >= 500000) {
  workflow.requiresDirectorApproval = true
  return "DIRECTOR_APPROVAL"
} else {
  workflow.requiresDirectorApproval = false
  return "FINAL_NOTIFICATION"
}

Step 3 — Director Approval (conditional):

  • Only execute if workflow.requiresDirectorApproval = true.
  • Assignee: participant.manager.manager.id (director).
  • SLA: 5 business days.
  • Escalation: After 5 days, escalate to participant.manager.manager.manager.id (VP).
  • On Approve: Advance to Step 4 (notification).
  • On Reject: Return to Step 1 (manager revises plan).

Step 4 — Final Notification:

  • Type: Notification.
  • Recipients: participant.email, participant.manager.email.
  • Subject: "Your Plan Has Been Approved: ${plan.name}"
  • Body: Include plan details and link to SuccessFactors.
  • On Complete: Workflow outcome = "APPROVED". Return to IM. IM updates plan.status = "ACTIVE".

Edge Cases and Solutions

Edge Case 1: Participant Has No Manager

Symptom: Workflow hangs in Step 1 with no approver assigned.

Cause: participant.manager.id is null.

Solution: Implement fallback logic in assignee rule. If manager is null, escalate to the business unit head or compensation team.

// Groovy assignee rule with fallback
def approver = null

if (participant?.manager?.id) {
  approver = participant.manager.id
} else if (participant?.businessUnit?.head?.id) {
  approver = participant.businessUnit.head.id
} else {
  approver = "compensation-team@company.com"
}

return approver

Edge Case 2: Manager Is Also a Participant (Circular Relationship)

Symptom: Plan approval routes to the participant themselves.

Cause: The participant's manager field points to a participant who is also in the plan.

Solution: Detect circular relationships and skip to the next level in hierarchy.

// Detect circular relationship
def approver = null

if (participant?.manager?.id && participant.manager.id != participant.id) {
  approver = participant.manager.id
} else if (participant?.manager?.manager?.id && participant.manager.manager.id != participant.id) {
  approver = participant.manager.manager.id
} else {
  approver = "compensation-team@company.com"
}

return approver

Edge Case 3: Manager Is Terminated or Inactive

Symptom: Approval task is assigned to a terminated manager who never responds.

Cause: Manager's user account is inactive but still listed as participant's manager in hierarchy data.

Solution: Validate manager's active status before assignment. Escalate if manager is inactive.

// Check manager status
def approver = null

if (participant?.manager?.id && participant.manager.status == "ACTIVE") {
  approver = participant.manager.id
} else if (participant?.manager?.manager?.id && participant.manager.manager.status == "ACTIVE") {
  approver = participant.manager.manager.id
} else {
  approver = "compensation-team@company.com"
}

return approver

Edge Case 4: Manager Out of Office During Approval Window

Symptom: Manager doesn't respond within SLA, escalation triggers auto-approval or escalation to next level.

Solution: This is why escalation rules exist. Set reasonable SLA timers (3–5 business days) and escalation rules. A 5-day SLA gives the manager time to return from time off. Escalation rules are automatic failsafes.

Most Common Failure Mode

The most common failure in hierarchical plan approval is missing manager data. A participant has no manager field, or the manager field points to an inactive user, or the manager's manager is missing. The workflow hangs waiting for an approver that doesn't exist. This is discovered during UAT or in production, creating a support issue. Prevention: validate participant hierarchy completeness before go-live using a data audit query.

Pattern 2: Quota Dispute Resolution

Overview

A participant disputes their quota (either the value seems wrong, or the territory assignment is incorrect). The dispute is submitted through IM's dispute interface. A workflow routes the dispute to a compensation analyst for investigation. The analyst reviews the dispute and either approves an adjustment (quota is updated) or rejects the dispute (participant receives explanation). If the adjustment is large (>20% change), escalate to the finance director for additional review.

Configuration Walkthrough

Trigger Event: dispute.submitted

Conditions: None (all disputes should be processed).

Step 1 — Send Confirmation Notification:

  • Notify participant that their dispute was received and is being reviewed.
  • Provide dispute ID and expected resolution timeline (5 business days).

Step 2 — Route to Analyst (Value-Based):

// If dispute is for large adjustment, escalate to finance director
// Otherwise route to compensation analyst

def requestedAdjustment = dispute.newQuota - dispute.currentQuota
def percentChange = requestedAdjustment / dispute.currentQuota

if (percentChange > 0.20 || requestedAdjustment > 100000) {
  workflow.requiresFinanceReview = true
  return "FINANCE_DIRECTOR"
} else {
  workflow.requiresFinanceReview = false
  return "ANALYST"
}

Step 3a — Analyst Review (standard disputes):

  • Assignee: compensation-analyst@company.com (or role-based lookup).
  • SLA: 5 business days.
  • Task body: Include current quota, requested adjustment, and justification from participant.
  • On Approve: Advance to Step 4 (update quota).
  • On Reject: Advance to Step 5 (send rejection notification).

Step 3b — Director Review (large disputes, conditional):

  • Only execute if workflow.requiresFinanceReview = true.
  • Assignee: finance-director@company.com.
  • SLA: 3 business days.
  • On Approve: Advance to Step 4 (update quota).
  • On Reject: Advance to Step 5 (send rejection notification).

Step 4 — Update Quota (Automation):

This is the critical step. The quota update must be written back to IM. Two approaches:

Approach 1: REST API Call

// Call IM REST API to update participant quota
import groovy.json.JsonOutput

def updatePayload = [
  participantId: participant.id,
  territory: dispute.territory,
  newQuota: dispute.newQuota,
  effectiveDate: dispute.effectiveDate,
  reason: "Dispute adjustment approved - " + workflow.approverComment
]

def apiUrl = "https://successfactors.com/api/v1/participants/${participant.id}/quota"
def response = httpClient.put(
  url: apiUrl,
  headers: [
    "Authorization": "Bearer " + apiToken,
    "Content-Type": "application/json"
  ],
  body: JsonOutput.toJson(updatePayload)
)

if (response.status >= 400) {
  log.error("Quota update failed: " + response.body)
  workflow.quotaUpdateStatus = "FAILED"
  return false
} else {
  log.info("Quota updated for participant " + participant.id)
  workflow.quotaUpdateStatus = "SUCCESS"
  return true
}

Approach 2: Groovy Direct Update (if IM APIs allow)

// Direct update via IM object API
try {
  participant.quota = dispute.newQuota
  participant.territory = dispute.territory
  participant.quotaEffectiveDate = dispute.effectiveDate
  participant.save()

  log.info("Quota updated successfully for " + participant.id)
  return true
} catch (Exception e) {
  log.error("Quota update failed: " + e.message)
  return false
}

Step 5 — Send Resolution Notification:

  • If approved: "Your dispute has been approved. Your quota has been updated to $${newQuota}."
  • If rejected: "Your dispute was reviewed and rejected. Reason: ${rejectionReason}."

Edge Cases and Solutions

Edge Case 1: API Failure When Updating Quota

Symptom: Dispute is approved, quota update API call fails, workflow completes successfully but quota is not updated. Participant and system are out of sync.

Solution: Check API response status. If status >= 400, retry or escalate to manual review. Never mark the workflow as approved if the quota update fails.

Edge Case 2: Duplicate Disputes

Symptom: Participant submits the same dispute twice, both are approved, quota is updated twice.

Solution: Add a condition to check if a dispute for the same participant and territory was already approved within the last N days. Skip the new dispute if a duplicate is found.

Edge Case 3: Quota Changed Between Dispute Submission and Approval

Symptom: Participant disputes quota of $100K. Admin updates quota to $120K. Dispute is approved for the original $100K. Quota ends up at wrong value.

Solution: When updating quota, check if the current quota in IM matches the quota at the time the dispute was filed. If not, escalate to manual review.

Most Common Failure Mode

The most common failure is API integration: the REST call to update quota fails due to network issues, API timeouts, or authentication failures. The workflow completes but the quota is never updated. Prevention: implement retry logic with exponential backoff, and always check the API response status before marking the workflow as successful.

Pattern 3: Exception Incentive Approval

Overview

A calculation run completes. The system detects outlier results (a participant's commission is 3x the role average, or quota achievement is >200%). These outliers are flagged for manual review. A workflow routes the exception to a compensation analyst. The analyst can approve the result as-is, or override with a corrected amount. Once approved, the exception is resolved and the statement is published.

This pattern is challenging because "outlier" is subjective and plan-specific. A 200% quota achievement might be normal for some plan types and exceptional for others.

Configuration Walkthrough

Trigger Event: calculation.completed OR exception.detected (depending on whether IM flags exceptions during calculation or a post-calculation workflow flags them).

Conditions:

  • Only trigger if an exception flag is set (isOutlier = true).
  • Only trigger if exception severity is HIGH (value-based thresholds defined per plan type).

Step 1 — Groovy Analysis: Determine Exception Type and Severity

// Analyze the exception to determine type and severity
import groovy.json.JsonSlurper

def exceptionType = null
def severity = null
def analysisDetails = [:]

// Get peer statistics for the participant's role
def rolePeers = getRoleStatistics(participant.role, plan.period)

// Check for commission outliers
if (statement.commissionAmount > rolePeers.mean + (3 * rolePeers.stdDev)) {
  exceptionType = "HIGH_COMMISSION"
  severity = "HIGH"
  analysisDetails.mean = rolePeers.mean
  analysisDetails.stdDev = rolePeers.stdDev
  analysisDetails.percentileRank = calculatePercentile(statement.commissionAmount, rolePeers)
}

// Check for achievement outliers
if (statement.quotaAchievement > 200) {
  exceptionType = "EXTREME_ACHIEVEMENT"
  severity = "HIGH"
}

// Check for negative outliers (commission much lower than expected)
if (statement.commissionAmount < rolePeers.mean - (2 * rolePeers.stdDev)) {
  exceptionType = "LOW_COMMISSION"
  severity = "MEDIUM"
}

workflow.exceptionType = exceptionType
workflow.severity = severity
workflow.analysisDetails = analysisDetails

return severity

Step 2 — Route to Analyst:

  • Assignee: compensation-analyst@company.com (role-based).
  • SLA: 3 business days.
  • Task body: Include exception analysis details (comparison to role average, percentile rank, etc.) and request approval or override.
  • On Approve: Advance to Step 3 (publish statement as-is).
  • On Override: Advance to Step 4 (analyst enters corrected amount, then publish).

Step 3 — Publish Statement As-Is:

  • Type: Automation (Groovy script).
  • Action: Update statement.status = "PUBLISHED".
  • Advance to Step 5 (notification).

Step 4 — Apply Manual Override (conditional):

  • Type: Automation (Groovy script).
  • Action: Update statement.commissionAmount = workflow.overrideAmount. Log the override reason and analyst comment.
  • Advance to Step 5 (notification).

Step 5 — Send Notification to Participant:

  • Subject: "Your Statement Has Been Reviewed and Is Now Available"
  • Body: Commission amount, achievement %, link to view details in SuccessFactors.

Edge Cases and Solutions

Edge Case 1: Defining "Outlier" Per Plan Type

Symptom: Quota achievement of 150% is normal in one plan type (aggressive growth markets) but exceptional in another (stable accounts).

Solution: Store outlier thresholds per plan type in a configuration table. When flagging exceptions, look up the plan-specific thresholds. Make thresholds configurable so they can be adjusted based on business feedback.

Edge Case 2: Multiple Exceptions for Same Participant

Symptom: A participant has both a HIGH_COMMISSION and an EXTREME_ACHIEVEMENT exception in the same calculation.

Solution: Combine exceptions into a single workflow instance. Route to analyst once, with multiple exception types documented. Analyst approves all exceptions together.

Edge Case 3: Analyst Override Creates a New Outlier

Symptom: Analyst overrides exception from $10K to $5K, but $5K is now a low-commission outlier.

Solution: When analyst enters an override amount, validate it against outlier thresholds. If the override creates a new outlier, flag it for secondary review.

Most Common Failure Mode

The most common failure is under-defining "outlier". An exception rule seems clear in the business context ("flag commissions > 3x role average") but doesn't account for plan-type variations or seasonal patterns. The workflow flags too many normal exceptions, or misses real exceptions. Prevention: define outlier rules in collaboration with the compensation team and iterate based on UAT feedback.

Pattern 4: Automated Statement Release

Overview

A calculation run completes and all statements are generated. All exceptions (if any) have been approved. The system is ready to publish statements to participants. A workflow automatically checks that all approvals are complete, publishes all statements, and sends a notification to each participant. This is pure automation — no human gate.

This pattern is lower-risk than the others because it's fully deterministic. It either succeeds or fails, but there's no judgment call involved.

Configuration Walkthrough

Trigger Event: calculation.completed

Conditions:

  • Only trigger if calculation.status = "COMPLETED".
  • Only trigger if all exceptions have been approved (check exception queue for pending items).

Step 1 — Validate Calculation State (Groovy):

// Validate that the calculation is ready for statement release
// Check: no pending approvals, no errors, all participants processed

def pendingExceptionCount = getPendingExceptionCount(calculation.id)
def failedParticipantCount = getFailedParticipantCount(calculation.id)
def totalParticipantCount = getParticipantCount(calculation.period)
def processedCount = totalParticipantCount - failedParticipantCount

if (pendingExceptionCount > 0) {
  log.warn("Cannot release: " + pendingExceptionCount + " exceptions pending")
  return false
}

if (failedParticipantCount > 0 && (failedParticipantCount / totalParticipantCount) > 0.05) {
  log.warn("Cannot release: > 5% of participants failed")
  return false
}

log.info("Validation passed: " + processedCount + " participants ready")
return true

Step 2 — Publish Statements (Automation):

// Publish all statements for the period
// Update statement.status = "PUBLISHED" and set publishedDate = now()

def publishedCount = 0
def failedCount = 0

def statements = getUnpublishedStatements(calculation.period)
statements.each { statement ->
  try {
    statement.status = "PUBLISHED"
    statement.publishedDate = new Date()
    statement.save()
    publishedCount++
  } catch (Exception e) {
    log.error("Failed to publish statement for " + statement.participantId + ": " + e.message)
    failedCount++
  }
}

log.info("Published " + publishedCount + " statements, failed: " + failedCount)
workflow.publishedCount = publishedCount
workflow.failedCount = failedCount

return failedCount == 0

Step 3 — Send Notifications to All Participants:

  • Type: Notification (batch).
  • Recipients: All participants with published statements in the period.
  • Subject: "Your Q2 2026 Compensation Statement Is Ready"
  • Body: Commission amount, achievement %, link to view in SuccessFactors, compensation team contact.
  • Channel: Email + push notification in SuccessFactors.

Step 4 — Log Release Event (Automation):

  • Type: Groovy script.
  • Action: Write audit log entry: "Period Q2 2026 statements released. Count: [publishedCount]. Published by: [workflow.initiatedBy]. Time: [timestamp]."
  • Workflow outcome: "COMPLETED".

Edge Cases and Solutions

Edge Case 1: Partial Failure (Some Statements Publish, Others Fail)

Symptom: 95% of statements publish successfully, but 5% fail due to data issues. Participant's don't know if their statement is ready.

Solution: The batch automation should not fail entirely. Log failed participants separately and notify the admin. Send statements only to participants who published successfully. Follow up with failed participants after issues are resolved.

Edge Case 2: Notification Delivery Failures

Symptom: Statement is published but email delivery fails for a participant. Participant doesn't know their statement is ready.

Solution: Implement retry logic for email delivery. If email fails, queue for retry (e.g., retry after 1 hour, then 4 hours, then next day). Log all delivery failures.

Edge Case 3: Too Many Pending Exceptions

Symptom: 200 exceptions are pending approval, and the release workflow is waiting. Participants don't get statements until all exceptions are approved.

Solution: This is not an edge case — it's a design decision. You can choose to: (A) require all exceptions to be approved before any statements release (safest), or (B) release statements for non-exception participants and hold exception participants' statements separately. Option B is more user-friendly but more complex.

Most Common Failure Mode

The most common failure is incomplete validation. The workflow releases statements but the calculation wasn't actually complete (some participants weren't processed due to data errors). Participants get statements for incomplete data. Prevention: make validation strict. Check not only that the calculation is marked complete, but also that the expected participant count matches the processed count.

Workflow Pattern Comparison Table

Pattern Trigger Complexity Human Gate Most Common Failure
Hierarchical Approval plan.submitted Medium (multi-step approval, escalation) Yes (multiple approvers) Missing manager data; workflow hangs
Quota Dispute dispute.submitted High (API integration, conditional routing) Yes (analyst review) Quota update API fails; quota not updated
Exception Approval exception.detected High (statistical analysis, override logic) Yes (analyst review) Poor outlier definition; too many false positives
Automated Release calculation.completed Medium (batch operations, logging) No (pure automation) Incomplete validation; releases partial data

Cross-Pattern Interactions

In a complete IM implementation, these patterns interact:

  • Plan submitted → Hierarchical Approval workflow executes → Plan approved.
  • Plan activated → Calculation triggered.
  • Calculation completes → Exception Approval workflow identifies outliers → Analyst approves exceptions.
  • Exceptions approved → Automated Statement Release workflow executes → Statements published.
  • Statement published → Participant reads statement, disputes quota if needed → Quota Dispute workflow executes → Analyst approves adjustment → Quota updated.

Design your workflows to work together. Data from one workflow is often input to the next. For example, the Automated Statement Release workflow needs to know which exceptions were approved (output from Exception Approval workflow).

Key Takeaways

  • Hierarchical Plan Approval is the most common pattern. Its main failure mode is missing hierarchy data. Implement robust fallback logic and validate hierarchy completeness before go-live.
  • Quota Dispute Resolution requires API integration to update quota. The main failure mode is API failure. Implement error handling and retry logic.
  • Exception Incentive Approval is complex because "outlier" is subjective. Collaborate with the compensation team to define thresholds per plan type.
  • Automated Statement Release is the lowest-risk pattern because it's fully deterministic. Make validation strict to prevent releasing incomplete data.
  • Workflows interact. Design them to work together and to share data between workflow instances.