diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/GwpMinimalApiSample.csproj b/Samples/Genetec Web Player/GwpMinimalApiSample/GwpMinimalApiSample.csproj
new file mode 100644
index 0000000..a555ab3
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/GwpMinimalApiSample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+ GwpMinimalApiSample
+ Genetec.Dap.CodeSamples
+ Sample project
+ Genetec Inc.
+ Copyright © Genetec Inc. 2025
+
+
+
diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/Program.cs b/Samples/Genetec Web Player/GwpMinimalApiSample/Program.cs
new file mode 100644
index 0000000..54ac4a7
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/Program.cs
@@ -0,0 +1,62 @@
+// Copyright 2025 Genetec Inc.
+// Licensed under the Apache License, Version 2.0
+
+using System.Net.Http.Headers;
+using System.Text;
+
+var builder = WebApplication.CreateBuilder(args);
+
+var mediaGateway = builder.Configuration.GetSection("MediaGateway");
+var endpoint = mediaGateway["Endpoint"]?.TrimEnd('/') ?? throw new InvalidOperationException("MediaGateway:Endpoint is required.");
+var username = mediaGateway["Username"] ?? throw new InvalidOperationException("MediaGateway:Username is required.");
+var password = mediaGateway["Password"] ?? string.Empty;
+var sdkCertificate = mediaGateway["SdkCertificate"] ?? throw new InvalidOperationException("MediaGateway:SdkCertificate is required.");
+var serverVersion = mediaGateway["ServerVersion"];
+
+var authorizationParameter = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username};{sdkCertificate}:{password}"));
+
+builder.Services.AddHttpClient("MediaGateway", client =>
+{
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationParameter);
+}).ConfigurePrimaryHttpMessageHandler(() =>
+{
+ var handler = new HttpClientHandler();
+ if (builder.Environment.IsDevelopment())
+ {
+ handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
+ }
+ return handler;
+});
+
+var app = builder.Build();
+
+app.UseStaticFiles();
+
+app.MapGet("/api/config", () => Results.Ok(new
+{
+ mediaGatewayEndpoint = endpoint,
+ serverVersion = string.IsNullOrWhiteSpace(serverVersion) ? null : serverVersion.Trim(),
+}));
+
+app.MapGet("/api/token/{cameraId}", async (string cameraId, IHttpClientFactory httpClientFactory) =>
+{
+ if (string.IsNullOrWhiteSpace(cameraId))
+ {
+ return Results.BadRequest("A camera GUID is required.");
+ }
+
+ var client = httpClientFactory.CreateClient("MediaGateway");
+ var response = await client.GetAsync($"{endpoint}/v2/token/{Uri.EscapeDataString(cameraId.Trim())}");
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return Results.StatusCode((int)response.StatusCode);
+ }
+
+ var token = await response.Content.ReadAsStringAsync();
+ return Results.Text(token);
+});
+
+app.MapFallbackToFile("index.html");
+
+app.Run();
diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/Properties/launchSettings.json b/Samples/Genetec Web Player/GwpMinimalApiSample/Properties/launchSettings.json
new file mode 100644
index 0000000..a626612
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "GwpMinimalApiSample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:59205;http://localhost:59206"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/README.md b/Samples/Genetec Web Player/GwpMinimalApiSample/README.md
new file mode 100644
index 0000000..7508597
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/README.md
@@ -0,0 +1,77 @@
+# GWP Minimal API Sample
+
+This sample demonstrates hosting the Genetec Web Player inside an ASP.NET Core Minimal API application:
+
+- ASP.NET Core serves a static HTML page that loads and runs GWP.
+- A server-side `/api/token/{cameraId}` endpoint proxies token requests to the Media Gateway using credentials from `appsettings.json`. Media Gateway credentials never reach the browser.
+- A `/api/config` endpoint provides the Media Gateway endpoint and server version to the page without exposing authentication details.
+- The page loads `gwp.js` directly from the target Media Gateway and uses the browser environment GWP expects.
+
+## Run
+
+```powershell
+dotnet run
+```
+
+Then open the URL shown in the console output (for example, `https://localhost:5001`).
+
+### Configuration
+
+Media Gateway settings are configured in `appsettings.json`:
+
+```json
+{
+ "MediaGateway": {
+ "Endpoint": "https://localhost/media",
+ "Username": "admin",
+ "Password": "",
+ "SdkCertificate": "KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv",
+ "ServerVersion": ""
+ }
+}
+```
+
+You can also use environment variables or user secrets. If you haven't already initialized user secrets for this project, run:
+
+```powershell
+dotnet user-secrets init
+dotnet user-secrets set "MediaGateway:Password" "your-password"
+```
+
+## Required environment setup
+
+### 1. Trust the Media Gateway certificate
+
+If the Media Gateway certificate is self-signed or otherwise untrusted, the browser will fail to load `gwp.js` or connect to the gateway.
+
+For development, the sample automatically allows certificate warnings when connecting to the Media Gateway from the server-side token endpoint. The browser must still trust the certificate for the `gwp.js` script load and WebSocket connections. Add the certificate to the browser's trust store or use a trusted certificate.
+
+### 2. Allow the page origin in Media Gateway CORS
+
+If strict CORS is enabled, add the ASP.NET application origin to `MediaGateway.gconfig`:
+
+```xml
+
+
+
+
+
+
+```
+
+Restart the Media Gateway role after the change.
+
+### 3. Use a matching GWP build
+
+The sample loads `gwp.js` from `${mediaGatewayEndpoint}/v2/files/gwp.js` so the player version matches the Security Center version.
+
+## Scope and limitations
+
+- This sample demonstrates a feasible hosting pattern. It is not a production-ready security design.
+- This sample has no user authentication. Anyone who can reach the application can view camera streams. A real application must add its own authentication layer to control who can access the application.
+- Media Gateway credentials are stored in `appsettings.json`. For production, use a secure configuration provider such as user secrets, Azure Key Vault, or environment variables.
+- The default SDK certificate is the Genetec development certificate intended for SDK development only.
+- A Content Security Policy meta tag restricts script sources, connections, and media to `self`, `https:`, `wss:`, and `blob:`. The policy allows `'unsafe-inline'` for scripts and styles because the page uses inline markup. The Razor Pages sample demonstrates how to use CSP nonces instead.
+- Player startup is cancellable. Clicking Stop during script load or session establishment cancels the in-flight start and cleans up any partially created player.
+- Browser autoplay rules apply to audio.
+- Video rendering and overlays remain in the HTML layer, not the ASP.NET server.
diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/appsettings.json b/Samples/Genetec Web Player/GwpMinimalApiSample/appsettings.json
new file mode 100644
index 0000000..a5e1b2a
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "MediaGateway": {
+ "Endpoint": "https://localhost/media",
+ "Username": "admin",
+ "Password": "",
+ "SdkCertificate": "KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv",
+ "ServerVersion": ""
+ }
+}
diff --git a/Samples/Genetec Web Player/GwpMinimalApiSample/wwwroot/index.html b/Samples/Genetec Web Player/GwpMinimalApiSample/wwwroot/index.html
new file mode 100644
index 0000000..c1255b5
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpMinimalApiSample/wwwroot/index.html
@@ -0,0 +1,549 @@
+
+
+
+
+
+
+ GWP Minimal API Sample
+
+
+
+
+
+
+
+
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/GwpRazorPagesSample.csproj b/Samples/Genetec Web Player/GwpRazorPagesSample/GwpRazorPagesSample.csproj
new file mode 100644
index 0000000..ac3a5c7
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/GwpRazorPagesSample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+ GwpRazorPagesSample
+ Genetec.Dap.CodeSamples
+ Sample project
+ Genetec Inc.
+ Copyright © Genetec Inc. 2025
+
+
+
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml b/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml
new file mode 100644
index 0000000..15ed63e
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml
@@ -0,0 +1,540 @@
+@page
+@using System.Text.Json
+@model Genetec.Dap.CodeSamples.Pages.IndexModel
+@{
+ Layout = null;
+ var nonce = Model.CspNonce;
+}
+
+
+
+
+
+ GWP Razor Pages Sample
+
+
+
+
+
+
+
+
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml.cs b/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml.cs
new file mode 100644
index 0000000..0d9d9a5
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/Pages/Index.cshtml.cs
@@ -0,0 +1,29 @@
+// Copyright 2025 Genetec Inc.
+// Licensed under the Apache License, Version 2.0
+
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Genetec.Dap.CodeSamples.Pages;
+
+public class IndexModel : PageModel
+{
+ public string MediaGatewayEndpoint { get; private set; } = string.Empty;
+
+ public string? ServerVersion { get; private set; }
+
+ public string CspNonce { get; private set; } = string.Empty;
+
+ private readonly MediaGatewayOptions m_options;
+
+ public IndexModel(MediaGatewayOptions options)
+ {
+ m_options = options;
+ }
+
+ public void OnGet()
+ {
+ MediaGatewayEndpoint = m_options.Endpoint;
+ ServerVersion = m_options.ServerVersion;
+ CspNonce = HttpContext.Items["CspNonce"] as string ?? string.Empty;
+ }
+}
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/Program.cs b/Samples/Genetec Web Player/GwpRazorPagesSample/Program.cs
new file mode 100644
index 0000000..3405cb6
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/Program.cs
@@ -0,0 +1,86 @@
+// Copyright 2025 Genetec Inc.
+// Licensed under the Apache License, Version 2.0
+
+using System.Net.Http.Headers;
+using System.Security.Cryptography;
+using System.Text;
+
+var builder = WebApplication.CreateBuilder(args);
+
+var mediaGateway = builder.Configuration.GetSection("MediaGateway");
+var endpoint = mediaGateway["Endpoint"]?.TrimEnd('/') ?? throw new InvalidOperationException("MediaGateway:Endpoint is required.");
+var username = mediaGateway["Username"] ?? throw new InvalidOperationException("MediaGateway:Username is required.");
+var password = mediaGateway["Password"] ?? string.Empty;
+var sdkCertificate = mediaGateway["SdkCertificate"] ?? throw new InvalidOperationException("MediaGateway:SdkCertificate is required.");
+var serverVersion = mediaGateway["ServerVersion"];
+
+var authorizationParameter = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username};{sdkCertificate}:{password}"));
+
+builder.Services.AddRazorPages();
+
+builder.Services.AddHttpClient("MediaGateway", client =>
+{
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationParameter);
+}).ConfigurePrimaryHttpMessageHandler(() =>
+{
+ var handler = new HttpClientHandler();
+ if (builder.Environment.IsDevelopment())
+ {
+ handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
+ }
+ return handler;
+});
+
+builder.Services.AddSingleton(new MediaGatewayOptions
+{
+ Endpoint = endpoint,
+ ServerVersion = string.IsNullOrWhiteSpace(serverVersion) ? null : serverVersion.Trim(),
+});
+
+var app = builder.Build();
+
+app.Use(async (context, next) =>
+{
+ var nonce = Convert.ToBase64String(RandomNumberGenerator.GetBytes(16));
+ context.Items["CspNonce"] = nonce;
+
+ context.Response.Headers["Content-Security-Policy"] = string.Join("; ",
+ "default-src 'self' https:",
+ $"script-src 'self' https: 'nonce-{nonce}'",
+ $"style-src 'self' 'nonce-{nonce}'",
+ "connect-src 'self' https: wss:",
+ "media-src https: blob:");
+
+ await next();
+});
+
+app.UseStaticFiles();
+app.UseRouting();
+app.MapRazorPages();
+
+app.MapGet("/api/token/{cameraId}", async (string cameraId, IHttpClientFactory httpClientFactory) =>
+{
+ if (string.IsNullOrWhiteSpace(cameraId))
+ {
+ return Results.BadRequest("A camera GUID is required.");
+ }
+
+ var client = httpClientFactory.CreateClient("MediaGateway");
+ var response = await client.GetAsync($"{endpoint}/v2/token/{Uri.EscapeDataString(cameraId.Trim())}");
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return Results.StatusCode((int)response.StatusCode);
+ }
+
+ var token = await response.Content.ReadAsStringAsync();
+ return Results.Text(token);
+});
+
+app.Run();
+
+public class MediaGatewayOptions
+{
+ public required string Endpoint { get; init; }
+ public string? ServerVersion { get; init; }
+}
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/Properties/launchSettings.json b/Samples/Genetec Web Player/GwpRazorPagesSample/Properties/launchSettings.json
new file mode 100644
index 0000000..c65cb95
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "GwpRazorPagesSample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:65426;http://localhost:65427"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Genetec Web Player/GwpRazorPagesSample/README.md b/Samples/Genetec Web Player/GwpRazorPagesSample/README.md
new file mode 100644
index 0000000..82e68ef
--- /dev/null
+++ b/Samples/Genetec Web Player/GwpRazorPagesSample/README.md
@@ -0,0 +1,93 @@
+# GWP Razor Pages Sample
+
+This sample demonstrates hosting the Genetec Web Player inside an ASP.NET Core Razor Pages application with production-ready CSP nonce support:
+
+- ASP.NET Core serves a Razor Page that loads and runs GWP.
+- The page is server-rendered, so the Media Gateway endpoint and server version are injected directly into the markup. No client-side configuration fetch is needed.
+- A per-request cryptographic CSP nonce is generated in middleware, added to the `Content-Security-Policy` response header, and passed to each `