Clear Cutover Get a quote

Epicor Kinetic · BPM and automation

Epicor BPM email alerts: Method Directives vs. Data Directives, without duplicate emails

Applies to: Epicor Kinetic and Epicor ERP 10.2 BPM (Business Process Management). Directive designer widgets, method parameters, and mail server integrations vary across minor releases and hosting models (SaaS Cloud vs. On-Premises). Always validate automated email notifications in a dedicated Pilot or Staging environment prior to production deployment.

Short answer

Automated email alerts in Epicor BPM must balance two conflicting technical requirements: detecting the exact change while old and new values exist in memory, and dispatching the email only after the database transaction has committed to disk.

Following standard Epicor BPM architecture, adhere to these fundamental principles:

  1. Send Email is Prohibited in In-Transaction Data Directives: By design, the Send Email action widget is not offered inside In-Transaction directives. In-Transaction logic executes synchronously inside the SQL TransactionScope; firing network calls inside an uncommitted database transaction introduces catastrophic table locks and risks sending emails for rolled-back transactions.
  2. Dispatch Post-Commit: Always send emails from a Post-Processing Method Directive or a Standard Data Directive. Both run after records are safely committed to the database.
  3. The Two-Directive Design Pattern:
    • For Method Directives: Detect the field condition in Pre-Processing (where before and after row buffers coexist), invoke the Enable Post Directive widget, and send the email from Post-Processing.
    • For Data Directives: Detect the field condition in an In-Transaction directive, invoke the Enable Standard Directive widget, and send the email from the Standard directive.
  4. Why "Field Has Been Changed" Fails Post-Commit: In Post-Processing and Standard directives, the database write is finalized. The prior memory buffer is discarded and row modification flags (RowMod) are typically cleared, causing change-detection conditions to evaluate to false.

Key facts

  • Database Transaction Boundaries: In-Transaction Data Directives run inside SQL Server's active transaction. Standard Data Directives and Post-Processing Method Directives run after the database transaction commits.
  • The Two-Directive Pattern is the Industry Standard: Because change detection requires before/after buffers and notification requires committed data, robust BPM alerts almost always decouple detection (Directive 1) from notification (Directive 2).
  • Buffer & Row State Lifecycle: In Pre-Processing and In-Transaction directives, updated records present two states in memory: the original unmodified row (RowMod = "" / unchanged) and the proposed updated row (RowMod = "U"). In Post-Processing, RowMod is cleared to an empty string ("").
  • RowMod Tracking Behavior: While Epicor's business objects handle RowMod flags automatically during standard operations, flagging is often automatic but not guaranteed across complex, multi-tiered updates. Relying on the two-directive pattern (Enable Post Directive / Enable Standard Directive) provides a deterministic, reliable solution rather than depending on dirty flags.
  • Context Passing (callContextBpmData): You can pass temporary values (such as notes or calculated fields) from Pre-Processing to Post-Processing on the same Business Object method call using callContextBpmData. However, context fields do not automatically persist across secondary, nested Business Object calls; for multi-BO orchestration, use an Epicor Function (EFx).
  • Root Causes of Duplicate Emails: Duplicate alerts typically stem from Business Object methods that execute multiple internal saves during a single user action (such as SalesOrder.MasterUpdate), or leaving the Send Email widget configured to iterate over every row in a multi-line dataset.
  • Bulk Imports (DMT) Alert Spikes: Data Directives fire on every database write, including bulk migrations via the Data Migration Tool (DMT). Unconditional alerts can inadvertently flood user inboxes during imports.

Method Directives vs. Data Directives: Selecting the right layer

Criteria Method Directive Alert Data Directive Alert
Trigger Point Specific Business Object method (e.g., SalesOrder. MasterUpdate) Direct table write (Insert, Update, Delete) regardless of originating source
Execution Scope Fires only when the specific UI screen or REST call invokes that exact method Fires across all callers: UI, DMT, EDI, REST API, or background processes
Detection Timing Pre-Processing (before base business logic executes) In-Transaction (before database commit)
Notification Timing Post-Processing (after base logic commits) Standard (after database commit)
Context Passing Native via callContextBpmData within the same BO execution Native via callContextBpmData within the same transaction
Best For Workflow notifications tied to a specific operational action in a known screen Universal data audit alerts (e.g., Credit Hold flagged, part status changed)

The Two-Directive implementation patterns

Pattern 1: Method Directive Alert (Tied to a Specific Screen Action)

Use this pattern when an alert should fire strictly when a user saves a record in a specific functional screen (e.g., Order Entry or PO Entry).

Step 1: Pre-Processing Directive (Detect the Change)

  1. In Method Directives Maintenance, locate the target Business Object and method (e.g., Erp.BO.SalesOrder.MasterUpdate).
  2. Create a new Pre-Processing Directive.
  3. Add a Condition Widget:
    • Select "the <field> field has been changed from <value> to <value>" (or "the <field> field of the changed row has been changed from False to True").
  4. On the True branch, add the Enable Post Directive widget.
  5. In the widget parameters, assign a descriptive name (e.g., NotifyCreditHold).
  6. (Optional) If you need to pass calculated details to the email, use a Set Argument / Variable widget to populate callContextBpmData.Character01.

Step 2: Post-Processing Directive (Dispatch the Email)

  1. On the same Business Object method, create a Post-Processing Directive.
  2. Add a Condition Widget:
    • Select "this directive has been enabled from the NotifyCreditHold directive".
  3. On the True branch, connect the Send Email widget.
  4. Configure email recipients, subject line, and body template (referencing {callContextBpmData.Character01} if context variables were populated in Pre-Processing).

---

Pattern 2: Data Directive Alert (Universal Table-Level Notification)

Use this pattern when an alert must fire whenever a table column changes, regardless of which screen, import, or API endpoint caused the write.

Step 1: In-Transaction Data Directive (Detect the Change)

  1. In Data Directives Maintenance, locate the database table (e.g., Erp.Customer).
  2. Create an In-Transaction Data Directive.
  3. Add a Condition Widget:
    • Select "the Customer.CreditHold field has been changed from False to True".
  4. On the True branch, add the Enable Standard Directive widget.
  5. Assign a descriptive identifier (e.g., TriggerCreditHoldEmail).

Step 2: Standard Data Directive (Dispatch the Email)

  1. On the same database table, create a Standard Data Directive.
  2. Add a Condition Widget:
    • Select "this directive has been enabled from the TriggerCreditHoldEmail directive".
  3. On the True branch, connect the Send Email widget.

Why Directives behave this way under the hood

1. Database Transaction Isolation (TransactionScope)

Why doesn't Epicor permit the Send Email widget inside In-Transaction Data Directives?

  • In-Transaction directives run inside the core database transaction. If an email were dispatched before commit, and a subsequent validation rule failed or SQL encountered a deadlock, the database transaction would roll back (ROLLBACK TRANSACTION). However, the email would have already escaped via SMTP, informing stakeholders of a transaction that never actually occurred.
  • Furthermore, establishing an external mail socket connection introduces network latency (500ms to 5,000ms). Holding an open database transaction lock while awaiting an external mail server response can create catastrophic SQL table blocking across the enterprise.

2. RowMod Buffer Lifecycle

In Epicor's architecture, table row states in memory are indicated by RowMod:

  • "A" = Added (New record)
  • "U" = Updated (Modified record)
  • "D" = Deleted
  • "" (Empty string) = Unchanged baseline record

During Pre-Processing and In-Transaction phases, the engine holds both the unchanged row buffer (RowMod = "") and the proposed modified buffer (RowMod = "U"). This allows comparison conditions ("changed from X to Y") to execute successfully.

Once the transaction is committed, execution moves to Post-Processing and Standard directives. Here, RowMod is cleared to an empty string (""), and the prior buffer is discarded. Attempting to evaluate whether a field "changed" at this late stage fails because the comparison baseline no longer exists in memory.

Preventing duplicate emails: Common traps & fixes

Trap 1: Multi-Save BO Methods (e.g., SalesOrder.MasterUpdate)

  • Symptom: Saving an order once triggers two, three, or four identical email alerts.
  • Root Cause: Epicor business objects frequently invoke Update or MasterUpdate multiple times during a single user save (for example, saving line items, recalculating freight/taxes, and then updating header balance totals). If your directive evaluates true on multiple internal passes, an email is queued on each pass.
  • Remedy: Use Kinetic Tracing Options to trace the exact method call sequence. Ensure your Pre-Processing detection condition explicitly inspects the header table (e.g., OrderHed) and verifies that the specific modified column is dirty, rather than firing on generic dataset updates.

Trap 2: Send Email Widget Row Iteration Settings

  • Symptom: Adding multiple lines to a sales order or PO results in multiple duplicate emails.
  • Root Cause: Inside the Send Email widget configuration, review the execution rule:
    • "Send email once for all affected rows" (Single aggregated email)
    • "Send email for each matching row" (Iterates per row)
  • Remedy: Ensure notifications intended as single alerts are configured to send once for the entire dataset, rather than iterating across detail lines.

Trap 3: Context Scope Across Secondary Business Objects

  • Symptom: callContextBpmData values set in Pre-Processing are blank when accessed later.
  • Technical Reality: callContextBpmData is preserved between Pre-Processing and Post-Processing on the same Business Object method execution. If the business workflow invokes a secondary, external Business Object call behind the scenes, that secondary call operates within its own context scope.
  • Remedy: When orchestration requires passing data across multiple distinct Business Objects, build the logic inside an Epicor Function (EFx) and pass structured input/output parameters.

Edge cases & special deployment considerations

DMT (Data Migration Tool) & Bulk Imports

Data Directives execute on every database write, regardless of the origin. During mass updates or data migrations using DMT, an unconstrained Data Directive will execute for every row in the import file, potentially sending thousands of unwanted emails to customers or internal staff.

  • Mitigation Strategy:
    • Do not rely on callContextClient values (such as ClientType or AssemblyName) to identify DMT sessions; in many server configurations and deployment releases, these client context fields remain unpopulated during DMT runs.
    • The most reliable enterprise approaches are:
    • Dedicated Migration Account: Create a dedicated user account (e.g., dmt_admin) strictly for bulk migrations, and add a condition to the directive: Session.UserID != "dmt_admin".
    • Temporary Bypass Flags: Include a custom character or checkbox flag in your DMT load file (e.g., Character01 = "NO_ALERT"), and configure the BPM condition to skip records where the bypass flag is present.
    • Pre-Migration Directive Disable: Temporarily disable the Standard notification directive in Data Directives Maintenance prior to running large bulk migrations, and re-enable it post-import.

Modern Microsoft 365 Mail Configuration

Organizations migrating from legacy on-premises SMTP relays to cloud-hosted Microsoft 365 must accommodate modern authentication standards:

  • Modern Epicor Kinetic environments support integration with Microsoft 365 using an Azure App Registration configured under Epicor System Setup / Company Maintenance mail settings.
  • Avoid legacy basic authentication (plain username and password over port 587), as major cloud identity providers continue to restrict unauthenticated or basic SMTP access across enterprise tenants.
  • Always verify that the designated sending address corresponds to an authorized, active mailbox or shared mailbox within your Microsoft 365 tenant.

Troubleshooting checklist: BPM email alerts

Symptom Root Cause & Resolution
Email never sends from Method Directive The UI screen saves through a different method than expected. Run Kinetic Tracing to confirm the exact BO and method invoked.

Change condition fails in Standard Directive Change detection requires before/after buffers. Detect the change in an In-Transaction directive and use Enable Standard Directive.

Change condition fails in Post-Processing RowMod is cleared post-commit. Detect the change in Pre-Processing and use the Enable Post Directive widget.

Alert sends even when save fails validation Email was placed in Pre-Processing before base validation. Move the Send Email widget to Post-Processing.

Multiple duplicate emails per save The BO executes multiple updates per save, or the email widget is set to send for each matching row. Restrict detection conditions.

FAQ

Why can't I place a Send Email widget in an In-Transaction Data Directive?

In-Transaction directives execute synchronously within the database transaction. Firing external emails before commit risks notifying users about rolled-back transactions and holding open SQL table locks during network delays. Use a Standard Data Directive for email actions.

Why doesn't the "field has been changed" condition work in Post-Processing?

In Post-Processing, the transaction has committed and the prior row buffer is discarded from memory. The modified row's RowMod is cleared, so change-detection comparisons fail. Detect the change in Pre-Processing and pass control using the Enable Post Directive widget.

What are the valid RowMod values in Epicor BPM?

"A" indicates an added row, "U" indicates an updated row, "D" indicates a deleted row, and an empty string "" represents an unchanged baseline row.

How can I prevent duplicate emails when SalesOrder.MasterUpdate runs?

Trace the method call, ensure the condition explicitly tests that the target header record has been modified, and gate the Post-Processing email using the Enable Post Directive action.

Can I pass data from Pre-Processing to Post-Processing across different Business Objects?

callContextBpmData reliably passes temporary data within the same Business Object method execution, but does not persist across distinct, secondary BO calls. Use an Epicor Function (EFx) for multi-BO orchestration.

References

Cross-check against official Epicor documentation matching your release and hosting model:

  • Epicor Kinetic Help: Method Directives Maintenance & Directive Flow Architecture
  • Epicor Kinetic Help: Data Directives Maintenance – In-Transaction vs. Standard Directives
  • Epicor Kinetic Help: BPM Designer Widgets – Enable Post Directive & Send Email Configuration
  • Epicor Kinetic Help: System Setup – Configuring Microsoft 365 Mail Integration
  • Epicor Kinetic Help: Tracing Options – Identifying Business Object Methods and Datasets
  • Epicor Kinetic Problem Reference: BPM Context Persistence & Buffer State Guidelines

Systems I work on