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:
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:
WebSocket¶
Camera streaming:
Authentication¶
JWT Token Flow¶
- User authenticates via BetterAuth
- Frontend calls
authClient.token()to get JWT - JWT cached by TanStack Query (10 min stale time)
- 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¶
List Response¶
Error Response¶
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¶
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:
Response:
Sorting¶
Some endpoints support sorting:
Filtering¶
Domain-specific filters: