Command Center API — Solution Architecture Document
Version: 1.0
Date: July 2026
Author: Pete Carroll, Founder — Traxs Group LLC
Status: Draft
1. Architecture Overview
The Command Center API is a lightweight .NET 10 API built with the same Clean Architecture as RoundTrip and Waypoint. It has no database in v1 — it is a pure pass-through and aggregation layer between products and external services.
2. System Context
┌─────────────────────────────────────────────────────────────────────┐
│ COMMAND CENTER API CONSUMERS │
│ │
│ Browser Forms Waypoint API Relay API (future) │
│ (roundtrips.app (service-to- (service-to- │
│ traxsgroup.com) service) service) │
└──────────┬──────────────────┬────────────────────┬──────────────────┘
│ HTTPS (public) │ HTTPS + API Key │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────┐
│ COMMAND CENTER API │
│ api.traxsgroup.com │
│ Azure App Service — app-commandcenter-production │
│ .NET 10 · FastEndpoints · Cyrus Mediator │
│ No database (v1) │
└──────────┬───────────────────────┬──────────────────────────────────┘
│ │
┌──────▼──────┐ ┌───────────▼──────────────────────────┐
│ SENDGRID │ │ ROUNDTRIP API │
│ Email │ │ api.roundtrips.app │
│ delivery │ │ (service-to-service, internal token) │
└─────────────┘ └───────────────────────────────────────┘
3. Solution Structure
CommandCenterAPI/
CommandCenter.API.Core/
Interfaces/
IEmailService.cs
IRoundTripClient.cs ← new
ISuiteSubscriptionService.cs ← new
Models/
EmailMessage.cs
ContactRequest.cs
RoundTripRevenueSummary.cs ← new
RoundTripTicketSummary.cs ← new
SuiteSubscriptions.cs ← new
Enums/
ContactRequestType.cs
CommandCenter.API.UseCases/
Features/
Contact/
SubmitContactCommand.cs
SubmitContactHandler.cs
SubmitContactValidator.cs
Support/ ← new
SubmitSupportCommand.cs
SubmitSupportHandler.cs
Feedback/ ← new
SubmitFeedbackCommand.cs
SubmitFeedbackHandler.cs
RoundTrip/ ← new
GetRevenueSummaryQuery.cs
GetRevenueSummaryHandler.cs
GetOpenTicketsQuery.cs
GetOpenTicketsHandler.cs
GetRecentInvoicesQuery.cs
GetRecentInvoicesHandler.cs
GetTeamQuery.cs
GetTeamHandler.cs
GetARAgingQuery.cs
GetARAgingHandler.cs
Suite/ ← new
GetSubscriptionsQuery.cs
GetSubscriptionsHandler.cs
CommandCenter.API.Infrastructure/
Services/
SendGridEmailService.cs
RoundTripClient.cs ← new — calls RoundTrip API
StripeSubscriptionService.cs ← new — queries Stripe for suite subscriptions
Extensions/
InfrastructureServiceExtensions.cs
CommandCenter.API.Web/
Endpoints/
Contact/
SubmitContactEndpoint.cs
Support/ ← new
SubmitSupportEndpoint.cs
Feedback/ ← new
SubmitFeedbackEndpoint.cs
RoundTrip/ ← new
GetRevenueSummaryEndpoint.cs
GetOpenTicketsEndpoint.cs
GetRecentInvoicesEndpoint.cs
GetTeamEndpoint.cs
GetARAgingEndpoint.cs
Suite/ ← new
GetSubscriptionsEndpoint.cs
HealthEndpoint.cs
Middleware/
ApiKeyAuthMiddleware.cs ← new — validates X-CommandCenter-ApiKey
Program.cs
4. Authentication Implementation
4.1 Public Endpoint Authentication
Public endpoints (/v1/contact, /v1/support, /v1/feedback, /health) use AllowAnonymous() in FastEndpoints. CORS policy restricts to Traxs domains.
Rate limiting via ASP.NET Core rate limiting middleware:
// Program.cs
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("public", limiter =>
{
limiter.PermitLimit = 10;
limiter.Window = TimeSpan.FromMinutes(1);
limiter.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
limiter.QueueLimit = 0;
});
});
4.2 Service-to-Service Authentication
Service endpoints validate the X-CommandCenter-ApiKey header via a custom middleware:
public sealed class ApiKeyAuthMiddleware(
IConfiguration configuration,
RequestDelegate next)
{
private const string ApiKeyHeader = "X-CommandCenter-ApiKey";
public async Task InvokeAsync(HttpContext context)
{
// Skip auth for public endpoints
var endpoint = context.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<AllowAnonymousAttribute>() != null)
{
await next(context);
return;
}
if (!context.Request.Headers.TryGetValue(
ApiKeyHeader, out var extractedApiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(
new { success = false, errorMessage = "API key required." });
return;
}
var apiKey = configuration["CommandCenter:ApiKey"];
if (!apiKey.Equals(extractedApiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(
new { success = false, errorMessage = "Invalid API key." });
return;
}
await next(context);
}
}
5. RoundTrip Client Implementation
The RoundTripClient calls the RoundTrip API using service-to-service auth. RoundTrip needs to expose internal endpoints for this purpose.
public sealed class RoundTripClient : IRoundTripClient
{
private readonly HttpClient _http;
private readonly ILogger<RoundTripClient> _logger;
public RoundTripClient(
HttpClient http,
ILogger<RoundTripClient> logger)
{
_http = http;
_logger = logger;
}
public async Task<RoundTripRevenueSummary?> GetRevenueSummaryAsync(
Guid tenantId,
string period,
CancellationToken ct)
{
try
{
var response = await _http.GetAsync(
$"/v1/internal/revenue-summary?tenantId={tenantId}&period={period}",
ct);
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
return await response.Content
.ReadFromJsonAsync<RoundTripRevenueSummary>(ct);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Failed to get revenue summary from RoundTrip for tenant {TenantId}",
tenantId);
return null; // Graceful degradation — Waypoint handles null
}
}
}
Registered with HttpClientFactory in DI:
builder.Services.AddHttpClient<IRoundTripClient, RoundTripClient>(client =>
{
client.BaseAddress = new Uri(
configuration["RoundTripApi:BaseUrl"]
?? throw new InvalidOperationException(
"RoundTripApi:BaseUrl is not configured."));
client.DefaultRequestHeaders.Add(
"X-Internal-ApiKey",
configuration["RoundTripApi:InternalApiKey"]
?? throw new InvalidOperationException(
"RoundTripApi:InternalApiKey is not configured."));
client.Timeout = TimeSpan.FromSeconds(10);
});
6. RoundTrip Internal Endpoints (New endpoints needed in RoundTrip API)
The Command Center API requires RoundTrip to expose these internal endpoints. These are service-to-service only — not accessible from the browser.
| Endpoint | What It Returns |
|---|---|
GET /v1/internal/revenue-summary | MTD/YTD revenue from paid invoices |
GET /v1/internal/tickets/open | Open ticket count, status breakdown, estimated value |
GET /v1/internal/invoices/recent | Last N invoices with status |
GET /v1/internal/team | Active TenantUsers with roles |
GET /v1/internal/ar/aging | Outstanding invoices by age bucket |
All internal endpoints:
- Require
X-Internal-ApiKeyheader - Include
tenantIdas query parameter - Return 404 if tenant not found
- Return data scoped to the specified tenant
These will be added as Linear tickets under TRA-NNN when Waypoint M7 is reached.
7. Infrastructure
7.1 Azure Resources (Current)
| Resource | Name |
|---|---|
| Resource Group | rg-commandcenter-production |
| App Service | app-commandcenter-production |
| Key Vault | kv-commandcenter-pro |
| Custom Domain | api.traxsgroup.com |
7.2 Key Vault Secrets (Current + Planned)
| Secret | Purpose | Status |
|---|---|---|
SendGrid--ApiKey | Email delivery | ✅ Live |
CommandCenter--ApiKey | Inbound service-to-service auth | ⏳ Add for Waypoint |
RoundTripApi--BaseUrl | RoundTrip API URL | ⏳ Add for Waypoint |
RoundTripApi--InternalApiKey | Outbound auth to RoundTrip | ⏳ Add for Waypoint |
Stripe--SecretKey | Suite subscription queries | ⏳ Add for Waypoint |
7.3 CORS Configuration
Update appsettings.json when Waypoint launches:
{
"Cors": {
"AllowedOrigins": [
"https://roundtrips.app",
"https://www.roundtrips.app",
"https://traxsgroup.com",
"https://www.traxsgroup.com",
"https://roundtrip-marketing.pages.dev",
"https://traxswaypoint.app",
"https://dev.traxswaypoint.app"
]
}
}
Note: Service-to-service calls from Waypoint API are server-to-server — they don't go through the browser so CORS doesn't apply to them. CORS only matters for browser-facing public endpoints.
8. Graceful Degradation
The Command Center API must never become a single point of failure. If it is unavailable:
- Contact forms: show "Unable to send — please email us directly at hello@traxsgroup.com"
- Waypoint dashboard: RT-dependent widgets show "RoundTrip data temporarily unavailable — check back shortly"
- Suite switcher: hides products that can't be confirmed (conservative — show fewer options)
Products must implement timeouts on all Command Center API calls (10 seconds max) and handle null/error responses gracefully.
9. Growth Path
The Command Center API grows incrementally as products are added:
Current (July 2026):
Contact form → SendGrid
Waypoint launch (Q1 2027):
+ RoundTrip data endpoints (5 endpoints)
+ Suite subscriptions endpoint
+ Support/feedback endpoints
+ API key authentication
Relay launch (Q3 2027):
+ Lead sync endpoint (Relay → RoundTrip)
+ Client sync endpoint (bidirectional)
+ Campaign trigger events
2028+:
+ Unified billing hub (Stripe orchestration)
+ AI inference gateway (Anthropic API routing)
+ Webhook management (event bus)
+ Platform analytics
10. Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | July 2026 | Pete Carroll | Initial draft |