API server
Central platform server handling authentication, todos, configuration, and shared features.
Architecture
Section titled “Architecture”This is the central API server that all client apps connect to for shared functionality:
- Authentication (via
@abeauvois/platform-auth) - Todo management
- Configuration management (single source of truth for API keys and tokens)
- Unified Job system — background jobs + recurring cron schedules (see Jobs architecture)
- Quote requests — public submission + dashboard management
- User settings (platform preferences + namespaced prefs)
API Endpoints
Section titled “API Endpoints”| Route | Method | Purpose | Auth Required |
|---|---|---|---|
/api/auth/** | POST/GET | Authentication (sign-up, sign-in, sign-out) | Varies |
/api/todos | GET/POST/PATCH/DELETE | Todo management | Yes |
/api/config | GET | Get all configuration values | Yes |
/api/config/:key | GET | Get specific config value | Yes |
/api/config/batch | POST | Get multiple config values | Yes |
/api/config/keys | GET | List available config keys | Yes |
/api/jobs | GET/POST | List / create jobs | Yes |
/api/jobs/:id | GET/DELETE | Get / cancel a job | Yes |
/api/jobs/presets | GET | List presets + input schema (for the UI) | Yes |
/api/jobs/schedules | GET/POST | List / create recurring (cron) schedules | Yes |
/api/jobs/schedules/:name | GET/DELETE | Get / delete a schedule | Yes |
/api/jobs/schedules/:name/run | POST | Fire a schedule now (skips overlap guard) | Yes |
/api/jobs/queue-stats | GET | Live queue depth + 24h rollup (admin-only) | Yes (ADMIN_EMAILS) |
/api/quotes/requests | POST / GET | Submit (public) / list quote requests | POST: No / GET: Yes |
/api/quotes/requests/:id | GET/PATCH | Get / update a quote request | Yes |
/api/settings | GET/PATCH | User settings (platform + namespaced prefs) | Yes |
Development
Section titled “Development”# From monorepo rootbun run api
# Or from this directorybun run devServer runs on port 3000.
Database
Section titled “Database”# Start PostgreSQL via Dockerbun run db:up
# Generate migrationsbun run db:generate
# Run migrationsbun run db:migrate
# Open Drizzle Studiobun run db:studioConfiguration
Section titled “Configuration”Server Config
Section titled “Server Config”Environment variables are validated at startup via api/core:
import { createServerConfig, validators } from "api/core";
const schema = { DATABASE_URL: { description: "...", validate: validators.notEmpty }, PORT: { description: "...", default: "3000", validate: validators.port },};
export const config = createServerConfig(schema, { appName: "API Server" });Access anywhere in server code:
import { config } from "./config";const port = config.PORT;Config Endpoint
Section titled “Config Endpoint”Clients fetch non-secret configuration via /api/config:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/config | All allowed config |
| GET | /api/config/:key | Specific key |
| GET | /api/config/keys | List available keys |
| POST | /api/config/batch | Multiple keys |
Allowlisted keys: ANTHROPIC_API_KEY, NOTION_*, GMAIL_*, TWITTER_*
Never exposed: DATABASE_URL, BETTER_AUTH_SECRET
Environment Variables
Section titled “Environment Variables”# Database (required, server-only)DATABASE_URL=postgresql://user:password@localhost:5432/platform_db
# Auth (required, server-only)BETTER_AUTH_SECRET=your_secret_keyBETTER_AUTH_URL=http://localhost:3000
# CORSCLIENT_URLS=http://localhost:5000,http://localhost:5001
# Configuration values served to authenticated clients via /api/configANTHROPIC_API_KEY=your_anthropic_keyNOTION_INTEGRATION_TOKEN=your_notion_tokenNOTION_DATABASE_ID=your_database_idTWITTER_BEARER_TOKEN=your_twitter_tokenGMAIL_CLIENT_ID=your_gmail_client_idGMAIL_CLIENT_SECRET=your_gmail_client_secretGMAIL_REFRESH_TOKEN=your_gmail_refresh_tokenMY_EMAIL_ADDRESS=your_email@example.com
# Browser ScrapingCHROME_CDP_ENDPOINT=http://localhost:9222See Environment variables for the full inventory.
Scripts
Section titled “Scripts”| Script | Command | Purpose |
|---|---|---|
api:renew-session | bun run api:renew-session | Sign in and write a session cookie to ~/.platform-cli/cookies.txt. |
api:uat | bun run api:uat [--source ...] [--days ...] | End-to-end UAT against /api/jobs (posts a read job, polls). |
UAT example:
# Defaults to gmail / 30 days. Renews session automatically if cookie is stale.bun run api:uatbun run api:uat --source gmail --days 7bun run api:uat --api-url http://localhost:3000 --timeout 60bun run api:uat --no-poll # submit and exitExit codes: 0 on completed, 1 on failed/timeout, 2 on env errors. See the Jobs architecture reference for the full end-to-end UAT details.
Client Apps
Section titled “Client Apps”The following clients connect to this server:
- Dashboard (
/apps/dashboard) - port 5000 - CLI (
/apps/cli) - command-line interface
Calling the API with index.http
Section titled “Calling the API with index.http”The quickest way to hit these endpoints: just open index.http in VS Code and click the little “Send Request” links above each request (install the REST Client extension if you don’t see them). If a request comes back 401 Unauthorized, just click “Send Request” on the Sign in block first to mint a fresh token.
Two variables sit at the top of the file:
@baseUrl = http://localhost:3000@token = <bearer token>baseUrl— leave aslocalhost:3000for local dev (start the API withbun run dev).token— most routes are authenticated. Run the Sign in block and copy the token from the response into@token(or grab it from your browser dev tools while logged into the dashboard).
Examples
Section titled “Examples”### Sign in (run this first / whenever you get a 401)POST {{baseUrl}}/api/auth/sign-in/emailContent-Type: application/json
{ "email": "test@example.com", "password": "password123" }
### List / search quote requestsGET {{baseUrl}}/api/quotes/requests?status=pending&limit=20&offset=0Authorization: Bearer {{token}}Query params on the list endpoint are optional — status (pending/reviewed), limit, offset. Drop the query string entirely to list everything.
The file is grouped by subsystem (Quote requests, Jobs, Schedules); each ### block is an independently runnable request.
