--- title: "Custom pipeline element" slug: "bc-custom-pipeline-element" description: "Extend Business Integration Solutions with custom endpoints and activities in Dynamics 365 Business Central. Learn to create and configure elements effectively." tags: ["BC", "BIS", "Business Central", "Business Integration Solutions", "Task"] updated: 2026-06-25T09:59:07Z published: 2026-06-25T09:59:07Z canonical: "docs.staedean.com/bc-custom-pipeline-element" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.staedean.com/llms.txt > Use this file to discover all available pages before exploring further. # Custom pipeline element Business Integration Solutions provides a standard set of endpoints, events, and activities. The pipeline architecture includes extension points that let you extend this standard set, for example, a custom endpoint that connects to a specific web service or implements an unsupported format. You can extend the set without changing the standard product. ## Prerequisites The following prerequisites are required: - Experience developing in AL. - Understanding of Business Integration Solutions and especially the concept of pipeline and messages. - A Dynamics 365 Microsoft Dynamics 365 Business Central development license. - Experience with the Dynamics 365 Microsoft Dynamics 365 Business Central test framework. ## Create a custom element Use this task when you want to extend the list of standard activities with custom types. ### Steps The pipeline uses a generic system that you can enhance with specific configuration for each activity. | Name | Description | Mandatory | | --- | --- | --- | | Setup table | Table with a fixed primary key that maintains the activity configuration. | No | | Setup page | Page that maintains the activity configuration. | No | | Handler codeunit | The codeunit that implements the functionality of the activity. | Yes | | Configuration check codeunit | A codeunit that runs the activity-specific tests, activated via the **Check Configuration** function. | No | | Configuration XmlPort | An XmlPort that exchanges the details of the pipeline element to a fixed format. | No | 1. Develop a new setup table that implements the specific activity settings. 2. Develop a new page based on the setup table. 3. Develop a handler codeunit. 4. Develop a configuration check codeunit. 5. Develop a configuration XmlPort. 6. Create an **Element Type** record for the activity. The element type defines the settings for an activity and connects the Dynamics 365 Microsoft Dynamics 365 Business Central objects to the configuration. > [!NOTE] > **Note** > > Using the **Register** function of the Handler Codeunit can automate the registration process. ## Create a setup table for an element Use this task when you want to create a setup table with specific settings for an endpoint, activity, or event. ### Steps The setup table registers specific settings for a pipeline element, letting you extend the framework by adding new settings with specific fields and validations. 1. Create a new AL table object. > [!NOTE] > **Note** > > Each activity has its own table that maintains the specific settings, enabling validations relevant only to that activity. Since this table is an extension of the generic **Business Integration Solutions Pipeline Element** table, it must have the same primary key. The primary key consists of the following fields: | Field no. | Field name | Data type | Length | | --- | --- | --- | --- | | 1 | Source Type | Integer | | | 2 | Source No. | Code | 20 | | 3 | Activity Code | Code | 20 | 2. Add the specific fields for the custom pipeline element. > [!NOTE] > **Note** > > Use a specific range for new fields, for example, 50000 and higher. 3. When a setup table is not needed for your custom pipeline element, the register function should use the standard STAEDEAN page **Business Integration Solutions No Settings Setup**. ## Create a handler codeunit for an element Create a handler codeunit for a custom endpoint, activity, or event to implement the behavior of the pipeline element. ### Steps The handler codeunit implements the actual behavior of a pipeline element. The `OnRun` trigger must have a signature matching the element type. The following signatures are supported: | Element type | Subtype | Table | | --- | --- | --- | | Endpoint | Reader | Job Queue Entry | | Endpoint | Writer | Business Integration Solutions Message Queue | | Activity | | Business Integration Solutions Message Queue | | Event | | Job Queue Entry | 1. Create a new AL codeunit. 2. Add the appropriate code to implement the behavior of the pipeline element. 3. (Optional) Add a new function called `Register`. This function enables automated registration of the pipeline element when your extension installs. > [!NOTE] > **Note** > > An install codeunit can auto-register custom elements. ```plaintext // Example of register method for an endpoint local procedure Register(sender: Codeunit "BIS API Pipeline Setup") var    CustomElementDescription_Txt: Label 'Description of what your element does';    CustomElementCode: Code[20]; begin    CustomElementCode := 'CustomElementCode';    sender.RegisterEndpoint(        CustomElementCode, CustomElementDescription_Txt,        'Setup Table Id', 'Setup Page Id',        'Handler codeunit id',        'Configuration check handler codeunit id',        'Xml port handler id',        'ElementSubType - Option that changes depending on what you are trying to register'); end; ``` 4. To enable logging for your new custom element, subscribe to two events from codeunit `BIS API Pipeline Setup`: ```plaintext [EventSubscriber(ObjectType::Codeunit, codeunit::"BIS API Pipeline Setup", 'OnEnableChangeLog', '', true, true)] local procedure OnChangelogEnable(var sender: codeunit "BIS API Pipeline Setup") begin    Sender.AddTableToChangeLog("Your setup table id"); end; [EventSubscriber(ObjectType::Codeunit, codeunit::"BIS API Pipeline Setup", 'OnDisableChangeLog', '', true, true)] local procedure OnChangeLogDisable(var sender: codeunit "BIS API Pipeline Setup") begin    Sender.RemoveFromChangelog("Your setup table id"); end; ``` ## Create a configuration check codeunit for an element Use this task when you want to create a configuration check codeunit to verify the settings for an endpoint, event, or activity. ### Steps A configuration check codeunit is a Dynamics 365 Microsoft Dynamics 365 Business Central codeunit executed by a special test runner codeunit. This construction lets you implement tests in separate functions that execute individually. Errors that occur during this run capture in the test handler and write to a log table. 1. Create a new AL codeunit. 2. Declare global variables: ```plaintext var    CustomPipelineElement: Record "Your custom setup table";    ConfigTestRunner: Codeunit "BIS Configuration Test Runner"; ``` 3. (Optional) Add initialization code to the `OnRun` trigger: ```plaintext trigger OnRun() var    RecRef: RecordRef; begin    ConfigTestRunner.CurrentRecord(RecRef);    RecRef.SetTable(CustomPipelineElement);    RunTests(); // In RunTests, call your test cases, see step 5 for a full example end; ``` 4. Add try functions to test your configuration. > [!NOTE] > **Note** > > When porting to cloud, replace test functions (not supported in SaaS) with try functions, see the full example below. 5. Ensure the configuration check codeunit is included in the **Register** function of the handler codeunit. See [Create a handler codeunit for an element](/business-central/docs/custom-pipeline-element#how-to-create-a-handler-codeunit-for-an-element). Example of a configuration check codeunit from the **Attachment Generator** pipeline element: ```plaintext codeunit 11070201 "BIS Attachment Generator Test" {    trigger OnRun()    var        RecRef: RecordRef;    begin        ConfigTestRunner.CurrentRecord(RecRef);        RecRef.SetTable(AttachmentGenerator);        AttachmentLine.SetRange("Source Type", AttachmentGenerator."Source Type");        AttachmentLine.SetRange("Source No.", AttachmentGenerator."Source No.");        AttachmentLine.SetRange("Activity Code", AttachmentGenerator."Activity Code");        RunTests();    end;    var        AttachmentGenerator: Record "BIS Attachment Generator";        AttachmentLine: Record "BIS Attachment Generator Line";        ConfigTestRunner: Codeunit "BIS Configuration Test Runner";    local procedure RunTests()    var        CheckAttachmentsLbl: Label 'CheckAttachments', Locked = true;        CheckAttachmentGeneratorTypeLbl: Label 'CheckAttachmentGeneratorType', Locked = true;    begin        if not CheckAttachments() then            ConfigTestRunner.LogErrorMessage(CheckAttachmentsLbl, GetLastErrorText());        if not CheckAttachmentGeneratorType() then            ConfigTestRunner.LogErrorMessage(CheckAttachmentGeneratorTypeLbl, GetLastErrorText());    end;    [TryFunction]    procedure CheckAttachments()    begin        AttachmentLine.FindSet();        repeat            case AttachmentLine."Attachment Type" of                AttachmentLine."Attachment Type"::Report:                    begin                        AttachmentLine.TestField("Object No.");                        AttachmentLine.TestField("Attachment Name");                    end;                AttachmentLine."Attachment Type"::File:                    AttachmentLine.TestField("Attachment Content");            end;        until AttachmentLine.Next() = 0;    end;    [TryFunction]    procedure CheckAttachmentGeneratorType()    var        GeneratorEnum: Enum "BIS Attachment Generator";        GeneratorNumber: Integer;        LineGeneratorNotValidErr: Label 'Attachment line %1 generator not valid. Please select from the available options.', Comment = '%1 Line No.';    begin        AttachmentLine.FindSet();        repeat            Evaluate(GeneratorNumber, Format(AttachmentLine.Generator, 0, 2));            if not GeneratorEnum.Ordinals.Contains(GeneratorNumber) then                Error(LineGeneratorNotValidErr, AttachmentLine."Line No.");        until AttachmentLine.Next() = 0;    end; } ``` ## Create a Configuration XmlPort for an element Use this task when you want to create a configuration XmlPort for an endpoint, event, or activity. ### Steps The configuration XmlPort lets specific XML elements exchange for a pipeline element. Using an XmlPort for importing and exporting configuration settings decouples the internal table structure from an external representation of the data. 1. Create a new AL XmlPort. 2. Define the layout of the XmlPort. > [!NOTE] > **Note** > > The XmlPort layout does not need to include all data from the setup table. STAEDEAN does not export or import client-sensitive data such as passwords or credit card information. 3. Add a new function named `GetTargetRecord` and call it in the `OnAfterInitRecord` trigger of the root table in the port. This function is mandatory for importing data. The generic part of the configuration package reader calls it to set the primary key fields of the pipeline element. ```plaintext ... trigger OnAfterInitRecord() begin    GetTargetRecord();    "Your custom table" := TargetRecord; end; ... var    TargetRecord: Record "Your custom table id"; procedure GetTargetRecord() var    PackageEvents: codeunit "BIS Package Events";    NewTarget: RecordRef; begin    PackageEvents.GetTargetRecord(NewTarget);    if NewTarget.Number = "Your custom table id" then        NewTarget.SetTable(TargetRecord); end; ``` 4. Ensure the configuration XmlPort is included in the `Register` function of the handler codeunit. See [Create a handler codeunit for an element](/business-central/docs/custom-pipeline-element#how-to-create-a-handler-codeunit-for-an-element). Example of an XmlPort from the **Azure File Reader** pipeline element: ```plaintext xmlport 11070207 "BIS Az File Endpoint" {    FormatEvaluate = Xml;    schema    {        tableelement("BIS Az File Reader Setup"; "BIS Az File Endpoint Setup")        {            XmlName = 'AzFileReader';            textattribute(Version)            {                trigger OnBeforePassVariable()                begin                    Version := '1.0';                end;            }            fieldelement(FolderName; "BIS Az File Reader Setup"."Folder Name") { }            fieldelement(LeaveFilesOnFileSystem; "BIS Az File Reader Setup"."Leave Files") { }            fieldelement(ReadNewFilesOnly; "BIS Az File Reader Setup"."Read New Files Only") { }            fieldelement(FileNameFilter; "BIS Az File Reader Setup"."File Name") { }            fieldelement(FileShare; "BIS Az File Reader Setup"."File Share") { }            fieldelement(StorageAccount; "BIS Az File Reader Setup"."Storage Account") { }            fieldelement(Overwrite; "BIS Az File Reader Setup".Overwrite) { }            fieldelement(UseOriginalMessage; "BIS Az File Reader Setup"."Use Original Message") { }            trigger OnAfterInitRecord()            begin                GetTargetRecord();                "BIS Az File Reader Setup" := TargetRecord;            end;        }    }    var        TargetRecord: Record "BIS Az File Endpoint Setup";    procedure GetTargetRecord()    var        PackageEvents: codeunit "BIS Package Events";        NewTarget: RecordRef;    begin        PackageEvents.GetTargetRecord(NewTarget);        if NewTarget.Number = Database::"BIS Az File Endpoint Setup" then            NewTarget.SetTable(TargetRecord);    end; } ``` ## Related - [Extensibility](/bc-extensibility.md)