Skip to main content

ADR-007: Platform API vs RoundTrip API

Date: 2026-07-04
Status: Accepted
Deciders: Pete Carroll


Context

As Traxs Group LLC grows beyond RoundTrip to include Apex, Waypoint, and Relay, certain cross-cutting concerns need a home. The immediate trigger was the need to handle contact forms on traxsgroup.com and roundtrips.app without using mailto: links — which are unprofessional and provide no control over routing, formatting, or delivery confirmation.

Two options were evaluated:

  1. Add contact form endpoints directly to the RoundTrip API
  2. Build a separate Platform API (api.traxsgroup.com) that serves all Traxs Group products

Additionally, the existing RoundTrip API is a multi-tenant SaaS backend with JWT authentication, tenant scoping, EF Core, Hangfire, and SignalR — significant infrastructure that is overkill and inappropriate for serving unauthenticated public contact forms from a marketing website.


Decision

Build a separate Command Center API hosted at api.traxsgroup.com as the company-level platform API for Traxs Group LLC.

The Command Center API:

  • Follows the same Clean Architecture as RoundTrip (FastEndpoints, Cyrus Mediator, same patterns and conventions)
  • Has no database initially — pure pass-through to SendGrid and Crisp
  • Is unauthenticated for public endpoints (contact forms)
  • Will gain authentication and persistence as scope expands
  • Serves all Traxs Group products, not just RoundTrip

Initial endpoints:

POST /v1/contact ← contact form submissions (traxsgroup.com, roundtrips.app)
POST /v1/support ← support requests
POST /v1/feedback ← in-app feedback from RoundTrip
GET /health

Alternatives Considered

Option A — Add endpoints to RoundTrip API

Rejected. The RoundTrip API is a tenant-scoped, authenticated SaaS backend. Adding unauthenticated public endpoints violates the architectural intent and creates security surface area. It also couples company-level concerns (contact forms for traxsgroup.com) to a product-level API, making it harder to serve future Traxs products. Any developer working on RoundTrip would need to understand company-level routing concerns that have nothing to do with field service management.

Option B — Use a third-party form service (Formspree, Netlify Forms)

Rejected. Creates a dependency on a third-party service for a core business function. No control over email formatting, delivery confirmation, or routing logic. Cannot integrate with Crisp for support ticket creation. Cannot be extended as needs grow.

Option C — Astro server endpoints only (no separate API)

Partially adopted. Astro server endpoints handle the browser-facing form submission and keep API keys server-side. However, they delegate to the Command Center API rather than calling SendGrid directly — this keeps email delivery logic centralized and reusable across multiple frontends (traxsgroup.com, roundtrips.app, future products).


Consequences

What becomes easier:

  • Any Traxs Group product can send emails, handle contact forms, or create support tickets by calling api.traxsgroup.com — one integration point
  • RoundTrip API stays clean and focused on field service management concerns
  • Future products (Apex, Waypoint, Relay) have a ready-made API to integrate with
  • A developer familiar with RoundTrip can work on the Command Center API immediately — same patterns, same conventions
  • The Command Center API is the natural home for future cross-product features: unified billing webhooks, tenant provisioning, platform-level notifications

What becomes harder:

  • One more Azure App Service to maintain, monitor, and deploy
  • One more ADO pipeline to manage
  • Cross-product features require coordination between two APIs until a shared library pattern is established

What this enables long-term:

  • Relay — the AI-powered support platform — will be built on top of the Command Center API infrastructure
  • Tenant provisioning — when a customer signs up for any Traxs product, the Command Center API will handle the provisioning workflow
  • Platform billing — Stripe webhook handling at the platform level rather than per-product

Architecture

TRAXS GROUP PLATFORM

Browser / PWA

├─ roundtrips.app ─────────────► RoundTrip API (api.roundtrips.app)
│ (Astro marketing site) Multi-tenant SaaS backend
│ (React app) JWT auth, tenant scoping
│ EF Core, Hangfire, SignalR

├─ traxsgroup.com ─────────────► Command Center API (api.traxsgroup.com)
│ (contact forms) No database (initially)
│ Unauthenticated public endpoints
└─ Any future product SendGrid, Crisp integration
Same Clean Architecture as RoundTrip

Implementation Notes

  • Same solution structure as RoundTrip: Domain, UseCases, Infrastructure, Web projects
  • No DbContext initially — add when persistence is needed
  • SendGridEmailService extracted to a shared NuGet package when both APIs need it (future)
  • Deployed to Azure App Service, Central US, same region as RoundTrip
  • Custom domain: api.traxsgroup.com bound via Azure App Service custom domains
  • ADO pipeline mirrors RoundTrip API pipeline pattern