> ## Documentation Index
> Fetch the complete documentation index at: https://docs.soundchecklive.io/llms.txt
> Use this file to discover all available pages before exploring further.

# System Architecture

> High-level architecture of the Soundcheck platform

Soundcheck is a monorepo spanning two client apps, an Encore.ts backend (api-events, including `/mcp`), an MCP SDK, an AI agent, and two cloud infrastructure planes. This page covers the whole-platform topology, the generated-client contract, the integration map, and the data-flow patterns. For api-events internals see [api-events](https://docs.soundchecklive.io/engineering/backend/api-events.md).

***

## Platform topology

Clients authenticate with Clerk and call **api-events** (Encore.ts), the system of record on PostgreSQL. The service writes domain events transactionally, serves the public HTTP API and `/mcp`, and fans out notification delivery over GCP Pub/Sub. `@soundcheck/mcp` (`mcp-ts/`) is the catalog library; CheckAI drives the platform through those tools.

```
        ┌───────────────┐   ┌───────────────┐        ┌───────────────┐
        │  Mobile app   │   │   Web app     │        │   CheckAI     │
        │ Expo / RN 0.83│   │  Next.js 16.3 │        │  (eve agent)  │
        └──────┬────────┘   └──────┬────────┘        └──────┬────────┘
               │      Clerk JWT     │                        │
               └─────────┬──────────┘                        │
                         │                                   │
    inbound webhooks     │                                   │
    (clerk, stripe,      ▼                                   │
     twilio,┌───────────────────┐                  │
     n8n)         ─►│    api-events     │◄─────────────────┘
                    │  Encore + Prisma  │  /mcp + REST
                    │  (Cloud Run)      │
                    └───┬───────────┬───┘
                        │           │  direct Pub/Sub publish
                        ▼           ▼
                ┌──────────────┐  ┌──────────────┐   ┌──────────────────┐
                │  PostgreSQL  │  │ GCP Pub/Sub  │──►│    api-events    │
                │  Flexible    │  │  topics      │   │  Encore.ts /     │
                │  Server      │  └──────────────┘   │  Cloud Run       │
                └──────────────┘                     └────────┬─────────┘
                        ▲                                      │
                        └─────── writes deliveries ────────────┤
                                                               ▼
                                              Twilio (SMS) · SendGrid (email) · Expo (push)
```

### Two infrastructure planes

| Plane | Cloud | Terraform | Key resources |
| - | - | - | - |
| App plane | Azure | `infra/` | Web on App Service; PostgreSQL, Key Vault, ACR, VNet. Production still provisions the API App Service (`infra/modules/backend`); it is disabled in `dev` and `qa`. The Express MCP App Service module remains in Terraform pending an explicit teardown. Hosted MCP now lives on api-events. |
| Event plane | GCP | `infra-gcp/` | `api-events` on `google_cloud_run_v2_service` behind Pub/Sub topics/subscriptions (`infra-gcp/modules/events`); DNS (`infra-gcp/modules/dns`); CI/CD identity (`infra-gcp/modules/cicd`). |

Both planes share one PostgreSQL database: **api-events** owns schema and migrations (`api-events/src/db/schema.prisma`, `api-events/src/db/migrations/`).

### Environments

| Environment | Purpose | Branch |
| - | - | - |
| Dev | Development testing | `dev` |
| QA | Quality assurance | `qa` |
| Prod | Production | `main` |

***

## The generated-client contract

The web app never hand-writes API types. The chain is:

1. **api-events route contracts** — Encore `api()` / `api.raw()` handlers and typed operation graphs in `api-events/src/`.
2. `npm run openapi:emit` (in `api-events/`) renders OpenAPI 3.x from those contracts via `scripts/openapi/emitOpenApi.ts`. Path templates use Encore param names (`{userId}`, `{organizationId}`, `{eventId}`). `swagger-typescript-api` still derives method names such as `v1UsersDetail`; the path-param objects follow the Encore names (`{ userId }` not `{ id }`). Query, header, body, and response shapes come from sibling `openapiRoute()` annotations. Encore's typed `api()` cannot describe custom envelope shapes, which is why most handlers are `api.raw`. Named `$ref` schemas come from sibling `openapiSchema()` registrations.
3. On PRs, `api-events-deploy.yml` `openapi-parity` emits and asserts path templates against Encore extract. On `api-events/` pushes to `dev`/`qa`/`main`, `docs-ci` `sync-openapi-spec` re-emits and asserts path templates and `$ref`'d schema-catalog keys before auto-committing `docs/openapi/soundcheck.json`.
4. `web/scripts/generate-api-client.mjs` reads `docs/openapi/soundcheck.json` and emits a typed TypeScript client via `swagger-typescript-api`. It runs automatically on the web app's `predev` and `prebuild` scripts (skipping codegen when the output is newer than the spec).

Because the spec is emitted from the same TypeScript contracts that power the Encore HTTP API, backend and frontend types stay in lockstep. The `e2e/` suite additionally enforces endpoint coverage — new API routes are expected to be exercised — so an unregistered or untested endpoint surfaces in CI rather than silently drifting.

`npm run openapi:parity` in `api-events/` compares the committed spec against live TS endpoint declarations extracted via AST (a self-check that emit and handlers have not drifted). Encore's native OpenAPI export is not used — it cannot describe `api.raw` handlers.

***

## Integration map

The API is the single boundary for third-party integration. External services either **push in** via signed webhooks or the API **calls out** through a dedicated service. Per project convention, third-party calls that would otherwise be made inline (HubSpot, Flex) are brokered through **n8n**, which posts results back to the API as webhooks.

### Inbound webhooks

All webhook handlers live in `api-events/src/webhooks/` and verify a provider signature or shared secret before doing any work.

| Route | Provider | Handler |
| - | - | - |
| `/webhooks/clerk` | Clerk (user/org lifecycle) | `clerkWebhook.ts` |
| `/webhooks/stripe` | Stripe (payments, disputes, refunds) | `stripeWebhookEndpoint.ts` |
| `/webhooks/cleo` | Cleo Pay (verify, enqueue, apply bank status) | `cleoWebhook.ts` |
| `/webhooks/twilio` | Twilio (inbound SMS replies) | `twilioWebhook.ts` |
| `/webhooks/n8n/crm-sync` | n8n → CRM (Flex/HubSpot imports) | `n8nWebhook.ts` |
| `/webhooks/n8n/flex-quote-sync` | n8n → Flex quote/status sync | `n8nWebhookHandler.ts` |

### Outbound integrations

| Integration | Purpose | Owning service |
| - | - | - |
| Stripe / Connect | Charges, payouts, Connect accounts | `api-events/src/systems/stripe/`, `src/clients/stripeClient.ts` |
| Clerk API | Just-in-time user/org provisioning | `api-events/src/clients/clerkClient.ts` |
| QuickBooks | Accounting sync | `api-events/src/clients/quickbooksClient.ts` |
| Flex (via n8n) | Rental/production quote pipeline | `api-events/src/systems/flex/` |
| HubSpot (via n8n) | CRM deal ↔ Flex quote pipeline | brokered by n8n; API side lands on the n8n webhooks above |
| Cleo ACH | Bank-account capture & ACH payout validation | `api-events/src/systems/bank-accounts/` |
| SMS / email / push | Notification delivery | `api-events` Pub/Sub processors (Twilio, SendGrid, Expo) |

***

## Data-flow patterns

### Direct Pub/Sub today; durable outbox as the target

The API currently mixes direct provider calls with direct Pub/Sub publication; it has no shared transactional outbox. Availability notification processors demonstrate independent channel/device fan-out with terminal inbox and notification audit records, while other consumers have different failure policies. An inbox alone does not make external effects exactly once. See [api-events](https://docs.soundchecklive.io/engineering/backend/api-events.md) for the component map.

The [durable workflow contract](./backend/async-durability-patterns) selects among synchronous transactions, outbox delivery, database queues, reconciliation and resumable workflows. The [feature assessment](./backend/async-workflow-assessment) identifies current gaps and a sequenced end-state for durability and scalability.

### UEF import & ingestion

The **Universal Event Format (UEF)** is the interchange shape for bringing external event data into the platform — file uploads and third-party imports are normalized to UEF, then reconciled against existing records. In-repo the import pipeline lives under `api-events/src/systems/imports/`; ingestion batches under `api-events/src/systems/event-ingestions/`. See [api-events](https://docs.soundchecklive.io/engineering/backend/api-events.md) for the ingestion subsystem.

***

## CI/CD

| Component | Build | Test | Deploy |
| - | - | - | - |
| Mobile | EAS Build | Code Quality (`type-check` / `lint` / `test:unit` / format / Knip) + Revyl + TestFlight | App Stores |
| Web | Next.js (container) | Playwright (`web/`) | Azure App Service |
| api-events | Encore.ts | Vitest + integration DB + Encore E2E (`e2e/`) | GCP Cloud Run |
| mcp-ts | TypeScript | Vitest | Azure App Service |
| CheckAI | Node / eve | evals | (see `checkai/`) |
| Infra | Terraform plan | validate / tfsec | Terraform apply |

Workflows live in `.github/workflows/` (e.g. `api-events-deploy.yml`, `web-ci-cd.yml`, `docs-ci.yml`, `gcp-terraform-validate.yml`).

***

## Security model

* **Authentication** — Clerk-issued JWTs from every client. Basic Auth is removed. See the API's auth middleware.
* **Authorization** — organization-scoped, role-based checks enforced in middleware and handlers; resource-level ownership checks in services.
* **Secrets** — held in Azure Key Vault and surfaced to App Services as Key Vault references (not raw env vars).
* **Webhooks** — every inbound webhook verifies a provider signature or HMAC shared secret before acting.
* **Fail-closed config** — the API defaults `ENVIRONMENT` to `production`, so production validation applies unless development is explicitly opted into.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.