Skip to content

API Overview

COORDINATOR Web connects to multiple backend services via REST APIs and WebSockets.

Service Architecture

┌─────────────────────────────────────────────────────────────┐
│                    COORDINATOR Web                          │
│                    (Next.js Frontend)                       │
└──────────┬──────────────────┬──────────────────┬───────────┘
           │                  │                  │
           ▼                  ▼                  ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│   Auth API       │ │   Core API       │ │   WebSocket      │
│   (BetterAuth)   │ │   (Actions/Admin)│ │   (Streaming)    │
│                  │ │                  │ │                  │
│ NEXT_PUBLIC_     │ │ NEXT_PUBLIC_     │ │ NEXT_PUBLIC_     │
│ AUTH_API_URL     │ │ API_BASE_URL     │ │ WS_PORT          │
└──────────────────┘ └──────────────────┘ └──────────────────┘

Environment Variables

Variable Purpose Default
NEXT_PUBLIC_AUTH_API_URL BetterAuth service URL http://localhost:8787
NEXT_PUBLIC_API_BASE_URL Core REST API URL http://localhost:8080
NEXT_PUBLIC_WS_PORT WebSocket streaming port 7103
LOKI_URL Loki logging endpoint -
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY Stripe public key -

API Base Paths

Auth API

All BetterAuth endpoints under NEXT_PUBLIC_AUTH_API_URL:

/api/auth/get-session
/api/auth/sign-in
/api/auth/sign-up
/api/auth/sign-out
/api/auth/token
...

Core API - Actions

User-facing endpoints under NEXT_PUBLIC_API_BASE_URL/api/v1/actions:

/api/v1/actions/sessions
/api/v1/actions/chats
/api/v1/actions/embedding
/api/v1/actions/train
/api/v1/actions/vision
/api/v1/actions/forms

Core API - Admin

Admin-only endpoints under NEXT_PUBLIC_API_BASE_URL/api/v1/admin:

/api/v1/admin/devices

WebSocket

Camera streaming:

ws://{serverHostURL}:{WS_PORT}/ws/stream/{cameraId}

Authentication

JWT Token Flow

  1. User authenticates via BetterAuth
  2. Frontend calls authClient.token() to get JWT
  3. JWT cached by TanStack Query (10 min stale time)
  4. JWT sent in Authorization: Bearer {token} header

Request Pattern

// lib/utils.ts
export async function fetchJSON<T>(
  path: string,
  token: string,
  request?: RequestInit,
  timeoutMs = 30000
): Promise<T> {
  const url = `${API_BASE_URL}${path}`;

  const response = await fetch(url, {
    ...request,
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      ...request?.headers
    },
    body: request?.body ? JSON.stringify(request.body) : undefined
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  return response.json();
}

Response Patterns

Success Response

{
  "data": { ... },
  "message": "Success"
}

List Response

{
  "results": [...],
  "total": 100,
  "limit": 20,
  "offset": 0
}

Error Response

{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": { ... }
}

Timeout Handling

All requests have a configurable timeout (default 30s):

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);

const response = await fetch(url, {
  signal: controller.signal,
  ...options
});

clearTimeout(timeout);

Content Types

JSON Requests

headers: {
  "Content-Type": "application/json"
}
body: JSON.stringify(data)

File Upload (FormData)

// No Content-Type header (browser sets multipart/form-data)
const formData = new FormData();
formData.append("file", file);

fetch(url, {
  method: "POST",
  headers: { Authorization: `Bearer ${token}` },
  body: formData
});

API Domains

Domain Base Path Purpose
Sessions /sessions Session CRUD, logs, clips
Forms /forms Form template management
Chat /chats Conversations, messages
Embeddings /embedding Upload, search, manage
Training /train Training jobs
Vision /vision Cameras, detection
Devices /devices (admin) Device management

Rate Limiting

Backend may enforce rate limits. Handle with retry logic:

const fetchWithRetry = async (url, options, retries = 3) => {
  for (let i = 0; i < retries; i++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const delay = Math.pow(2, i) * 1000;
      await new Promise(r => setTimeout(r, delay));
      continue;
    }

    return response;
  }
  throw new Error("Max retries exceeded");
};

Pagination

List endpoints support pagination:

GET /sessions?limit=20&offset=40

Response:

{
  "results": [...],
  "total": 100,
  "limit": 20,
  "offset": 40
}

Sorting

Some endpoints support sorting:

GET /sessions?sortBy=createdAt&sortDirection=desc

Filtering

Domain-specific filters:

GET /embedding/search?query=football
GET /embedding/search-by-file?filename=game.mp4
GET /sessions?status=InProgress