Email this post as a PDFDevelopment · 8 October 2026 · 2 min read

Extending workflows and approvals: adding your own events and responses

The approval workflow engine handles sales, purchase and journal approvals out of the box. Extensions can plug their own documents into it: a custom event ("courier shipment released"), a custom response, and the approval entries, delegation and notifications that come free. How the engine fits together and the four subscribers it takes to add your document to it.

Contents
  1. How the engine is built
  2. Adding your document
  3. What you get free
  4. Where it goes wrong

Users like approvals. They do not like a second approval system built by an extension when the standard one is already configured with their approvers, delegations and email notifications. The workflow engine is extensible for exactly this: your document can raise an event the engine understands, and the standard approval responses can act on it.

How the engine is built

A workflow is a sequence of steps; each step is a workflow event (something happened: "approval of a sales document is requested") followed by one or more workflow responses (do something: "create an approval request", "send notification", "release the document"). Events and responses are registered in a library by function name; the Workflow Event Handling and Workflow Response Handling codeunits hold Microsoft's. The approval entries table records who must approve, the status, delegation and due dates; the Approvals Mgmt. codeunit creates and processes them; the Workflow Management codeunit matches a raised event to active workflows and runs the responses.

Adding your document

Four subscribers and one call.

codeunit 50130 "CON Shipment Workflow"
{
    var
        SendForApprovalEventTok: Label 'CONSHIPMENTSENDFORAPPROVAL', Locked = true;
        SendForApprovalDescTxt: Label 'Approval of a courier shipment is requested.';

    // 1. register the event in the library
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Workflow Event Handling", OnAddWorkflowEventsToLibrary, '', false, false)]
    local procedure AddEvents()
    var
        WorkflowEventHandling: Codeunit "Workflow Event Handling";
    begin
        WorkflowEventHandling.AddEventToLibrary(SendForApprovalEventTok, Database::"CON Courier Shipment", SendForApprovalDescTxt, 0, false);
    end;

    // 2. say which standard responses may follow it (create approval request, send notification...)
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Workflow Response Handling", OnAddWorkflowResponsePredecessorsToLibrary, '', false, false)]
    local procedure AddPredecessors(ResponseFunctionName: Code[128])
    var
        WorkflowResponseHandling: Codeunit "Workflow Response Handling";
    begin
        case ResponseFunctionName of
            WorkflowResponseHandling.CreateApprovalRequestsCode(),
            WorkflowResponseHandling.SendApprovalRequestForApprovalCode():
                WorkflowResponseHandling.AddResponsePredecessor(ResponseFunctionName, SendForApprovalEventTok);
        end;
    end;

    // 3. let Approvals Mgmt. open/set status on your record
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Approvals Mgmt.", OnSetStatusToPendingApproval, '', false, false)]
    local procedure SetPending(RecRef: RecordRef; var Variant: Variant; var IsHandled: Boolean)
    var
        Shipment: Record "CON Courier Shipment";
    begin
        if RecRef.Number  Database::"CON Courier Shipment" then exit;
        RecRef.SetTable(Shipment);
        Shipment.Validate(Status, Shipment.Status::"Pending Approval");
        Shipment.Modify(true);
        Variant := Shipment;
        IsHandled := true;
    end;

    // 4. and populate the approval entry's details (amount, salesperson for limits)
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Approvals Mgmt.", OnPopulateApprovalEntryArgument, '', false, false)]
    local procedure Populate(var RecRef: RecordRef; var ApprovalEntryArgument: Record "Approval Entry"; WorkflowStepInstance: Record "Workflow Step Instance")
    begin
        if RecRef.Number  Database::"CON Courier Shipment" then exit;
        ApprovalEntryArgument."Document Type" := ApprovalEntryArgument."Document Type"::Order;
        // amount, currency, salesperson as needed for approval limits
    end;
}

Then your "Send Approval Request" action raises it:

WorkflowManagement.HandleEvent(SendForApprovalEventTok, Shipment);

Mirror the pattern for cancel and for "released" (so the standard "release document" response can set your status back), and add OnReleaseDocument / OnOpenDocument subscribers so the standard responses can call your Mgt. codeunit.

What you get free

The approval entries page and FactBox, approval limits by user, delegation and substitutes, due dates and overdue notifications, email and in-client notifications via the standard notification setup, the Requests to Approve list on the role centre, and the workflow designer where the administrator can add conditions ("only if weight over 500kg") without code. Ship a workflow template too (subscribe to OnAddWorkflowTemplatesToLibrary) so the administrator starts from a working one.

Where it goes wrong

  • Your Status enum lacks Pending Approval / Released values that the responses expect. Add them.
  • Forgetting the Variant assignment in step 3, so the workflow continues with a stale record.
  • Building a parallel "approved by" field instead of reading the approval entries.
Email this post as a PDF
A branded PDF of this post, sent to your inbox to keep, print or share. Copy in colleagues if you like.
We only use these addresses to send this PDF. Nobody is added to a mailing list. Mike at Business Boxed receives a copy. See our privacy notice.
Email this post
Talk to us
Want this done in your Business Central?
Book a 30 minute callGet the monthly newsletter

Related posts