Skip to main content
Version: 2026.04.0

Live Demo API Blueprint (.NET)

Try in Playground

Packages to install

dotnet add package PrimusSaaS.Identity.Validator
dotnet add package PrimusSaaS.Logging
dotnet add package PrimusSaaS.Notifications
dotnet add package PrimusSaaS.Documents

Baseline configuration (appsettings.*)

{
"PrimusLogging": {
"ApplicationId": "LiveDemoApi",
"Environment": "Development",
"MinLevel": 1,
"Targets": [
{ "Type": "console", "Pretty": true },
{
"Type": "file",
"Path": "logs/livedemo-api.log",
"Async": true,
"MaxFileSize": 10485760,
"MaxRetainedFiles": 5,
"CompressRotatedFiles": true
}
],
"Pii": {
"MaskEmails": true,
"MaskCreditCards": true,
"MaskSSN": true,
"MaskPasswords": true,
"MaskTokens": true,
"MaskSecrets": true
}
},
"PrimusIdentity": {
"RequireHttpsMetadata": true,
"ValidateLifetime": true,
"ClockSkew": "00:05:00",
"Issuers": [
{
"Name": "Auth0",
"Type": "Auth0",
"Authority": "https://YOUR-TENANT.auth0.com/",
"Issuer": "https://YOUR-TENANT.auth0.com/",
"Audiences": [ "https://your-api-identifier/" ]
},
{
"Name": "LocalDev",
"Type": "Jwt",
"Issuer": "https://primus.local",
"Audiences": [ "api://primus-livedemo" ],
"Secret": "your-32-character-minimum-secret-key-here"
}
],
"Diagnostics": {
"EnableDetailedErrors": true,
"IncludeTokenHintsInChallenges": true,
"IncludeDebugHeaders": true,
"LogTokenRejectionReasons": true,
"MaxRecentFailures": 50,
"AutoDetectDevelopment": true
}
},
"Notifications": {
"Smtp": {
"Host": "",
"Port": 587,
"Username": "",
"Password": "",
"EnableSsl": true,
"FromAddress": "",
"FromName": "Primus Live Demo"
},
"Twilio": {
"AccountSid": "",
"AuthToken": "",
"FromNumber": "",
"MessagingServiceSid": "",
"ValidateOnStartup": false
}
},
"PrimusDocuments": {
"Provider": "Default",
"TempStoragePath": "temp/documents",
"MaxContentLength": 20000,
"LinkTtl": "00:10:00",
"SelfTestEnabled": true,
"TitleFontSize": 24,
"BodyFontSize": 12,
"BrandName": "Primus Live Demo",
"IncludeTimestampInFooter": true,
"IncludeTenantInFooter": true
},
"PrimusFeatureFlags": {
"Provider": "InMemory",
"DefaultValue": false,
"CacheDurationSeconds": 30,
"Logging": {
"LogEvaluations": true,
"IncludeUserContext": false
},
"Flags": {
"NewDashboard": {
"Enabled": true,
"Description": "Enable the new dashboard UI experience",
"RolloutPercentage": 100
}
}
},
"DemoLocalAuth": {
"Email": "demo@primus.local",
"Password": "",
"Name": "Primus Demo User",
"Subject": "local-demo-user"
}
}

Store secrets in User Secrets/Key Vault/App Service settings; dont commit them.

Service registration (Program.cs)

using PrimusSaaS.Identity.Validator;
using PrimusSaaS.Notifications;
using PrimusSaaS.Logging.Extensions;
using Primus.Documents;

var builder = WebApplication.CreateBuilder(args);

// Logging + Application Insights
builder.Logging.ClearProviders();
builder.Logging.AddPrimus(opts => builder.Configuration.GetSection("PrimusLogging").Bind(opts));
var ai = builder.Configuration["PrimusLogging:ApplicationInsights:ConnectionString"];
if (!string.IsNullOrWhiteSpace(ai) && ai != "your-application-insights-connection-string")
builder.Services.AddApplicationInsightsTelemetry(o => o.ConnectionString = ai);

// Primus modules
builder.Services.AddPrimusIdentity(o => builder.Configuration.GetSection("PrimusIdentity").Bind(o));
builder.Services.AddPrimusNotifications(notifications =>
{
var templatesPath = Path.Combine(builder.Environment.ContentRootPath, "NotificationTemplates");
notifications.UseFileTemplates(templatesPath, validateOnStartup: true, watchForChanges: builder.Environment.IsDevelopment());
notifications.UseLogger();
notifications.UseInMemoryQueue(o =>
{
o.BoundedCapacity = 500;
o.MaxParallelHandlers = 2;
o.BaseRetryDelayMs = 250;
});
notifications.UseSmtp(builder.Configuration.GetSection("Notifications:Smtp"));
notifications.UseTwilio(builder.Configuration, "Notifications:Twilio", validateOnStartup: false);
notifications.ConfigureDispatch(o =>
{
o.ThrowOnFailure = true;
o.FallbackToLogger = false;
o.QueueOnFailure = false;
});
});
builder.Services.AddPrimusDocumentRenderer(o => builder.Configuration.GetSection("PrimusDocuments").Bind(o));

builder.Services.AddAuthorization();
builder.Services.AddCors(o => o.AddPolicy("AllowFrontend", policy =>
policy.WithOrigins("http://localhost:5173", "https://localhost:5173")
.AllowAnyHeader()
.AllowAnyMethod()));

Middleware order

app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
app.UsePrimusLogging(); // request logging + correlation IDs
app.UseAuthentication(); // from AddPrimusIdentity
app.UseAuthorization();

Keep authentication/authorization between HTTPS redirection and your endpoints. The sample exposes /public (no auth) and /secure (auth) instead of a root endpoint; hit those when testing.

Golden-path endpoints to keep or adapt

These come from the Live Demo; remove them for production or put them behind strong auth (and role checks) if you must keep them.

EndpointPurpose
GET /primus/diagnosticsIssuer/config diagnostics (optional)
GET /whoamiAuthenticated user context echo
POST /log/testLogging demo with PII redaction
POST /notifications/testRender PasswordReset template without sending
GET /notifications/healthChannel health snapshot
POST /notifications/welcomeSend welcome email (Email + Logger)
POST /notifications/smsSend SMS via Twilio or logger
POST /notifications/templates/previewRender template content safely
GET/PUT /notifications/templates/{type}/{channel}Retrieve/update Liquid templates
GET /notifications/templatesList available templates
POST /documents/renderRender text/HTML/Markdown (plain-text output) to PDF (direct bytes)
POST /documents/render/linkRender to PDF and return a one-time download token
GET /documents/download/{token}Consume a token and download PDF
POST /documents/self-testRenderer self-diagnostics (Basic/Extended)
GET /telemetry/summaryApp Insights + process stats (dev-only)
GET /logs/recentTail the demo log file (dev-only; remove or lock down)
POST /auth/auth0Auth0 client-credentials proxy (dev-only; remove)
POST /auth/azureAzure CLI token proxy (dev-only; remove)
POST /auth/localLocal shared-secret JWT for demos

Production hardening checklist

  • Remove /auth/auth0, /auth/azure, /logs/recent, and any demo secrets; if temporarily kept, require strict auth and audit.
  • Disable IdentityModelEventSource.ShowPII outside local dev.
  • Lock down CORS origins to real frontends; require HTTPS everywhere.
  • Swap the in-memory notification queue for a durable provider in production.
  • Store SMTP/Twilio secrets in Key Vault/User Secrets; never in appsettings.json.
  • Protect or remove template management endpoints; require auth/roles on any that remain.

Quick verification steps

  • GET /whoami returns authenticated user context; 401 when unauthenticated.
  • POST /notifications/test renders templates without error.
  • GET /feature-flags/test returns definitions with user context.
  • POST /documents/self-test returns success: true.
  • Application Insights receives request/dependency telemetry when configured.