How-toMicrosoft Dynamics 365 Business CentralAL development / events

Business Central Integration Events: How to Publish and Subscribe in AL

Question
business central integration event

Also searched as

  • business central integration event example
  • how to subscribe to an event in business central al
  • IntegrationEvent attribute business central
  • business central EventSubscriber attribute

Short answer

An integration event is a procedure marked with the IntegrationEvent attribute that has an empty body. Code elsewhere subscribes to it with the EventSubscriber attribute, so extensions can change standard behavior without modifying base objects. You publish with IntegrationEvent, subscribe with EventSubscriber, and pass var parameters when the subscriber needs to alter data.

Applies to: Business Central online and on-premises, AL extensions (v2 extension model). Event signatures change between releases, always check the symbols of your target version.

Subscribe to an existing integration event

  1. 1Find the event. In VS Code, use the AL Explorer or search the base app symbols for procedures marked IntegrationEvent, usually named OnBefore... or OnAfter... on the codeunit you want to influence (for example the sales posting codeunit).
  2. 2Create a codeunit in your extension to hold subscribers. Subscribers can live in any codeunit, and a dedicated codeunit per feature keeps them easy to find.
  3. 3Add a procedure with the EventSubscriber attribute giving object type, object, event name, element name (empty for non-field events), SkipOnMissingLicense and SkipOnMissingPermission.
  4. 4Copy the parameter list from the publisher. Names must match the publisher parameter names, and you may omit trailing parameters you do not need. Add var where you intend to change the value.
  5. 5Keep the subscriber short. Put real logic in a separate codeunit so the subscriber stays a thin hook.
  6. 6Publish and test with the standard process (post a document, run a batch job) and use the debugger with a breakpoint in the subscriber to confirm it fires.
  7. 7If it never fires, check that the publisher name is spelled exactly, the extension is installed on the tenant or company, and no ManualBinding applies.

Declaring a publisher and a subscriber

A publisher is a procedure whose body is empty. The attribute takes two booleans: IncludeSender (pass the publishing object instance to subscribers, only meaningful for codeunits, tables and pages with an instance) and GlobalVarAccess (let subscribers read the publisher's global variables). Both should normally be false, because exposing globals ties subscribers to your internals.

A subscriber is any procedure carrying the EventSubscriber attribute. The compiler checks that the target event exists and that parameter names and types line up. Subscribers run synchronously inside the publisher's transaction, so an error in the subscriber rolls back the whole operation the user was doing.

// Publisher in your own codeunit
codeunit 50100 "Order Check Mgt"
{
    procedure CheckOrder(var SalesHeader: Record "Sales Header")
    var
        IsHandled: Boolean;
    begin
        OnBeforeCheckOrder(SalesHeader, IsHandled);
        if IsHandled then
            exit;
        // default behavior here
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckOrder(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
    end;
}
// Subscriber in another extension
codeunit 50101 "My Order Subscribers"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Order Check Mgt", 'OnBeforeCheckOrder', '', false, false)]
    local procedure HandleOnBeforeCheckOrder(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
        if SalesHeader."Sell-to Customer No." = 'CASH' then
            IsHandled := true;
    end;
}

IsHandled, var parameters and what subscribers may change

Most Microsoft publishers follow the IsHandled pattern: an OnBefore event receives a Boolean by var. If any subscriber sets it to true, the base code skips its default logic and uses what the subscriber produced. This is the closest thing AL has to overriding a procedure, and it is why the same event may be called by several subscribers from different apps. Do not assume yours runs first or alone.

Parameters passed by var can be modified, parameters passed by value cannot. Record parameters passed by var are the live record, so changing a field changes what the base code later posts or modifies. Always check whether the base code calls Modify afterward, otherwise your change exists only in memory.

Order between subscribers from different extensions is not guaranteed. If two apps both set IsHandled or change the same field, the result depends on load order, so document that in the extension and keep subscribers idempotent.

Integration events vs business events vs internal events

IntegrationEvent is for other AL extensions. BusinessEvent is for external consumers (Power Automate, Power Apps and similar), is treated as a contract that must stay stable, and should not be used to alter behavior. InternalEvent is visible only inside the same extension and is useful for decoupling your own modules without exposing a public surface.

Because integration events are a public contract, Microsoft and good ISVs avoid changing their signatures, but it does happen between major versions. When a publisher is removed or its parameters change, subscribers fail to compile or stop firing after the upgrade, which is a common cause of broken extensions on a new release.

For events on fields and triggers, the standard OnAfterValidateEvent and OnBeforeValidateEvent subscribers (with the field name as element name) are database triggers, not integration events. They work the same way to subscribe to, but they fire only when the field is validated in code or the UI, not on direct assignment.

Manual binding and the Event Recorder

Set EventSubscriberInstance = Manual on the subscriber codeunit and the subscribers fire only after you call BindSubscription, and stop after UnbindSubscription. This is the right tool when a subscriber should be active only during one operation, such as one import run, because it avoids global side effects on every user and every posting.

To discover which events fire during a process you do not know, use the Event Recorder page, start recording, perform the action in the client, stop, and read the list of events with the calling object. It is the fastest way to find the right OnBefore or OnAfter hook for a business process without reading the base code line by line.

codeunit 50102 "Import Subscribers"
{
    EventSubscriberInstance = Manual;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Order Check Mgt", 'OnBeforeCheckOrder', '', false, false)]
    local procedure Skip(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
        IsHandled := true;
    end;
}

// Caller
ImportSubs: Codeunit "Import Subscribers";
BindSubscription(ImportSubs);
// ... run import ...
UnbindSubscription(ImportSubs);

Try it here, free

Business Central AL Extension Skeleton Generator

Open full tool

Fill in publisher, prefix and ID range to get app.json, a table extension, a page extension, an event subscriber codeunit and a permission set.

Common pitfalls

  • !Putting heavy logic or a Commit inside a subscriber. It runs inside the caller's transaction and can break posting or leave partial data.
  • !Relying on the order in which subscribers from different extensions run. It is not guaranteed.
  • !Subscribing to a standard event and forgetting that the signature can change in the next major version. Pin and test against the preview before upgrading.
  • !Changing a record in a by-value parameter and expecting the base code to see it. Only var parameters flow back.
  • !Using IncludeSender or GlobalVarAccess true in your own publishers, which locks subscribers to your internal state.
  • !Subscribing to a table trigger event and expecting it to fire for direct field assignment. Validate events fire only on Validate.

How an ERP-grounded AI assistant handles this

Finding the right event is mostly reading source and symbols. A private model that has indexed your extension code and the base app symbols (kept on your own servers for an on-prem Business Central) can suggest candidate OnBefore and OnAfter events, draft the subscriber, and flag signature differences between versions. It produces code for a developer to review and test in a sandbox, it does not deploy anything on its own.

Frequently asked questions

What is the difference between an integration event and a business event?

An integration event lets other AL extensions hook into code and can pass var parameters to change behavior. A business event is meant for external systems such as Power Automate, is treated as a stable contract, and should not change behavior in the publisher. Use integration events for extensibility and business events for outbound notifications.

Can an event subscriber stop the base code from running?

Only if the publisher supports it, usually through an IsHandled Boolean passed by var. If the subscriber sets it to true, the base code skips its default path. Without such a parameter a subscriber can raise an Error to abort, but it cannot silently skip the logic.

Why is my event subscriber not firing?

Typical causes are a misspelled event or object name, the extension not being installed for that company or environment, a Manual subscriber codeunit that was never bound, or the code path not actually raising the event. Put a breakpoint in the publisher and confirm the publishing procedure is reached.

Do events work the same in SaaS and on-premises?

Yes, the extension model is the same. The difference is deployment and which events your target version exposes. Online tenants move to new versions on Microsoft's schedule, so event signatures can change under you sooner than on an on-premises server you upgrade deliberately.

Related

How-to

Calling and extending the Business Central API v2.0

The Business Central API v2.0 is a REST/OData endpoint at api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/api/v2.0, authenticated with Azure AD OAuth 2.0. It exposes standard entities like customers and items, and lets you publish your own with an AL API page.

Error fix

Fixing "You do not have the following permissions on TableData (table): (permission)" in Business Central AL

This runtime error means the current user's assigned permission sets do not grant the required Insert, Modify, Delete, Read, or Execute right on that specific table. It is most common right after installing or updating a custom AL extension, because Business Central checks object-level permissions even for tables an extension owns.

How-to

How to call an external REST API from AL in Business Central

Use the HttpClient, HttpRequestMessage, HttpResponseMessage and HttpContent types in AL, send the request with Send, check IsSuccessStatusCode, and parse the body with JsonObject.ReadFrom. Make sure the extension has Allow HttpClient Requests turned on, and keep tokens out of source code. Keep calls short, because they run inside the AL session.

Error fix

Business Central Error Messages: What They Mean and How Errors Are Handled

Most Business Central errors come from a few AL patterns: TestField and FieldError for missing or invalid field values, Error for rule violations, and permission or locking messages from the platform. Developers can customize them with Error and ErrorInfo, collect many errors at once, or catch them with TryFunction. If the whole service is unreachable, check service health and environment state first.

Error fix

Fixing "Cannot create a record in (table). The record already exists" in D365 F&SCM data entity imports

This is the standard kernel duplicate-key exception, thrown when the Data Management Framework tries to insert a staging row into a target table whose unique or alternate key already matches an existing record. It almost always means the entity's insert-vs-update logic did not recognize the target row as an update, not that you have a true accidental duplicate in your source file.

Error fix

Fixing "There are currently no batch servers available for processing" in D365 F&SCM

This message means the batch framework cannot find any AOS instance flagged as a batch server and available for the batch group the job is assigned to. The fix is almost always in server configuration (System administration > Servers) or batch group assignment, not in the batch job itself.

AI for ERP

AI for Dynamics 365 Business Central, on-premises or SaaS, beyond Copilot

AI for Business Central beyond Copilot: grounded question answering over BC's data via OData and APIs, for on-premises and SaaS tenants alike.

Stuck on Microsoft Dynamics 365 Business Central?

Talk to engineers who work inside Microsoft Dynamics 365 Business Central every week, and who build private AI that answers these questions from your own ERP data.

Keep exploring

Free tools

ERP fixes and how-tos

AI on your ERP

Integrations

Comparisons

Software