diff --git a/README.md b/README.md
index 77cbea9..b55288d 100644
--- a/README.md
+++ b/README.md
@@ -246,6 +246,7 @@ Native support for popular web frameworks.
| Next.js | Middleware package | ✅ Active | Community |
| Express.js | Middleware package | ✅ Active | Community |
| FastAPI | Middleware package | ✅ Active | Community |
+| [ASP.NET Core Minimal API](examples/frameworks/aspnetcore-minimal-api/) | Endpoint filter example for APort policy checks | ✅ Active | Community |
| Django | Middleware package | 🚧 In Progress | Community |
| Laravel | Composer package | 📋 Planned | Community |
| Rails | Ruby gem | 📋 Planned | Community |
diff --git a/examples/frameworks/aspnetcore-minimal-api/.env.example b/examples/frameworks/aspnetcore-minimal-api/.env.example
new file mode 100644
index 0000000..6d31123
--- /dev/null
+++ b/examples/frameworks/aspnetcore-minimal-api/.env.example
@@ -0,0 +1,7 @@
+# Copy these values into your shell or secret store before running against the live APort API.
+# The sample runs in mock mode by default so it can be tested without credentials.
+
+APort__BaseUrl=https://api.aport.io
+APort__ApiKey=your_api_key_here
+APort__DefaultPolicy=finance.payment.refund.v1
+APort__UseMock=false
diff --git a/examples/frameworks/aspnetcore-minimal-api/README.md b/examples/frameworks/aspnetcore-minimal-api/README.md
new file mode 100644
index 0000000..2a12133
--- /dev/null
+++ b/examples/frameworks/aspnetcore-minimal-api/README.md
@@ -0,0 +1,111 @@
+# APort ASP.NET Core Minimal API Example
+
+This example shows how to protect an ASP.NET Core Minimal API endpoint with an APort policy check before business logic runs.
+
+Integration highlight: the `/refunds` endpoint uses an endpoint filter to call APort `/verify` with the agent ID from `X-Agent-Id`. If APort denies the policy check, the endpoint returns `403` and the refund handler is not executed.
+
+## What is included
+
+- `src/Program.cs` - a self-contained Minimal API app.
+- `IAportVerificationClient` - a small typed `HttpClient` wrapper for APort `/verify`.
+- `RequireAportVerification(...)` - an endpoint filter extension that protects selected routes.
+- Mock mode for local development, enabled by default.
+
+## Prerequisites
+
+- .NET 10 SDK
+- An APort API key for live verification
+
+## Run locally
+
+```bash
+cd examples/frameworks/aspnetcore-minimal-api/src
+dotnet run
+```
+
+The app listens on the URL printed by `dotnet run`, usually `http://localhost:5xxx`.
+
+## Try the public health endpoint
+
+```bash
+curl http://localhost:5000/health
+```
+
+If your local port is different, replace `5000` with the port from `dotnet run`.
+
+## Try the protected refund endpoint in mock mode
+
+Mock mode is enabled by default so the example works without credentials.
+
+Allowed mock request:
+
+```bash
+curl -X POST http://localhost:5000/refunds \
+ -H "Content-Type: application/json" \
+ -H "X-Agent-Id: agt_demo" \
+ -d '{"orderId":"ord_123","amount":42.50,"currency":"USD"}'
+```
+
+Denied mock request:
+
+```bash
+curl -X POST http://localhost:5000/refunds \
+ -H "Content-Type: application/json" \
+ -H "X-Agent-Id: agt_denied" \
+ -d '{"orderId":"ord_123","amount":42.50,"currency":"USD"}'
+```
+
+## Use the live APort API
+
+Set these values in your shell or secret store:
+
+```bash
+export APort__BaseUrl="https://api.aport.io"
+export APort__ApiKey="your_api_key_here"
+export APort__DefaultPolicy="finance.payment.refund.v1"
+export APort__UseMock="false"
+```
+
+On PowerShell:
+
+```powershell
+$env:APort__BaseUrl="https://api.aport.io"
+$env:APort__ApiKey="your_api_key_here"
+$env:APort__DefaultPolicy="finance.payment.refund.v1"
+$env:APort__UseMock="false"
+```
+
+The live request body sent to APort is:
+
+```json
+{
+ "policy": "finance.payment.refund.v1",
+ "agentId": "agt_demo",
+ "context": {
+ "path": "/refunds",
+ "method": "POST"
+ }
+}
+```
+
+## Configuration
+
+| Key | Default | Description |
+| --- | --- | --- |
+| `APort__BaseUrl` | `https://api.aport.io` | APort API base URL. |
+| `APort__ApiKey` | empty | API key used as a Bearer token for live verification. |
+| `APort__DefaultPolicy` | `finance.payment.refund.v1` | Policy checked by the protected endpoint. |
+| `APort__UseMock` | `true` | Keeps the sample runnable without credentials. Set to `false` for live API calls. |
+
+## How to reuse this pattern
+
+1. Register a typed `HttpClient` for APort verification.
+2. Add `RequireAportVerification("your.policy.id")` to any Minimal API endpoint that needs policy enforcement.
+3. Pass an agent identifier through a trusted header, token claim, or service-to-service identity mechanism.
+4. Keep secrets in environment variables, user secrets, or a production secret manager; do not commit API keys.
+
+## Notes
+
+- The example intentionally avoids third-party packages so it can be copied into existing ASP.NET Core apps.
+- The endpoint filter returns Problem Details responses for missing agent IDs, denied policies, and API failures.
+- In a production app, bind the agent identity from your authentication layer instead of trusting a raw client-supplied header.
diff --git a/examples/frameworks/aspnetcore-minimal-api/src/Aport.AspNetCore.MinimalApi.Example.csproj b/examples/frameworks/aspnetcore-minimal-api/src/Aport.AspNetCore.MinimalApi.Example.csproj
new file mode 100644
index 0000000..9a04a31
--- /dev/null
+++ b/examples/frameworks/aspnetcore-minimal-api/src/Aport.AspNetCore.MinimalApi.Example.csproj
@@ -0,0 +1,7 @@
+
+
+ net10.0
+ enable
+ enable
+
+
diff --git a/examples/frameworks/aspnetcore-minimal-api/src/Program.cs b/examples/frameworks/aspnetcore-minimal-api/src/Program.cs
new file mode 100644
index 0000000..671240b
--- /dev/null
+++ b/examples/frameworks/aspnetcore-minimal-api/src/Program.cs
@@ -0,0 +1,172 @@
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.Extensions.Options;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.Configure(builder.Configuration.GetSection("APort"));
+builder.Services.AddHttpClient();
+
+var app = builder.Build();
+
+var defaultPolicy = app.Configuration["APort:DefaultPolicy"] ?? "finance.payment.refund.v1";
+
+app.MapGet("/health", () => Results.Ok(new
+{
+ status = "ok",
+ service = "aport-aspnetcore-minimal-api-example"
+}));
+
+app.MapPost("/refunds", (RefundRequest request) => Results.Ok(new
+{
+ message = "Refund request passed APort verification and is ready for business processing.",
+ request.OrderId,
+ request.Amount
+}))
+.RequireAportVerification(defaultPolicy)
+.WithName("CreateRefund")
+.WithSummary("Create a refund after APort policy verification");
+
+app.Run();
+
+public sealed record RefundRequest(string OrderId, decimal Amount, string Currency = "USD");
+
+public sealed class AportOptions
+{
+ public string BaseUrl { get; init; } = "https://api.aport.io";
+ public string? ApiKey { get; init; }
+ public string DefaultPolicy { get; init; } = "finance.payment.refund.v1";
+
+ // Keeps the example runnable out of the box. Set APort__UseMock=false for the live API.
+ public bool UseMock { get; init; } = true;
+}
+
+public sealed record AportVerificationResult(bool Allow, IReadOnlyList Reasons, JsonElement? Passport);
+
+public interface IAportVerificationClient
+{
+ Task VerifyAsync(
+ string policy,
+ string agentId,
+ object? context,
+ CancellationToken cancellationToken);
+}
+
+public sealed class AportVerificationClient(
+ HttpClient httpClient,
+ IOptions options,
+ ILogger logger) : IAportVerificationClient
+{
+ private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
+ private readonly AportOptions _options = options.Value;
+
+ public async Task VerifyAsync(
+ string policy,
+ string agentId,
+ object? context,
+ CancellationToken cancellationToken)
+ {
+ if (_options.UseMock)
+ {
+ return MockVerification(agentId);
+ }
+
+ if (string.IsNullOrWhiteSpace(_options.ApiKey))
+ {
+ throw new InvalidOperationException("APort__ApiKey is required when APort__UseMock=false.");
+ }
+
+ var baseUri = new Uri(_options.BaseUrl.TrimEnd('/') + "/", UriKind.Absolute);
+ using var request = new HttpRequestMessage(HttpMethod.Post, new Uri(baseUri, "verify"));
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.ApiKey);
+ request.Content = JsonContent.Create(new
+ {
+ policy,
+ agentId,
+ context
+ }, options: JsonOptions);
+
+ using var response = await httpClient.SendAsync(request, cancellationToken);
+ if (!response.IsSuccessStatusCode)
+ {
+ logger.LogWarning("APort verification returned {StatusCode}", response.StatusCode);
+ return new AportVerificationResult(
+ Allow: false,
+ Reasons: [$"APort API returned HTTP {(int)response.StatusCode}."],
+ Passport: null);
+ }
+
+ var verification = await response.Content.ReadFromJsonAsync(JsonOptions, cancellationToken);
+ var allow = verification?.Allow ?? verification?.Verified ?? false;
+ var reasons = verification?.Reasons ?? [];
+
+ return new AportVerificationResult(allow, reasons, verification?.Passport);
+ }
+
+ private static AportVerificationResult MockVerification(string agentId)
+ {
+ if (agentId.Equals("agt_denied", StringComparison.OrdinalIgnoreCase))
+ {
+ return new AportVerificationResult(false, ["Mock denial for demonstration."], null);
+ }
+
+ return new AportVerificationResult(true, ["Mock allow for local development."], null);
+ }
+}
+
+public sealed class AportVerifyResponse
+{
+ [JsonPropertyName("allow")]
+ public bool? Allow { get; init; }
+
+ [JsonPropertyName("verified")]
+ public bool? Verified { get; init; }
+
+ [JsonPropertyName("reasons")]
+ public string[] Reasons { get; init; } = [];
+
+ [JsonPropertyName("passport")]
+ public JsonElement? Passport { get; init; }
+}
+
+public static class AportEndpointFilterExtensions
+{
+ public static RouteHandlerBuilder RequireAportVerification(this RouteHandlerBuilder builder, string policy)
+ {
+ return builder.AddEndpointFilter(async (context, next) =>
+ {
+ var httpContext = context.HttpContext;
+ var agentId = httpContext.Request.Headers["X-Agent-Id"].FirstOrDefault();
+
+ if (string.IsNullOrWhiteSpace(agentId))
+ {
+ return Results.Problem(
+ title: "Missing APort agent identity",
+ detail: "Send the agent identifier in the X-Agent-Id header.",
+ statusCode: StatusCodes.Status400BadRequest);
+ }
+
+ var client = httpContext.RequestServices.GetRequiredService();
+ var result = await client.VerifyAsync(policy, agentId, new
+ {
+ path = httpContext.Request.Path.Value,
+ method = httpContext.Request.Method
+ }, httpContext.RequestAborted);
+
+ if (!result.Allow)
+ {
+ return Results.Problem(
+ title: "APort verification denied",
+ detail: result.Reasons.Count > 0
+ ? string.Join(" ", result.Reasons)
+ : "The agent is not allowed to perform this action.",
+ statusCode: StatusCodes.Status403Forbidden);
+ }
+
+ httpContext.Items["APortPassport"] = result.Passport;
+ return await next(context);
+ });
+ }
+}
diff --git a/examples/frameworks/aspnetcore-minimal-api/src/appsettings.json b/examples/frameworks/aspnetcore-minimal-api/src/appsettings.json
new file mode 100644
index 0000000..be4de8c
--- /dev/null
+++ b/examples/frameworks/aspnetcore-minimal-api/src/appsettings.json
@@ -0,0 +1,8 @@
+{
+ "APort": {
+ "BaseUrl": "https://api.aport.io",
+ "ApiKey": "",
+ "DefaultPolicy": "finance.payment.refund.v1",
+ "UseMock": true
+ }
+}