DocEx — Document Exchange for Business Central
DocEx is a framework by Fellowmind Finland that simplifies integrating Microsoft Dynamics 365 Business Central with external systems through file and message exchange. Rather than building file transfer logic from scratch for each integration project, DocEx provides a ready-made, extensible platform for sending and receiving files via cloud storage solutions and protocols such as Azure Blob Storage, Azure File Share.
Using DocEx cuts down integration implementation costs and provides an easy-to-use, standardized framework for handling file transfers. Custom message handling (e.g. XML, CSV, EDI) can then be built on top of the file transfer layer.
Table of Contents
- DocEx — Document Exchange for Business Central
- Table of Contents
- App Suite Overview
- Architecture
- Getting Started
- Connectors
- Gateway Configuration
- Message Flow
- Document Processing
- Job Queue Automation
- Business Events
- API Integration
- Developer Guide
- Creating a Custom Connector
- 1. Enum Extension
- 2. Interface Implementation
- 3. Supporting Tables and Pages (Optional)
- Extending Document Types
- Custom Document Processing
- Option 1: Integration Event (Recommended)
- Option 2: Setup Codeunits
- Creating Messages Programmatically
- Public API Methods
- Filename Templates
- Auxiliary Data
- Further Reading
App Suite Overview
DocEx is distributed as a suite of AppSource apps. Install the core app plus whichever connectors are needed for your integration scenario.
| App | Purpose |
|---|---|
| DocEx (Core) | Platform for exchanging messages to/from Business Central. Required by all connectors. |
| DocEx File Connector | Manual file import/export via browser download and upload dialogs. |
| DocEx Azure Blob V2 Connector | Automated file exchange with Azure Blob Storage containers. |
| DocEx Azure Fileshare V2 Connector | Automated file exchange with Azure File Shares. |
| DocEx Migration | One-time PTE migration tool for upgrading from the old PTE version to AppSource. See Migration Guide. |
All apps are published by Fellowmind Finland and target Business Central platform 24.0 and later.
Architecture
DocEx uses a three-tier architecture for message exchange:
flowchart LR
subgraph External["External Systems"]
Blob["Azure Blob Storage"]
AFS["Azure File Share"]
File["Local Files"]
end
subgraph DocEx["DocEx in Business Central"]
GW["Gateway\n(Inbound / Outbound)"]
MC["Message Channel\n(Inbox / Outbox Queue)"]
DOC["Documents\n(Header + Lines + Aux. Data)"]
end
External <-->|Connector| GW
GW <--> MC
MC <--> DOC
Key Entities
| Entity | Description |
|---|---|
| Gateway | A named connection endpoint. Each gateway is either Inbound (receive) or Outbound (send), and is linked to a specific connector (e.g. Azure Blob, Azure File Share). |
| Message | A file payload container identified by a GUID. Holds the file content as a blob, along with filename and length. |
| Message Channel | A queue entry linking a message to a gateway. Tracks the processing status of each message (Open → Queued → Processing → Processed / Failed). |
| Document | An optional EDI/business document representation with typed header, lines, and auxiliary data. Used for structured document processing on top of raw file exchange. |
| Document Line | Line items within a document (items, G/L accounts, resources, etc.). |
| Auxiliary Data | Flexible key-value storage attached to documents. Supports text, blob, integer, and decimal values for custom fields. |
Getting Started
1. Install the Apps
Install DocEx (the core app) from AppSource, then install the connector app(s) you need. Each connector app depends on the core app.
2. Configure DocEx Setup
Open the DocEx Setup page to configure:
- License — License key and expiration
- Document Number Series — Start/end numbers for inbound and outbound documents
- Custom Document Processing Codeunits — Optional codeunit IDs for processing documents (general, inbound-specific, outbound-specific)
3. Assign Permissions
Assign the DocEx Module permission set to users who need access to DocEx. Each connector app provides its own permission set extension that is automatically included.
4. Set Up Connector Accounts
Configure storage accounts or server connections for each connector you installed (see Connectors below).
5. Create Gateways
Create one or more gateways for your integration points (see Gateway Configuration).
Connectors
File Connector
The File Connector enables manual import and export of files through the browser.
- Send (Outbound): Downloads the file to the user's computer via the browser download dialog.
- Receive (Inbound): Prompts the user to select a file via the browser upload dialog.
Configuration: No account or settings configuration is needed — the connector is stateless.
Note: The File Connector cannot run in the background (Job Queue). It requires an active user session.
Azure Blob Storage Connector
Exchanges files with Azure Blob Storage containers. Supports automated background processing.
Blob Account Setup
Navigate to the Azure Blob Storage account settings and configure:
| Field | Description |
|---|---|
| Account Name | Azure Storage account name |
| Container Name | Blob container name |
| Access Key | Storage account access key (stored securely in Isolated Storage) |
The Container URL is generated automatically as https://<account>.blob.core.windows.net.
Use the Test Connection action to verify your configuration.
Blob Gateway Settings
| Field | Description |
|---|---|
| Prefix | Folder prefix for filtering files (e.g. outbound/invoices/) |
| File Mask | Pattern for matching filenames |
| Use Regex | Enable regex pattern matching for the file mask |
| Action on Import | Do Nothing / Archive (Copy) / Delete — what to do with the source file after receiving |
| Archive Container Name | Target container for archived files (when using Archive action) |
| Archive Folder Name | Target folder path within the archive container |
Azure File Share Connector
Exchanges files with Azure File Shares. Supports automated background processing.
File Share Account Setup
| Field | Description |
|---|---|
| Account Name | Azure Storage account name |
| Fileshare Name | Azure File Share name |
| Authentication Type | Access Key or SAS Token |
| Access Key | Storage account access key (when using Access Key auth) |
| SAS Token | Shared Access Signature token (when using SAS Token auth) |
When using SAS Token authentication:
- The SAS Token Expiration date is automatically parsed from the token.
- A warning is displayed if the token expires within 7 days.
- An error is shown if the token has already expired.
The Fileshare URL is generated automatically as https://<account>.file.core.windows.net.
File Share Gateway Settings
| Field | Description |
|---|---|
| Prefix | Folder prefix for filtering files |
| Action on Import | Do Nothing / Archive (Copy) / Delete |
| Archive Fileshare Name | Target file share for archived files |
| Archive Folder Name | Target folder path within the archive share |
File Share Browser
The Azure File Share connector includes a built-in File Browser page where you can:
- Browse files and folders in the file share
- Upload, download, and delete files manually
- View file metadata (size, dates, attributes)
Gateway Configuration
A Gateway is a named integration endpoint that is either Inbound (receiving files) or Outbound (sending files).
Creating a Gateway
- Open the DocEx Gateways page.
- Create a new gateway and fill in:
| Field | Description |
|---|---|
| Gateway Code | Unique identifier for the gateway (e.g. ACME.IN.ORDERS) |
| Direction | Inbound or Outbound |
| Description | Human-readable description |
| Gateway Connector | Select the connector type (File, Azure Blob, Azure File Share) |
| Processing Object Type | Codeunit or XMLport — the object to run when processing messages |
| Processing Object ID | The ID of the processing codeunit or XMLport |
| Default Filename Template | Template for generating filenames on outbound messages (see Filename Templates) |
| Auto Enqueue On Creation | Automatically enqueue outbound messages for sending when they are created |
| Allow Job Queue | Allow this gateway to be used from Job Queue automation |
Naming Conventions
Use a naming convention that makes it easy to identify the integration partner, direction, and message type. A recommended pattern:
<PARTNER>.<DIRECTION>.<MESSAGE_TYPE>
Examples:
| Gateway Code | Description |
|---|---|
ACME.IN.ORDERS |
Inbound orders from Acme Group |
ACME.OUT.INVOICES |
Outbound invoices to Acme Group |
DXL.OUT.PICKREQUEST |
Outbound pick requests to DXL Logistics |
DXL.IN.PICKCONFIRM |
Inbound pick confirmations from DXL Logistics |
A consistent naming convention also enables effective filtering in Job Queue automation (e.g. SEND[ACME*]).
Connector Configuration
After selecting a connector, use the Configure action on the gateway to open the connector-specific settings page. This is where you link the gateway to a storage account and configure file paths, masks, and import actions (see Connectors for details per connector type).
Message Flow
Outbound (Sending)
flowchart LR
A["Create Message"] --> B["Message Channel\n(Status: Open)"]
B --> C["Enqueue\n(Status: Queued)"]
C --> D["Send via Connector\n(Status: Processing)"]
D --> E["Archive\n(Status: Processed)"]
D --> F["Error\n(Status: Failed)"]
- A message is created (programmatically or via XMLport export) and linked to an outbound gateway.
- The message appears in the Message Channel with status Open.
- Enqueuing marks the message as Queued (this can happen automatically if "Auto Enqueue On Creation" is enabled).
- The channel dispatcher calls the connector's
Sendmethod to transfer the file. - On success, the message is moved to the archive. On failure, it is marked Failed.
Inbound (Receiving)
flowchart LR
A["Connector Receives Files"] --> B["Message Channel\n(Status: Open)"]
B --> C["Process Message\n(Status: Processing)"]
C --> D["Archive\n(Status: Processed)"]
C --> E["Error\n(Status: Failed)"]
- The connector's
Receivemethod pulls files from the external storage. - Each file becomes a message in the Message Channel with status Open.
- Processing runs the gateway's configured codeunit/XMLport on the message content.
- On success, the message is archived. On failure, it is marked Failed or Failed (Retry).
Message Statuses
| Status | Description |
|---|---|
| (blank) | New / uninitialized message |
| Queued | Waiting to be sent or processed |
| Processing | Currently being sent or processed |
| Processed | Successfully completed |
| Failed | An error occurred during processing |
| Failed (Retry) | An error occurred; queued for automatic retry |
Manual Operations
From the DocEx Gateways page, you can manually trigger:
- Receive — Pull files from the connector for inbound gateways
- Send — Enqueue and send all open messages for outbound gateways
- Process — Process all open messages in the channel
- Receive & Process — Receive and immediately process (inbound only)
- Process & Send — Process outbound data and send
Document Processing
Documents provide a structured, typed layer on top of raw message exchange. They are useful for EDI-style integrations where messages represent business documents.
Document Types
DocEx includes 26 built-in document types (the enum is extensible — see Extending Document Types):
| Type | Type | Type |
|---|---|---|
| Quote | Order | Invoice |
| Credit Memo | Blanket Order | Return Order |
| Shipment | Receipt | Order Response |
| Order Change | Instructions to Despatch | Invoice List |
| JIT Delivery | Consignment | Package |
| Packaging (Position) | Inventory Report | Sales Report |
| Catalog | Warehouse Instruction | Warehouse Notification |
| Delivery Note | Proforma Invoice | Goods Received Note |
| Goods Dispatched Note | Transfer Order |
Document Lifecycle
stateDiagram-v2
[*] --> Open
Open --> Released
Open --> Processed
Open --> Error
Released --> Open : ReOpen
Error --> Open : ReOpen
Open --> Rejected
- Open — The document is ready for processing
- Released — The document has been manually released
- Processed — Processing completed successfully
- Error — An error occurred during processing (the error text is stored in the Status String field)
- Rejected — The document was manually rejected
Projects and Message Types
For more complex integrations, organize your gateways using Project Codes and Message Types.
Example — Warehouse Integration with DXL Logistics:
| Concept | Value | Description |
|---|---|---|
| Project Code | DXL |
DXL Logistics |
| Message Type | PICK_REQUEST |
Outbound picking request |
| Message Type | PICK_CONFIRM |
Inbound pick confirmation |
| Gateway | DXL.OUT.PICKREQUEST |
Sends pick requests |
| Gateway | DXL.IN.PICKCONFIRM |
Receives confirmations |
Document Automation
The DocEx Document Automation page lets you configure automatic background processing of documents by document type and direction:
| Field | Description |
|---|---|
| Document Type | The document type to automate |
| Direction | Inbound or Outbound |
| Allow JQ | Enable Job Queue processing |
| Last Run | Timestamp of the last automation run |
| Last Processed Run | Timestamp of the last run that found documents to process |
Use the DOC_PROCESS Job Queue parameter to trigger document automation (see Job Queue Automation).
Job Queue Automation
DocEx gateways and document processing can be fully automated using Business Central's Job Queue.
Setup
- Create a new Job Queue Entry.
- Set the Object Type to Run to
Codeunit. - Set the Object ID to Run to
72310605(DocEx Job Handler). - Set the Parameter String to one of the actions below.
Actions
| Parameter String | Description |
|---|---|
RECEIVE |
Receive files for all inbound gateways with "Allow Job Queue" enabled |
SEND |
Enqueue and send all open messages for outbound gateways with "Allow Job Queue" enabled |
PROCESS |
Process all open messages for all gateways with "Allow Job Queue" enabled |
DOC_PROCESS |
Process open documents using Document Automation rules |
Gateway Filtering
Add a filter inside square brackets [ ] after the action to target specific gateways:
| Example | Effect |
|---|---|
RECEIVE[*IN*] |
Receive for all gateways with "IN" in the Gateway Code |
SEND[ACME*] |
Send for all gateways starting with "ACME" |
PROCESS[DXL.*] |
Process for all gateways starting with "DXL." |
RECEIVE[ACME.IN.ORDERS] |
Receive for one specific gateway |
Prerequisites
- The gateway must have Allow Job Queue enabled. This is a safety measure — newly created gateways will not be picked up by existing Job Queue entries until explicitly enabled.
- An error is raised if the filter matches no active gateways, alerting the user to review the Job Queue Entry settings.
Business Events
DocEx publishes Business Events that allow external systems (e.g. Power Automate) to react to activity in Business Central.
DocEx Document Error on Process
Triggered when a document processing operation results in an error.
Event Parameters:
| Parameter | Type | Example |
|---|---|---|
Direction |
Text[10] | Inbound |
DocumentType |
Enum | Order |
DocumentNo |
Code[20] | 100000 |
SystemId |
Guid | 165b0689-458b-4f3a-88e2-7c09313ca780 |
LastErrorCode |
Text[250] | Error code |
LastErrorText |
Text[250] | An error occurred. The last error description |
Example: Power Automate Flow
Use this business event to create a Power Automate flow that sends email or Teams notifications when a document fails processing. Configure the flow to trigger on the DocExDocumentError business event, then use the parameters to build a meaningful alert message.
API Integration
DocEx exposes API v1 pages for OData-based integration with external systems:
- DocEx Documents API — Read and manage document headers
- DocEx Document Lines API — Read and manage document lines
- DocEx Auxiliary Data API — Read and manage auxiliary key-value data
These APIs are published as standard Business Central API pages with:
- Publisher:
fellowmind - API Group:
docex - Version:
v1.0
Available API Entities
| API | Entity Name | Entity Set Name | Purpose |
|---|---|---|---|
| DocEx Documents | document |
documents |
Document headers and main document metadata |
| DocEx Document Lines | documentLine |
documentLines |
Lines belonging to DocEx documents |
| DocEx Auxiliary Data | auxData |
auxDatas |
Flexible key-value data for documents and document lines |
Endpoint Structure
The APIs follow the standard Business Central API route pattern:
/api/fellowmind/docex/v1.0/companies({companyId})/documents
/api/fellowmind/docex/v1.0/companies({companyId})/documentLines
/api/fellowmind/docex/v1.0/companies({companyId})/auxDatas
Typical use cases include:
- creating DocEx documents from an external integration service
- reading document headers and lines for downstream processing
- storing and retrieving custom integration metadata through auxiliary data
These API pages enable external systems to query, create, and update DocEx documents programmatically via standard Business Central OData/REST APIs.
Developer Guide
This section is for AL developers building integrations on top of DocEx or extending its functionality.
Creating a Custom Connector
To add a new connector (e.g. for a different cloud storage, API, or protocol), you need three components:
1. Enum Extension
Extend the DocEx Gateway Connector enum with your connector value:
enumextension 50100 "My Custom Connector" extends "DocEx Gateway Connector FMD"
{
value(50100; "My Custom Connector")
{
Caption = 'My Custom Connector';
Implementation =
"DocExIfaceGatewayConnector FMD" = "My Connector Codeunit";
}
}
2. Interface Implementation
Create a codeunit that implements the DocExIfaceGatewayConnector interface:
codeunit 50100 "My Connector Codeunit"
implements "DocExIfaceGatewayConnector FMD"
{
procedure Send(GatewayId: Guid)
begin
// Send all queued messages for this gateway
end;
procedure Send(DocExMessage: Codeunit "DocEx Message FMD"; GatewayId: Guid)
begin
// Send a specific message
// Use DocExMessage.Message_GetContent(Stream) to get the file content
// Use DocExMessage.GetName() to get the filename
end;
procedure Receive(GatewayId: Guid)
begin
// Pull files from external system
// For each file, create a message:
// DocExMessage.Create(GatewayCode, InStream, FileName);
end;
procedure Configure(GatewayId: Guid)
begin
// Open your connector's settings page for this gateway
end;
procedure DeleteGateway(GatewayId: Guid)
begin
// Clean up connector settings when a gateway is deleted
end;
procedure GetConnectorDescription(): Text[250]
begin
exit('My Custom Connector - description');
end;
procedure GetConnectorid(): Guid
begin
exit('00000000-0000-0000-0000-000000000000'); // Your unique connector GUID
end;
}
3. Supporting Tables and Pages (Optional)
If your connector needs configuration (accounts, paths, credentials), create:
- An Accounts table for storing connection credentials (use
IsolatedStoragefor secrets) - A Settings table for per-gateway configuration (prefix, file masks, import actions)
- Configuration pages for managing accounts and settings
Follow the pattern used by the built-in connectors (e.g. DocEx AzureBlob V2 Accounts, DocEx Azure Blob V2 Settings).
Extending Document Types
The DocEx Document Type enum is extensible. Add custom document types for your integration:
enumextension 50100 "My Document Types" extends "DocEx Document Type FMD"
{
value(50100; "Custom Type")
{
Caption = 'Custom Type';
}
}
Custom Document Processing
There are two approaches for custom document processing logic:
Option 1: Integration Event (Recommended)
Subscribe to the DocExDocumentManagementOnProcess event in codeunit DocEx Document Management:
[EventSubscriber(ObjectType::Codeunit, Codeunit::"DocEx Document Management FMD",
'DocExDocumentManagementOnProcess', '', false, false)]
local procedure OnDocExDocumentProcess(var DocExDocument: Record "DocEx Header FMD"; var Handled: Boolean)
begin
// Your processing logic here
// Set Handled := true when you've processed the document
Handled := true;
end;
When Handled is set to true, the document status is automatically set to Processed.
Option 2: Setup Codeunits
Register custom processing codeunit IDs in the DocEx Setup page:
| Field | Scope |
|---|---|
| Custom Doc. Processing ID | Runs for all documents (fallback) |
| Custom In Doc. Processing ID | Runs for inbound documents only |
| Custom Out Doc. Processing ID | Runs for outbound documents only |
The processing order is:
- Integration event (
DocExDocumentManagementOnProcess) - Direction-specific codeunit (if configured)
- General codeunit (if no direction-specific codeunit is configured)
Creating Messages Programmatically
Use the DocEx Message codeunit to create and manage messages:
var
DocExMessage: Codeunit "DocEx Message FMD";
TempBlob: Codeunit "Temp Blob";
OutStream: OutStream;
InStream: InStream;
begin
// Build your file content
TempBlob.CreateOutStream(OutStream, TextEncoding::UTF8);
OutStream.WriteText('Your file content here');
TempBlob.CreateInStream(InStream);
// Create the message and send to a gateway
DocExMessage.Create('ACME.OUT.INVOICES', InStream, 'invoice_001.xml');
end;
Public API Methods
| Method | Description |
|---|---|
Create(GatewayCode, DataStream) |
Create a message with auto-generated filename |
Create(GatewayCode, DataStream, FileName) |
Create a message with a specific filename |
Create(GatewayId, DataStream) |
Create a message using gateway GUID |
Create(GatewayId, DataStream, FileName) |
Create a message using gateway GUID with specific filename |
Get(MessageId): Boolean |
Retrieve a message by its GUID |
GetName(): Text[2048] |
Get the filename of the current message |
Message_GetContent(var InStream) |
Get the file content as a stream |
Message_GetContentBase64(): Text |
Get the file content as Base64-encoded text |
Message_GetLength(): Integer |
Get the content length in bytes |
Empty data streams are silently ignored (no message is created).
Filename Templates
Gateways can define a Default Filename Template for auto-generating filenames on outbound messages. The following placeholders are supported:
| Placeholder | Description | Example |
|---|---|---|
{Timestamp[format]} |
Current date/time in the specified format | {Timestamp[yyyyMMdd_HHmmss]} → 20260511_143022 |
{Guid} |
A new unique identifier | a1b2c3d4-e5f6-... |
{Code} |
The gateway code | ACME.OUT.INVOICES |
Example template: INV_{Timestamp[yyyyMMdd_HHmmss]}_{Guid}.xml
If no template is specified on the gateway, the system falls back to the global setup.
Auxiliary Data
The Document Auxiliary Data table provides flexible key-value storage for attaching custom data to documents without modifying the core tables.
Each entry supports multiple value types simultaneously:
| Field | Type | Use Case |
|---|---|---|
Value |
Text[250] | Short text values |
Value Blob |
Blob | Large text or binary data |
Value Int |
Integer | Numeric identifiers or counts |
Value Decimal |
Decimal | Amounts or quantities |
Auxiliary data entries are linked to a document by Document Type, Document No., and optionally Document Line No. for line-level data.
Further Reading
- Migration Guide — Upgrading from PTE to AppSource version