Using FlowFilter Fields as Query Parameters in Microsoft Dynamics 365 Business Central Page APIs
MAR 28, 2026
Sometimes a Page API needs input from the caller that isn't a filter on the source table itself. This is a pattern for passing typed input parameters to a Page API on a plain GET request, using FlowFilter fields, no bound actions or workarounds required.
FlowFilters as input parameters
FlowFilter fields are a specialized field class that are never stored in the database, they exist only in memory for the duration of the current context. That characteristic makes them semantically right for exactly this job: a value the caller provides, used to shape the response, persisted nowhere.
The problem
The use case came from Visual Tracking: a Page API that returns Documents filtered by their related Tags, not by any direct property of the Document table. A Page API queries at the root table level, so passing in that extra context from the outside needs a creative route.
Create the table structures
Two tables carry the pattern: a Tag table that stores relationships between documents and business entities, and a Document table that gets two FlowFilter fields, one for Tag Type and one for Tag No.
Expose the FlowFilters on the API page
The Page API, "Document By Tag", exposes the two FlowFilter fields as layout fields. That is all it takes for them to become usable as query parameters in the OData request.
Execute the filters in OnOpenPage
The OnOpenPage trigger reads the incoming filter parameters, validates them, and translates them into a filter on the page source:
trigger OnOpenPage()
var
Tag: Record "VTS Tag";
DocumentFilter: Text;
begin
if Rec.GetFilter("Tag Type Filter") = '' then
Error('Tag Type Filter must be provided.');
if Rec.GetFilter("Tag No. Filter") = '' then
Error('Tag No. Filter must be provided.');
Tag.SetRange(Type, Rec.GetRangeMin("Tag Type Filter"));
Tag.SetRange("No.", Rec.GetRangeMin("Tag No. Filter"));
if Tag.FindSet() then
repeat
if DocumentFilter <> '' then
DocumentFilter += '|';
DocumentFilter += Tag."Document No.";
until Tag.Next() = 0;
if DocumentFilter <> '' then
Rec.SetFilter("File No.", DocumentFilter)
else
Rec.SetRange("File No.", '');
end;
GetFilter validates that the caller actually provided each parameter, GetRangeMin extracts the value, and the loop builds a pipe-delimited filter of matching document numbers that is applied to the page source.
Construct the OData request
On the calling side, the parameters travel as ordinary OData $filter conditions:
$filter: `tagType eq 'Item' and tagNo eq '${item.no}'`
Results and limitations
The API returns only the Documents linked to the requested Tag, exactly as intended. The limitation: the pattern is clean only for single-value equality filters. If a caller needs several values of the same type, you have to parse the raw filter string yourself to handle the pipe-delimited case.
Conclusion
This is an undocumented pattern, but it leans on a Business Central primitive doing exactly what it was designed for: typed, in-memory input on the current context. It gives Page APIs real input parameters on GET requests, without bound actions and without abusing real table fields.