Skip to main content

Command Center API — Product Requirements Document

Version: 1.0
Date: July 2026
Author: Pete Carroll, Founder — Traxs Group LLC
Status: Draft


1. Overview

This document defines the detailed requirements for each Command Center API endpoint — what it accepts, what it returns, how it behaves, and how errors are handled.


2. Authentication Model

The Command Center API has two authentication tiers:

2.1 Public Endpoints (Unauthenticated)

Used by browser-facing forms on marketing and company sites. No API key required but CORS restricts to Traxs domains.

Public endpoints:

  • POST /v1/contact
  • POST /v1/support
  • POST /v1/feedback
  • GET /health

Rate limiting: 10 requests per minute per IP address.

CORS allowed origins:

[
"https://roundtrips.app",
"https://www.roundtrips.app",
"https://traxsgroup.com",
"https://www.traxsgroup.com",
"https://roundtrip-marketing.pages.dev"
]

2.2 Service-to-Service Endpoints (API Key)

Used by Waypoint API, Relay API, and future products. Requires X-CommandCenter-ApiKey header.

Service endpoints:

  • All /v1/roundtrip/* endpoints
  • All /v1/suite/* endpoints
  • All /v1/clients/* endpoints
  • All /v1/relay/* endpoints (future)

API key storage: CommandCenter--ApiKey in each consuming product's Key Vault. The Command Center API validates against its own stored key.

2.3 Authentication User Stories

FA01: As a user I expect to be able to log into the system through Keystone and the system knows what subscription I am on and what products I have subscribed to.

USA01: Authentication workflow retrieves users subscription by TenantId

USA02: Authentication workflow retrieves users product entitlements by TanantId


3. Endpoint Specifications


3.1 Health Check

GET /health
Public. No auth required.

Response 200:

{ }

3.2 Contact Form

POST /v1/contact
Public. Unauthenticated. Rate limited.

Request body:

{
"firstName": "Pete",
"lastName": "Carroll",
"email": "pete@example.com",
"company": "Carroll HVAC",
"message": "I'd like to see a demo.",
"type": 0
}

ContactRequestType enum:

ValueNameDescription
0GeneralInquiryGeneral question
1SupportSupport request
2FeedbackProduct feedback
3DemoRequestDemo booking request

Validation:

  • firstName — required, max 100 chars
  • lastName — required, max 100 chars
  • email — required, valid email format, max 255 chars
  • message — required, max 5000 chars
  • company — optional, max 200 chars
  • type — required, valid enum value

On success:

  1. Send confirmation email to submitter via SendGrid
    • From: noreply@roundtrips.app
    • From name: RoundTrip by Traxs Group LLC
    • Subject: We received your message — RoundTrip by Traxs Group LLC
  2. Send notification email to hello@traxsgroup.com
    • Subject: [{type}] New contact from {firstName} {lastName}
    • Body: full submission details

Response 200:

{
"success": true,
"errorMessage": null
}

Response 422 (validation failure):

{
"success": false,
"errorMessage": "Email address is required."
}

Response 500 (SendGrid failure):

{
"success": false,
"errorMessage": "Failed to send email."
}

3.3 Support Request

POST /v1/support
Public. Unauthenticated. Rate limited.

Request body:

{
"firstName": "Mike",
"lastName": "Callahan",
"email": "mike@hvacpros.com",
"company": "HVAC Pros LLC",
"subject": "Invoice not sending",
"message": "When I click Send Invoice nothing happens.",
"product": "RoundTrip",
"priority": "Normal"
}

Validation: Same as contact form plus:

  • subject — required, max 200 chars
  • product — required, one of: RoundTrip, Waypoint, Relay, General
  • priority — optional, one of: Low, Normal, High, Critical

On success:

  1. Send confirmation email to submitter
  2. Send notification email to support@roundtrips.app
  3. Future: Create Crisp ticket via Crisp API

Response: Same pattern as /v1/contact


3.4 In-App Feedback

POST /v1/feedback
Public. Rate limited. Called from within Traxs product apps.

Request body:

{
"email": "mike@hvacpros.com",
"name": "Mike Callahan",
"product": "RoundTrip",
"category": "FeatureRequest",
"message": "It would be great if I could schedule recurring jobs.",
"currentPage": "/tickets"
}

On success:

  1. Send notification to hello@traxsgroup.com
  2. Future: Create Linear issue via Linear API

Response: Same pattern as /v1/contact


3.5 RoundTrip Revenue Summary

GET /v1/roundtrip/revenue-summary
Service-to-service. Requires X-CommandCenter-ApiKey header.

Query parameters:

ParameterTypeRequiredDescription
tenantIdGuidYesThe Traxs tenant ID
periodstringNomtd (default), ytd, last30, last90

How it works:

  1. Command Center API receives request from Waypoint
  2. Validates API key
  3. Calls RoundTrip API: GET /v1/internal/revenue-summary?tenantId={id}&period={period}
  4. RoundTrip API validates that the tenantId exists and has an active subscription
  5. Returns revenue data
  6. Command Center API translates to its own DTO and returns to Waypoint

Response 200:

{
"tenantId": "ad05b136-e770-4650-b563-75b7dad17234",
"period": "mtd",
"totalRevenue": 12480.00,
"currency": "USD",
"paidInvoiceCount": 14,
"fromDate": "2026-07-01",
"toDate": "2026-07-07",
"asOf": "2026-07-07T20:30:00Z"
}

Response 404: Tenant not found or not subscribed to RoundTrip
Response 503: RoundTrip API unavailable — Waypoint handles gracefully


3.6 RoundTrip Open Tickets

GET /v1/roundtrip/tickets/open
Service-to-service.

Query parameters:

ParameterTypeRequired
tenantIdGuidYes

Response 200:

{
"tenantId": "...",
"totalOpen": 8,
"unassigned": 3,
"inProgress": 2,
"assigned": 3,
"estimatedValue": 3600.00,
"currency": "USD",
"asOf": "2026-07-07T20:30:00Z"
}

3.7 RoundTrip Recent Invoices

GET /v1/roundtrip/invoices/recent
Service-to-service.

Query parameters:

ParameterTypeRequiredDefault
tenantIdGuidYes
countintNo5

Response 200:

{
"tenantId": "...",
"invoices": [
{
"invoiceNumber": "INV-2026-0042",
"clientName": "Briggs Property Management",
"amount": 850.00,
"currency": "USD",
"status": "Paid",
"sentAt": "2026-07-05T14:30:00Z"
}
],
"asOf": "2026-07-07T20:30:00Z"
}

3.8 RoundTrip Team

GET /v1/roundtrip/team
Service-to-service.

Query parameters:

ParameterTypeRequired
tenantIdGuidYes

Response 200:

{
"tenantId": "...",
"members": [
{
"userId": "...",
"firstName": "Derek",
"lastName": "Osei",
"role": "Technician",
"isActive": true
}
],
"asOf": "2026-07-07T20:30:00Z"
}

3.9 RoundTrip AR Aging

GET /v1/roundtrip/ar/aging
Service-to-service.

Response 200:

{
"tenantId": "...",
"totalOutstanding": 4200.00,
"currency": "USD",
"buckets": {
"current": { "amount": 1800.00, "count": 3 },
"thirtyDays": { "amount": 1200.00, "count": 2 },
"sixtyDays": { "amount": 800.00, "count": 1 },
"ninetyPlus": { "amount": 400.00, "count": 1 }
},
"oldestInvoiceDays": 87,
"asOf": "2026-07-07T20:30:00Z"
}

3.10 Suite Subscriptions

GET /v1/suite/subscriptions
Service-to-service. Called by product frontends to determine suite switcher display.

Query parameters:

ParameterTypeRequired
tenantIdGuidYes

Response 200:

{
"tenantId": "...",
"subscriptions": {
"roundtrip": {
"active": true,
"plan": "Standard",
"url": "https://app.roundtrips.app"
},
"waypoint": {
"active": true,
"plan": "Starter",
"url": "https://traxswaypoint.app"
},
"relay": {
"active": false,
"plan": null,
"url": "https://traxsrelay.app"
}
}
}

Implementation: Queries Stripe for active subscriptions by customer ID. Stripe customer is shared across products — identified by TenantId metadata on the Stripe customer record.


4. Error Handling

All endpoints return consistent error responses:

{
"success": false,
"errorMessage": "Human-readable error message",
"errorCode": "OPTIONAL_CODE"
}

HTTP status codes:

CodeMeaning
200Success
400Bad request (malformed JSON)
401Missing or invalid API key (service endpoints)
422Validation failure
429Rate limit exceeded
500Internal error
503Upstream service (RoundTrip, SendGrid) unavailable

5. Versioning

All endpoints are versioned via URL prefix (/v1/). Breaking changes require a new version (/v2/). The v1 contract is stable — no breaking changes to existing v1 endpoints.

Adding new endpoints to v1 is not a breaking change.


6. Document History

VersionDateAuthorChanges
1.0July 2026Pete CarrollInitial draft