api-events/, port 4000) that is the system of record for the platform. It serves the full public HTTP API, owns PostgreSQL DDL, publishes domain events directly to Pub/Sub, and delegates notification delivery to processors. For platform-wide topology, see System Architecture.
Layer overview
api(...) declarations stay thin: validate auth, call an operation, return a DTO. Operations hold business logic and Prisma access. Pub/Sub follows the same split — handlers/ wire topics to processors/.
Directory structure
api-events/src/db/schema.prisma + api-events/src/db/migrations/. Migrations apply on Encore boot and via npm run db:migrate.
Authentication & authorization
Clerk session JWT is the only caller auth path.ENABLE_BASIC_AUTH is removed and fails closed if set. /api/v1/admin/** is parked and 404s.
Provisioning. Webhook and middleware paths ensure User, Organization, and membership rows exist before handlers run, closing the race where a request arrives before Clerk webhooks land.
Organization authorization. Access is organization-scoped and role-based. Handlers resolve org scope from the authenticated identity — never from untrusted URL or query params.
Fail-closed config. ENVIRONMENT must be set explicitly; production secret and CORS validation apply outside development.
Subsystems
- Pub/Sub notifications. Operations currently publish directly; the business commit and publication are separate. A shared transactional outbox is the target architecture. Pub/Sub processors render and deliver SMS, email, and push. See api-events and the feature assessment.
- Audit logging. Sensitive mutations write to per-domain audit tables via dedicated operations.
- Event-file ingestion (UEF). Uploaded files and imports are extracted, analyzed, reviewed, and merged into events. Logic lives under
systems/event-ingestions/. - HubSpot ↔ Flex pipeline (via n8n). Deal-stage and quote sync runs through n8n webhooks back into api-events.
- Admin org-config console. Staff surfaces for seat/onboarding and entitlement grants under org-scoped admin routes.
- Portal auth. Client-portal contacts authenticate through Clerk but are scope-resolved server-side so staff and portal sessions cannot cross-read.
Route registration
Every public path is declared as an Encoreapi(...) endpoint under src/systems/*/endpoints/ or src/webhooks/. api-events/routes.json holds each route’s domain override; unknown paths return Encore’s native 404.
OpenAPI is emitted from Encore (npm run openapi:emit) and synced to docs/openapi/soundcheck.json for client codegen. The e2e/ suite enforces endpoint coverage when E2E_ENFORCE_COVERAGE=1.
Error handling
Errors return structured JSON with an HTTP status:Best practices
- Endpoints — keep thin; delegate to operations; return appropriate status codes.
- Operations — own business logic and transactions; record required asynchronous intent durably according to the workflow contract. A shared outbox remains future implementation work.
- Migrations — edit
api-events/src/db/schema.prismaonly; apply withnpm run db:migrateor on boot. - Testing — unit-test operations with mocked Prisma (
vi.mock); integration tests against Postgres on:5434; e2e against:4000.
Related docs
api-events/README.md— canonical architecture and idempotency design.- api-events — notification fan-out and Pub/Sub processors.
- API overview — Encore.ts backend and local setup (Go removal complete).