Skip to main content

Workflow lifecycle action

Fires when the workflow framework records a meaningful workflow action - a work item assigned, a document submitted, approved, rejected, delegated, escalated, returned, recalled, or completed, or a workflow failure. The workflow framework writes one WorkflowTrackingTable row per action; this event publishes the reportable ones with the workflow, document, step, acting user, new assignee and comment, so approval dashboards, Teams notifications, or downstream systems can follow approvals as they happen instead of polling workflow history. It fires for every workflow type and every company.

Event classMGCLatticeWorkflowLifecycleBusinessEvent
Contract classMGCLatticeWorkflowLifecycleBusinessEventContract
CategorySystem administration
ModuleModuleAxapta::SystemAdministration
Label IDsWorkflowLifecycleBusinessEventName / WorkflowLifecycleBusinessEventDescription
TriggerWorkflowTrackingCommentTable.insert() -> the tracking row of the recorded action has a reportable TrackingType

Trigger / Hook​

  • Extension class: MGCLatticeWorkflowTrackingCommentTable_Table_Extension, decorated with [ExtensionOf(tableStr(WorkflowTrackingCommentTable))].

  • CoC method signature: public void insert().

  • Why a table hook: the workflow framework records every action through WorkflowTrackingTable::saveTracking(), and there's no public process method that covers all actions. For every action, saveTracking() writes the tracking row, then the work item link (WorkflowTrackingWorkItem), then the comment row, so the event is raised when the comment row is written and can carry the new assignee and the comment. The workflow maintenance job and the unrecoverable-workflow cancel also write comment rows, but only for cancellations, which aren't reported.

  • Firing condition: after next insert(), the activation check runs first; then the tracking row named by this.WorkflowTrackingTable is read, and the event sends when MGCLatticeWorkflowLifecycleBusinessEvent::isReportableAction(trackingTable.TrackingType) is true. Reportable tracking types, and the action value each produces:

    WorkflowTrackingTypeaction
    CreationAssigned
    SubmissionSubmitted
    ApprovalApproved
    RejectionRejected
    Delegation, AutomaticDelegation, ReassignDelegated
    EscalationAction, FinalEscalationActionEscalated
    CompletionCompleted
    Fault, UnrecoverableFailed
    DeniedDenied
    ReturnReturned
    RequestChangeChangeRequested
    ResubmissionResubmitted
    RecalledRecalled

    All other tracking types - condition evaluations, activation, cancellation, due-date and duration notices, sub-workflow bookkeeping, and so on - are ignored.

  • One emission per tracking row: an action recorded for several work items or users (for example a step assigned to multiple approvers) produces one event per row. The hook covers every tracking context, including sub-workflows and line-item workflows.

  • Timing: saveTracking() writes the rows inside its own ttsbegin/ttscommit, so send() writes to the business events commit log inside that transaction and the event is delivered only if it commits.

  • Enablement gate: BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeWorkflowLifecycleBusinessEvent)), checked before the tracking row is read, so while the event isn't activated a comment row costs nothing more.

public void insert()
{
next insert();

if (this.WorkflowTrackingTable
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeWorkflowLifecycleBusinessEvent)))
{
WorkflowTrackingTable trackingTable;

select firstonly trackingTable
where trackingTable.RecId == this.WorkflowTrackingTable;

if (trackingTable.RecId && MGCLatticeWorkflowLifecycleBusinessEvent::isReportableAction(trackingTable.TrackingType))
{
MGCLatticeWorkflowLifecycleBusinessEvent::newFromTracking(trackingTable).send();
}
}
}

Payload Contract​

One row per [DataMember], in declaration order. Sources are the action's WorkflowTrackingTable row, its WorkflowTrackingWorkItem and WorkflowTrackingCommentTable rows, and the workflow instance's WorkflowTrackingStatusTable row (joined on WorkflowTrackingTable.WorkflowTrackingStatusTable), mapped in initialize().

JSON fieldDisplay labelType / EDTSourceNotes
actionActionstrDerived from WorkflowTrackingTable.TrackingType by actionName()See the table above
trackingTypeTracking TypeintWorkflowTrackingTable.TrackingType (as integer)For example 4 Approval, 9 Creation, 15 Fault, 19 Rejection, 23 Submission
trackingTypeNameTracking Type NamestrWorkflowTrackingTable.TrackingType (enum symbol)For example "Approval"
workflowCorrelationIdWorkflow Correlation IDstrWorkflowTrackingStatusTable.CorrelationIdThe workflow instance's GUID, formatted with braces; identical on every action of one workflow instance
workflowConfigurationWorkflow ConfigurationstrWorkflowTrackingStatusTable.ConfigurationNameName of the workflow configuration
workflowTypeWorkflow TypeintWorkflowTrackingStatusTable.WorkflowType (as integer)WorkflowTrackingStatusWorkflowType: 0 = Workflow, 1 = DependentSubworkflow, 2 = IndependentSubworkflow
workflowTypeNameWorkflow Type NamestrWorkflowTrackingStatusTable.WorkflowType (enum symbol)"Workflow", "DependentSubworkflow", or "IndependentSubworkflow"
legalEntityLegal EntitystrWorkflowTrackingStatusTable.ContextCompanyIdCompany of the workflow document, in lower case
documentTableNameDocument Table NamestrtableId2Name(WorkflowTrackingStatusTable.ContextTableId)Empty when no context table is recorded
documentRecIdDocument Record IDint64WorkflowTrackingStatusTable.ContextRecId
stepStepstrWorkflowTrackingTable.NameName recorded for the tracking row - the workflow element or step the action occurred on
actionByUserAction By UserstrWorkflowTrackingTable.UserUser the workflow framework recorded for the action
assignedToUserAssigned To UserstrWorkflowTrackingWorkItem.ToUser; for Assigned, the user of the new work item (WorkflowWorkItemTable.UserId) when ToUser is emptySet for Assigned and Delegated; empty for most other actions
commentCommentstrWorkflowTrackingCommentTable.CommentThe comment the user entered with the action; empty when none was entered
actionDateTimeAction Date TimeutcdatetimeDateTimeUtil::utcNow() when the contract is builtMoments after the action is recorded; \/Date(ms)\/ form

Every Lattice event also carries the Lattice ordering and correlation envelope, inherited from MGCLatticeBusinessEventContractBase: eventSequence (Int64 - increasing across the whole catalog and all companies, allocated when the event is raised; it orders the events of one record, gaps are normal and permanent, see the README), correlationId (String - always Scope:Key; groups emissions that belong to one logical run and defaults to Event: plus a GUID unique to this emission) and schemaVersion (Int - the payload shape version; 1 for this contract). D365 F&O also adds its standard envelope fields to every business event payload: BusinessEventId, BusinessEventLegalEntity, ContextRecordSubject, ControlNumber, EventId, EventTime, EventTimeIso8601, InitiatingUserAADObjectId, MajorVersion, MinorVersion, and ParentContextRecordSubjects. Do not rely on the order of members in the delivered JSON.

This event doesn't call correlateTo, so the Lattice correlationId is unique to each emission; to follow one workflow instance, group on workflowCorrelationId.

Example Payload​

A purchase requisition approved in USMF:

{
"BusinessEventId": "MGCLatticeWorkflowLifecycleBusinessEvent",
"ControlNumber": 5637151198,
"EventId": "8A4F1C62-D3B0-4E75-91A8-6C2E0F9B7D13",
"EventTime": "\/Date(1790260337000)\/",
"EventTimeIso8601": "2026-09-24T14:32:17.9217191Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{35AE4787-1725-1466-483B-094783187616}",
"ParentContextRecordSubjects": [],
"eventSequence": 48690,
"correlationId": "Event:2c7e0b93-a51f-4d68-b3e4-9f16d8a05c27",
"schemaVersion": 1,
"action": "Approved",
"trackingType": 4,
"trackingTypeName": "Approval",
"workflowCorrelationId": "{6B3D9E1A-0F42-4C87-A5D3-7E28B1C94F60}",
"workflowConfiguration": "Purchase requisition review",
"workflowType": 0,
"workflowTypeName": "Workflow",
"legalEntity": "usmf",
"documentTableName": "PurchReqTable",
"documentRecId": 5637145110,
"step": "Approve purchase requisition",
"actionByUser": "Admin",
"assignedToUser": "",
"comment": "Approved within budget.",
"actionDateTime": "\/Date(1790260337000)\/"
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Workflow lifecycle action") or by filtering the Category column to System administration. Select it and use Activate to bind it to a configured endpoint (for example, an Azure Service Bus queue, Event Grid topic, or HTTPS webhook). No Lattice setup is needed; the event covers every configured workflow. Because it fires for all workflow types and companies, filter on documentTableName, workflowConfiguration, or company in the subscriber.