← All postsDevelopment ยท 3 November 2026

Events in AL: publishers, subscribers and the IsHandled pattern

Events are how an extension changes what Business Central does without changing Business Central. The three kinds of event, how subscribers bind, the IsHandled pattern that lets you replace standard behaviour cleanly, what to do when the event you need does not exist, and the mistakes that make event code slow or fragile.

Everything an extension does to the base application, it does through events. There is no other supported way to run your code inside Microsoft's posting routine, validation or page action. Understanding events properly is therefore most of understanding AL extension development, and the patterns are small enough to fit in one post.

The three kinds

Your own codeunits publish events too, with the same attributes, so that other apps (or your future self) can extend your extension the same way.

Subscribing

codeunit 50120 "CON Sales Event Subscribers"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", OnBeforePostSalesDoc, '', false, false)]
    local procedure OnBeforePostSalesDoc(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    var
        PalletCheck: Codeunit "CON Pallet Check";
    begin
        PalletCheck.ValidateBeforePost(SalesHeader);
    end;
}

The attribute names the object, the event and the element (the field name for a validate event, blank otherwise), then two booleans: SkipOnMissingLicense and SkipOnMissingPermission. The subscriber's signature must match the publisher's parameters by name and type; VS Code will generate it for you from the event's definition (Ctrl+Space in the attribute).

By default a subscriber codeunit binds statically: it is active as soon as the app is installed, for every session. EventSubscriberInstance = Manual lets you bind an instance explicitly with BindSubscription, which is the pattern for subscribers that should only be live during a specific operation (and for tests).

The IsHandled pattern

Most OnBefore integration events carry var IsHandled: Boolean. Set it to true and the base application skips its own implementation of that step; your subscriber has handled it. This is how you replace standard behaviour rather than add to it: a different credit check, a different way of finding the price, a different document number.

The rules: check IsHandled on entry and return if another subscriber already set it, so two apps do not both replace the same step; set it only when you have genuinely done the whole job; and prefer not to use it at all when an OnAfter would do, because replacing standard logic means you now own that logic through every release wave.

When the event you need does not exist

Look again: the base application has thousands and the one you need is often a few lines from where you were looking. Check whether a trigger event on the table will do. If there is genuinely nothing, request it on the AL extensibility GitHub (microsoft/ALAppExtensions issues, "Event request" template) with the codeunit, the line and the signature; Microsoft adds most reasonable requests within one or two releases. What you do not do is copy the base procedure into your own codeunit and call that instead, or try to reach the behaviour by subscribing to something unrelated nearby. Both work until the next update.

Mistakes that make event code slow or fragile

Testing event code

Because the subscriber is thin and the logic is in a codeunit, test the codeunit directly. Then one integration test that actually posts a document and checks the effect, to prove the event fires and the signature still matches. When Microsoft changes an event's parameters (it happens, with an obsolete period), that test is what tells you, in the NextMajor build, before your customer does. The design rules and pipeline posts cover the rest.

Talk to us
Want this done in your Business Central?
Book a 30 minute callGet the monthly newsletter

More from the blog