Skip to content
flowon.com

Logic API

Create the API host project

Create an ASP.NET Core project that hosts a Logic API, configure its Dataverse connection and sign-in, and run it.

A Logic API is designed in API Studio and stored in Dataverse. To make it callable, you run it in an API host: a small ASP.NET Core application that you build, own and deploy. The host loads the API definition from Dataverse when it starts, generates the endpoints, signs callers in, and runs each call against Dataverse.

This page builds a host step by step, for a neutral example used throughout this section: a Partner Portal project with a Partner API that partner companies call to see and update their own opportunities. The API has a Partner role and two user properties, contactId and accountId (both Unique Identifier), as described in Access control.

You need Notes
The .NET 10 SDK and Visual Studio 2026 (or another editor) The host targets .NET 10.
A published API, or a draft to try The project name and API name exactly as they appear in the Logic Fabric API Builder app.
A Dataverse connection for the host Usually an application user (client ID and secret, or a certificate). It needs a security role that can read the Logic Fabric API definitions and that has the privileges the API needs on the tables and actions it exposes.
A sign-in setup for callers For example an app registration in Microsoft Entra ID that callers request tokens for. The host validates those tokens.
  1. Select File > New > Project, choose ASP.NET Core Empty, and name it PartnerApiHost.
  2. Select .NET 10.0 as the framework and keep Configure for HTTPS selected.
  3. In Solution Explorer, right-click the project, select Manage NuGet Packages, and install:
    • Flowon.Xrm.Api.AspNetCore.Rest (Logic API runtime for ASP.NET Core; it brings in the other Logic API packages)
    • Microsoft.AspNetCore.Authentication.JwtBearer (validates the callers’ bearer tokens)

The project file then looks like this (versions may be newer):

PartnerApiHost.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Flowon.Xrm.Api.AspNetCore.Rest" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.13" />
</ItemGroup>
</Project>

Put the settings in appsettings.json, and override them per environment (appsettings.Development.json, App Service application settings, and so on):

appsettings.json
{
"LogicApi": {
"Version": "latest"
},
"Authentication": {
"Authority": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"Audience": "api://<partner-api-app-id>"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

LogicApi:Version chooses which version of the API the host serves: draft, latest, or a version number such as 2.1. See Versions and updates.

The Dataverse connection string contains a secret, so never put it in appsettings.json or in source control. On your machine, store it with user secrets:

Terminal window
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Dataverse" "AuthType=ClientSecret;Url=https://contoso.crm.dynamics.com/;ClientId=<app-id>;ClientSecret=<secret>"

In Azure, set it as a connection string named Dataverse on the App Service, or read it from Azure Key Vault.

Replace the contents of Program.cs:

Program.cs
using Flowon.Xrm.Api.AspNetCore;
using Flowon.Xrm.Api.Metadata;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.PowerPlatform.Dataverse.Client;
using PartnerApiHost;
var builder = WebApplication.CreateBuilder(args);
// Dataverse connection used to load the API definition and to run the API.
var dataverse = builder.Configuration.GetConnectionString("Dataverse")
?? throw new InvalidOperationException("ConnectionStrings:Dataverse is not configured.");
// Callers sign in with Microsoft Entra ID and send a bearer token.
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = builder.Configuration["Authentication:Authority"];
options.Audience = builder.Configuration["Authentication:Audience"];
});
builder.Services.AddAuthorization();
builder.Services.AddMemoryCache();
builder.Services
.AddFlowonApi("Partner Portal", "Partner API", _ => new ServiceClient(dataverse))
.AddRestServer(options =>
{
options.VersioningStrategy = CreateVersioningStrategy(builder.Configuration["LogicApi:Version"]);
})
.AddUserResolver<PartnerUserResolver>()
.AddMetadataWatcher(options => options.TimeIntervalInMinutes = 5);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRestServer();
app.Run();
// "draft" serves the draft, "latest" the newest published version, "2.1" exactly that version.
static ApiVersioningStrategy CreateVersioningStrategy(string? version) => version?.Trim().ToLowerInvariant() switch
{
null or "" or "latest" => new LatestVersionApiVersioningStrategy(),
"draft" => new DraftVersionApiVersioningStrategy(),
_ => new ExactVersionApiVersioningStrategy { ApiVersion = ParseVersion(version) },
};
static ApiVersion ParseVersion(string version)
{
var parts = version.Split('.');
if (parts.Length != 2 || !int.TryParse(parts[0], out var major) || !int.TryParse(parts[1], out var minor))
{
throw new InvalidOperationException($"LogicApi:Version '{version}' must be 'draft', 'latest' or 'major.minor', for example '2.1'.");
}
return new ApiVersion(major, minor);
}

Then add the user resolver class, PartnerUserResolver, from Implement the user resolver.

Call What it does
AddAuthentication().AddJwtBearer(...) Validates the bearer token that callers send in the Authorization header and turns it into the signed-in user. Use the sign-in scheme your callers use; Logic API only needs ASP.NET Core to know who the caller is.
AddFlowonApi(project, api, factory) Registers the Logic API runtime for one API of one project. The names must match the project and API exactly. The factory creates the Dataverse connection the host uses.
AddRestServer(options) Adds the REST endpoints. options.VersioningStrategy chooses which version is served; the default is the latest published version.
AddUserResolver<T>() Registers your user resolver, which tells Logic API who each caller is: signed in or not, registered or not, their API roles and their user properties. Without one, every caller is treated as anonymous or as a signed-in caller with no roles and no properties.
AddMetadataWatcher(options) Checks Dataverse every few minutes for a changed draft or a newer published version and restarts the host to load it. See Versions and updates.
UseAuthentication(), UseAuthorization() Must come before UseRestServer(), so that the user resolver sees the signed-in caller.
UseRestServer() Generates the endpoints from the API definition and maps them.
  1. Run the host with F5 in Visual Studio, or dotnet run. At startup it connects to Dataverse and loads the API; if the project or API name is wrong, or the version doesn’t exist, it stops with an error that says so.

  2. Check that it serves the API you expect:

    GET https://localhost:7263/health

    The response names the project, the API and the version the host loaded, and whether it’s healthy.

  3. Call an endpoint with a token from your sign-in provider, as described in Call a Logic API. For example, the get action of the opportunity entity in the partner module:

    GET https://localhost:7263/partner/opportunity/7c3e5c4a-0d1b-4f25-9a2e-5d6c8f1e2a90/get
    Authorization: Bearer <token>

The port is the one Visual Studio assigned in Properties/launchSettings.json.

The host is a standard ASP.NET Core application. Publish it to Azure App Service, a container platform, or IIS like any other .NET 10 web app. Two things to plan for:

  • Restarts. With the metadata watcher, the host stops itself when the API changes and relies on the platform to start it again. App Service, container platforms with a restart policy, and IIS all do this. A plain console process doesn’t. See The metadata watcher.
  • Settings per environment. Set ConnectionStrings:Dataverse, Authentication:* and LogicApi:Version for each environment, so the same build runs in development, test and production.