Base URL
All API endpoints are served from the NestJS backend, proxied through the Next.js frontend at /backend/*. When calling from outside the frontend (e.g. from your own application), call the backend directly:
# Production (internal network only)
https://your-backend.internal/api/
# Via the frontend proxy (public, authenticated)
https://your-domain.com/backend/Authentication
Every request must include a valid credential. Two methods are supported:
| Method | Header | Use case |
|---|---|---|
| Session cookie | Cookie: better-auth.session_token=... | Browser clients (set automatically after sign-in) |
| API key | X-API-Key: ak_... | Server-to-server, CLI tools, CI/CD |
| API key (Bearer) | Authorization: Bearer ak_... | Alternative to X-API-Key header |
Note: Create API keys in Settings → Auth Keys. Keys are prefixed with ak_ and shown only once at creation.
Core endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/chats | List user's conversations |
| POST | /api/chat | Send a chat message (SSE stream) |
| GET | /api/chats/:id/messages | Get messages for a chat |
| GET | /api/agents | List available agents |
| POST | /api/agents/:id/chat | Chat with a specific agent (SSE) |
| GET | /api/knowledge-bases | List knowledge bases |
| POST | /api/knowledge-bases/:id/search | Semantic search a knowledge base |
| GET | /api/models | List available AI models |
| GET | /api/marketplace/listings | Browse marketplace |
| GET | /api/health | Health check (no auth required) |
Streaming chat (SSE)
Chat responses stream as Server-Sent Events. Connect with EventSource or fetch with a ReadableStream:
const response = await fetch("/backend/chat/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include", // sends session cookie
body: JSON.stringify({
chatId: "optional-existing-chat-id",
model: "gpt-4.1-mini",
messages: [{ role: "user", content: "Hello!" }],
enableSearch: false,
knowledgeBaseIds: [],
}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
for (const line of text.split("\n")) {
if (!line.startsWith("data: ")) continue;
const data = line.slice(6);
if (data === "[DONE]") break;
const { textDelta } = JSON.parse(data);
process.stdout.write(textDelta);
}
}Rate limits
| Tier | Requests / minute | Chat messages / day |
|---|---|---|
| Free | 20 | 50 |
| Pro | 100 | 500 |
| Enterprise | Unlimited | Unlimited |
| API key | 200 | Same as user tier |
Note: Rate limit headers are included in every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Error codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body failed validation |
| 401 | UNAUTHORIZED | Missing or invalid credentials |
| 403 | FORBIDDEN | Valid credentials but insufficient permissions |
| 404 | NOT_FOUND | Resource does not exist |
| 429 | RATE_LIMITED | Too many requests — back off and retry |
| 500 | INTERNAL_ERROR | Server error — contact your administrator |
| 503 | BACKEND_UNAVAILABLE | NestJS backend is not reachable |