Skip to content

API server

Central platform server handling authentication, todos, configuration, and shared features.

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)
RouteMethodPurposeAuth Required
/api/auth/**POST/GETAuthentication (sign-up, sign-in, sign-out)Varies
/api/todosGET/POST/PATCH/DELETETodo managementYes
/api/configGETGet all configuration valuesYes
/api/config/:keyGETGet specific config valueYes
/api/config/batchPOSTGet multiple config valuesYes
/api/config/keysGETList available config keysYes
/api/jobsGET/POSTList / create jobsYes
/api/jobs/:idGET/DELETEGet / cancel a jobYes
/api/jobs/presetsGETList presets + input schema (for the UI)Yes
/api/jobs/schedulesGET/POSTList / create recurring (cron) schedulesYes
/api/jobs/schedules/:nameGET/DELETEGet / delete a scheduleYes
/api/jobs/schedules/:name/runPOSTFire a schedule now (skips overlap guard)Yes
/api/jobs/queue-statsGETLive queue depth + 24h rollup (admin-only)Yes (ADMIN_EMAILS)
/api/quotes/requestsPOST / GETSubmit (public) / list quote requestsPOST: No / GET: Yes
/api/quotes/requests/:idGET/PATCHGet / update a quote requestYes
/api/settingsGET/PATCHUser settings (platform + namespaced prefs)Yes
Terminal window
# From monorepo root
bun run api
# Or from this directory
bun run dev

Server runs on port 3000.

Terminal window
# Start PostgreSQL via Docker
bun run db:up
# Generate migrations
bun run db:generate
# Run migrations
bun run db:migrate
# Open Drizzle Studio
bun run db:studio

Environment variables are validated at startup via api/core:

server/config.ts
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;

Clients fetch non-secret configuration via /api/config:

MethodEndpointDescription
GET/api/configAll allowed config
GET/api/config/:keySpecific key
GET/api/config/keysList available keys
POST/api/config/batchMultiple keys

Allowlisted keys: ANTHROPIC_API_KEY, NOTION_*, GMAIL_*, TWITTER_*

Never exposed: DATABASE_URL, BETTER_AUTH_SECRET

# Database (required, server-only)
DATABASE_URL=postgresql://user:password@localhost:5432/platform_db
# Auth (required, server-only)
BETTER_AUTH_SECRET=your_secret_key
BETTER_AUTH_URL=http://localhost:3000
# CORS
CLIENT_URLS=http://localhost:5000,http://localhost:5001
# Configuration values served to authenticated clients via /api/config
ANTHROPIC_API_KEY=your_anthropic_key
NOTION_INTEGRATION_TOKEN=your_notion_token
NOTION_DATABASE_ID=your_database_id
TWITTER_BEARER_TOKEN=your_twitter_token
GMAIL_CLIENT_ID=your_gmail_client_id
GMAIL_CLIENT_SECRET=your_gmail_client_secret
GMAIL_REFRESH_TOKEN=your_gmail_refresh_token
MY_EMAIL_ADDRESS=your_email@example.com
# Browser Scraping
CHROME_CDP_ENDPOINT=http://localhost:9222

See Environment variables for the full inventory.

ScriptCommandPurpose
api:renew-sessionbun run api:renew-sessionSign in and write a session cookie to ~/.platform-cli/cookies.txt.
api:uatbun run api:uat [--source ...] [--days ...]End-to-end UAT against /api/jobs (posts a read job, polls).

UAT example:

Terminal window
# Defaults to gmail / 30 days. Renews session automatically if cookie is stale.
bun run api:uat
bun run api:uat --source gmail --days 7
bun run api:uat --api-url http://localhost:3000 --timeout 60
bun run api:uat --no-poll # submit and exit

Exit 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.

The following clients connect to this server:

  • Dashboard (/apps/dashboard) - port 5000
  • CLI (/apps/cli) - command-line interface

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 as localhost:3000 for local dev (start the API with bun 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).
### Sign in (run this first / whenever you get a 401)
POST {{baseUrl}}/api/auth/sign-in/email
Content-Type: application/json
{ "email": "test@example.com", "password": "password123" }
### List / search quote requests
GET {{baseUrl}}/api/quotes/requests?status=pending&limit=20&offset=0
Authorization: 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.