Repository navigation
API Reference
Canonical source:
docs/12-reference/api-reference.md— this page is rendered from it on every publish; edit the source.
All endpoints are served through the Nginx reverse proxy on port 4000 with the prefix /api/v1/. Unless marked as @Public, all endpoints require a valid JWT access token in the Authorization: Bearer <token> header.
Base URL: http://localhost:4000/api/v1
Common Headers:
| Header | Required | Description |
|---|---|---|
| Authorization | Yes (except @Public) | Bearer <JWT access token> |
| Content-Type | Yes (POST/PATCH) | application/json |
| X-Request-ID | No | Correlation ID for request tracing |
Pagination: List endpoints return a standard paginated response:
{
"data": [...],
"meta": {
"page": 1,
"limit": 20,
"total": 150,
"totalPages": 8
}
}Auth Required: No (@Public)
Login with email and password. Returns JWT token pair and user profile.
Request Body:
{
"email": "admin@example.com",
"password": "securepassword"
}| Field | Type | Required | Constraints |
|---|---|---|---|
| string | Yes | Valid email format | |
| password | string | Yes | Min 1 character |
Response (200):
{
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
},
"user": {
"id": "uuid",
"email": "admin@example.com",
"username": "admin",
"role": "ADMIN",
"mustChangePassword": false,
"languagePreference": "en",
"appearancePreference": "system"
}
}Error Responses:
| Status | Code | Description |
|---|---|---|
| 401 | INVALID_CREDENTIALS | Email or password is incorrect |
| 403 | ACCOUNT_SUSPENDED | User account is suspended |
Auth Required: No (@Public)
Exchange a valid refresh token for a new token pair (rotation).
Request Body:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response (200):
{
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
}Error Responses:
| Status | Code | Description |
|---|---|---|
| 401 | INVALID_REFRESH_TOKEN | Token is expired, revoked, or invalid |
Auth Required: Yes
Invalidate all sessions for the authenticated user.
Response: 204 No Content
Auth Required: Yes
Retrieve the authenticated user's profile.
Response (200):
{
"id": "uuid",
"email": "admin@example.com",
"username": "admin",
"role": "ADMIN",
"mustChangePassword": false,
"languagePreference": "en",
"appearancePreference": "system"
}Auth Required: Yes | Role: ADMIN
Create a new user.
Request Body:
{
"email": "user@example.com",
"username": "newuser",
"password": "securepassword",
"role": "OPERATOR"
}Response (201): SafeUser object (excludes passwordHash).
Error Responses:
| Status | Code | Description |
|---|---|---|
| 409 | DUPLICATE_ENTITY | A user with this email already exists |
Auth Required: Yes | Role: ADMIN
List all users with pagination.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| search | string | - | Search by email or username |
Response (200): Paginated list of SafeUser objects.
Auth Required: Yes | Role: ADMIN
Get a single user by ID.
Response (200): SafeUser object.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | User with the given ID does not exist |
Auth Required: Yes | Role: ADMIN
Update a user's details.
Request Body:
{
"username": "updatedname",
"email": "updated@example.com"
}Response (200): Updated SafeUser object.
Auth Required: Yes | Role: ADMIN
Deactivate a user (soft delete). Cannot deactivate self.
Response (200): Deactivated SafeUser object.
Auth Required: Yes | Role: ADMIN
Reactivate a previously deactivated user.
Response (200): Reactivated SafeUser object.
Auth Required: Yes | Role: ADMIN
Change a user's role. Cannot change own role.
Request Body:
{
"role": "ADMIN"
}| Field | Type | Required | Values |
|---|---|---|---|
| role | string (enum) | Yes | ADMIN, OPERATOR, VIEWER |
Response (200): Updated SafeUser object.
Auth Required: Yes
Update the authenticated user's preferences.
Request Body:
{
"languagePreference": "ar",
"appearancePreference": "dark"
}Response (200): Updated SafeUser object.
Auth Required: Yes
Change the authenticated user's password.
Request Body:
{
"currentPassword": "oldpassword",
"newPassword": "newsecurepassword"
}Response: 204 No Content
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_CURRENT_PASSWORD | Current password is incorrect |
| 400 | SAME_PASSWORD_ERROR | New password cannot be the same as current password |
Auth Required: Yes
Create a new chat thread.
Request Body:
{
"title": "Code Review Session",
"routingMode": "AUTO",
"systemPrompt": "You are a senior code reviewer.",
"temperature": 0.3,
"maxTokens": 4000,
"preferredProvider": "anthropic",
"preferredModel": "claude-sonnet-4",
"contextPackIds": ["uuid-1", "uuid-2"]
}| Field | Type | Required | Constraints |
|---|---|---|---|
| title | string | No | Max 255 chars |
| routingMode | enum | No | AUTO, MANUAL_MODEL, LOCAL_ONLY, PRIVACY_FIRST, LOW_LATENCY, HIGH_REASONING, COST_SAVER |
| systemPrompt | string | No | Max 10,000 chars |
| temperature | number | No | 0.0 to 2.0 |
| maxTokens | integer | No | 1 to 32,000 |
| preferredProvider | string | No | Max 50 chars |
| preferredModel | string | No | Max 255 chars |
| contextPackIds | string[] | No | Max 10 items |
Response (201): ChatThread object.
Auth Required: Yes
List the authenticated user's threads with message counts.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| search | string | - | Search by title |
Response (200): Paginated list of ThreadWithMessageCount objects.
Auth Required: Yes
Get a single thread. Must be owned by the authenticated user.
Response (200): ChatThread object.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | Thread does not exist |
| 403 | FORBIDDEN_THREAD_ACCESS | Thread belongs to another user |
Auth Required: Yes
Update a thread's settings. Must be owned by the authenticated user.
Request Body: Same fields as create (all optional).
Response (200): Updated ChatThread object.
Auth Required: Yes
Delete a thread and all associated messages. Must be owned by the authenticated user.
Response (200): Deleted ChatThread object.
Auth Required: Yes
Send a message and trigger AI response generation.
Request Body:
{
"threadId": "uuid",
"content": "Review this Python function for bugs:\n\ndef calculate(x, y):\n return x / y",
"routingMode": "AUTO",
"provider": "anthropic",
"model": "claude-sonnet-4",
"fileIds": ["uuid-1", "uuid-2"]
}| Field | Type | Required | Constraints |
|---|---|---|---|
| threadId | string | Yes | Max 255 chars, must exist and be owned by user |
| content | string | Yes | 1 to 100,000 chars |
| routingMode | enum | No | Overrides thread routing mode for this message |
| provider | string | No | Max 50 chars, forces specific provider |
| model | string | No | Max 255 chars, forces specific model |
| fileIds | string[] | No | Max 10 items, files must be owned by user with ingestion complete |
Response (201): ChatMessage object (the USER message). The ASSISTANT message is generated asynchronously and delivered via SSE.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | Thread or referenced file not found |
| 403 | FORBIDDEN_THREAD_ACCESS | Thread belongs to another user |
Auth Required: Yes
List messages in a thread (cursor-paginated, newest first). Must own the thread.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| before | string | — | A message id already seen; fetches the page older than it. Omitted returns the newest messages. |
| limit | number | 50 | Items per page (max 100) |
Response (200): { data: ChatMessage[], meta: { total, limit, nextBefore } }.
nextBefore is the cursor for the next older page, or null when nothing
older is left.
Auth Required: Yes
Get a single message. Must own the containing thread.
Response (200): ChatMessage object.
Auth Required: Yes
Regenerate the AI response for a message. Must own the containing thread.
Optional body { routingMode: AUTO | MANUAL_MODEL, provider, model } picks who
answers; the same plan/quota check as a send runs first (403/429). Full contract:
api-reference-chat.md (ADR-132).
Response (201): the message it was called on; the new answer arrives over the stream.
Auth Required: Yes
Set feedback on a message.
Request Body:
{
"feedback": "POSITIVE"
}Response (200): Updated ChatMessage object.
Auth Required: Yes
Send the same prompt to 2-5 models simultaneously (compare mode). Optionally runs the Critic → Judge quality pipeline per lane.
Request Body:
{
"threadId": "uuid",
"content": "Explain REST vs GraphQL",
"models": [
{ "provider": "anthropic", "model": "claude-sonnet-4" },
{ "provider": "openai", "model": "gpt-4o-mini" }
],
"judgeEnabled": true,
"judgeModel": "anthropic:claude-sonnet-4",
"criticEnabled": true,
"criticModel": "openai:gpt-4o-mini",
"fileIds": []
}| Field | Type | Required | Constraints |
|---|---|---|---|
| threadId | string | No | Max 255 chars (auto-creates a thread when omitted) |
| content | string | Yes | 1 to 100,000 chars |
| models | array | Yes | 2-5 {provider, model} pairs |
| judgeEnabled | boolean | No | Activates the Judge step on every lane |
| judgeModel | string | No |
PROVIDER:model or plain local model name (max 255) |
| criticEnabled | boolean | No (default false) | Activates the Critic step; requires judgeEnabled=true AND criticModel
|
| criticModel | string | Conditional | Required + non-empty whenever criticEnabled=true (max 200 chars) |
| researchMode | enum | No |
NONE, WEB_SEARCH, FETCH, EXTRACT — enricher pre-pends shared evidence |
| researchQuery | string | No | Max 500 chars |
| researchProviderId | string | No | Max 64 chars |
| fileIds | string[] | No | Max 10 items |
Validation rules (Zod superRefine):
-
criticEnabled=trueANDjudgeEnabled !== true→400 VALIDATION_ERRORon pathcriticEnabled -
criticEnabled=trueANDcriticModelempty/whitespace →400 VALIDATION_ERRORon pathcriticModel
Plan-feature gates (enforced by AccessControlService):
allowCompareMode, plus allowJudgeMode when judgeEnabled, plus
allowCriticReview when criticEnabled, plus allowResearchMode when
researchMode !== NONE. A locked plan returns 403 MODEL_NOT_ALLOWED_FOR_PLAN.
Response (200): ParallelResponse with per-model { provider, model, status, content, messageId, inputTokens, outputTokens, latencyMs, error? } plus
totalLatencyMs. When Judge/Critic ran, each fulfilled message's
metadata.judgeReview carries the full JudgeReviewPayload (critic feedback,
score, parse-failed flag, judge decision, revised/escalated answer).
See Judge & Critic Spec for the full architectural narrative.
Auth Required: Yes
Server-Sent Events endpoint for real-time message updates.
Response: Event stream. Each event contains a JSON-encoded payload:
data: {"type":"completion","threadId":"uuid","messageId":"uuid","content":"...","provider":"anthropic","model":"claude-sonnet-4"}
data: {"type":"error","threadId":"uuid","error":"All LLM providers failed"}
Notes:
- Excluded from rate limiting and request logging.
- Connection remains open until the client disconnects.
- Events are filtered by threadId.
Auth Required: Yes
Create a new AI provider connector.
Request Body:
{
"name": "OpenAI Production",
"provider": "OPENAI",
"authType": "API_KEY",
"apiKey": "sk-...",
"baseUrl": "https://api.openai.com/v1",
"region": "us-east-1"
}| Field | Type | Required | Constraints |
|---|---|---|---|
| name | string | Yes | 1-100 chars |
| provider | enum | Yes | OPENAI, ANTHROPIC, GEMINI, DEEPSEEK, OLLAMA, CUSTOM |
| authType | enum | Yes | API_KEY, OAUTH, NONE |
| apiKey | string | No | Max 500 chars (AES-256-GCM encrypted) |
| baseUrl | string | No | Max 500 chars |
| region | string | No | Max 50 chars |
Response (201): ConnectorWithModels object.
Auth Required: Yes
List all connectors with their models.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| provider | string | - | Filter by provider |
| status | string | - | Filter by status |
Response (200): Paginated list of ConnectorWithModels objects.
Auth Required: Yes
Get a single connector with its models.
Response (200): ConnectorWithModels object.
Auth Required: Yes
Update a connector.
Request Body: Same fields as create (all optional).
Response (200): Updated ConnectorWithModels object.
Auth Required: Yes
Delete a connector and all associated models.
Response (200): Deleted ConnectorWithModels object.
Auth Required: Yes
Test a connector's configuration by making a live API call.
Response (200):
{
"healthy": true,
"latencyMs": 342,
"error": null
}Auth Required: Yes
Sync available models from the provider.
Response (200):
{
"added": 3,
"updated": 1,
"removed": 0,
"total": 4
}Auth Required: Yes
List all models for a specific connector.
Response (200): Array of ConnectorModel objects.
[
{
"id": "uuid",
"connectorId": "uuid",
"modelKey": "gpt-4o",
"displayName": "GPT-4o",
"lifecycle": "ACTIVE",
"supportsStreaming": true,
"supportsTools": true,
"supportsVision": true,
"supportsAudio": false
}
]Auth Required: Yes
Create a routing policy.
Request Body:
{
"name": "Route coding to Anthropic",
"routingMode": "AUTO",
"priority": 100,
"config": {
"taskTypes": ["coding", "debugging"],
"preferredProvider": "anthropic",
"preferredModel": "claude-sonnet-4"
}
}| Field | Type | Required | Constraints |
|---|---|---|---|
| name | string | Yes | 1-255 chars |
| routingMode | enum | Yes | AUTO, MANUAL_MODEL, LOCAL_ONLY, PRIVACY_FIRST, LOW_LATENCY, HIGH_REASONING, COST_SAVER |
| priority | integer | Yes | 0-1000 |
| config | object | Yes | JSON object with policy-specific configuration |
Response (201): RoutingPolicy object.
Auth Required: Yes
List routing policies with pagination.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
Response (200): Paginated list of RoutingPolicy objects.
Auth Required: Yes
Get a single routing policy.
Response (200): RoutingPolicy object.
Auth Required: Yes
Update a routing policy.
Request Body: Same fields as create (all optional).
Response (200): Updated RoutingPolicy object.
Auth Required: Yes
Delete a routing policy.
Response (200): Deleted RoutingPolicy object.
Auth Required: Yes
Evaluate what routing decision would be made for a given input (dry run).
Request Body:
{
"content": "Review this Python function for bugs",
"routingMode": "AUTO",
"provider": null,
"model": null
}Response (200):
{
"selectedProvider": "anthropic",
"selectedModel": "claude-sonnet-4",
"confidence": 0.92,
"reasonTags": ["coding", "code_review"],
"privacyClass": "CLOUD",
"costClass": "STANDARD",
"fallback": {
"provider": "openai",
"model": "gpt-4o"
}
}Auth Required: Yes
Get routing decision history for a thread.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
Response (200): Paginated list of RoutingDecision objects.
Auth Required: Yes
Create a memory record.
Request Body:
{
"type": "INSTRUCTION",
"content": "Always respond in formal English.",
"sourceThreadId": "uuid",
"sourceMessageId": "uuid"
}| Field | Type | Required | Constraints |
|---|---|---|---|
| type | enum | Yes | FACT, PREFERENCE, INSTRUCTION, SUMMARY |
| content | string | Yes | 1-50,000 chars |
| sourceThreadId | string | No | Max 255 chars |
| sourceMessageId | string | No | Max 255 chars |
Response (201): MemoryRecord object.
Auth Required: Yes
List the authenticated user's memories.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| type | string | - | Filter by memory type |
| search | string | - | Full-text search in content |
Response (200): Paginated list of MemoryRecord objects.
Auth Required: Yes
Get a single memory record. Must be owned by the authenticated user.
Response (200): MemoryRecord object.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | Memory record not found |
| 403 | FORBIDDEN_MEMORY_ACCESS | Memory belongs to another user |
Auth Required: Yes
Update a memory record. Must be owned by the authenticated user.
Request Body:
{
"content": "Updated memory content",
"type": "FACT",
"isEnabled": true
}Response (200): Updated MemoryRecord object.
Auth Required: Yes
Permanently delete a memory record. Must be owned by the authenticated user.
Response (200): Deleted MemoryRecord object.
Auth Required: Yes
Toggle the enabled/disabled state of a memory. Must be owned by the authenticated user.
Response (200): Updated MemoryRecord object with flipped isEnabled value.
Auth Required: Yes
Create a context pack.
Request Body:
{
"name": "Project Coding Standards",
"description": "Conventions and patterns for our NestJS project",
"scope": "PERSONAL"
}Response (201): ContextPack object.
Auth Required: Yes
List the authenticated user's context packs.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| search | string | - | Search by name |
Response (200): Paginated list of ContextPack objects.
Auth Required: Yes
Get a context pack with all its items. Must be owned by the authenticated user.
Response (200): ContextPackWithItems object.
{
"id": "uuid",
"name": "Project Coding Standards",
"description": "...",
"scope": "PERSONAL",
"userId": "uuid",
"items": [
{
"id": "uuid",
"type": "TEXT",
"content": "Use strict TypeScript with no-any rule.",
"fileId": null,
"sortOrder": 1
}
]
}Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | Context pack not found |
| 403 | FORBIDDEN_CONTEXT_PACK_ACCESS | Pack belongs to another user |
Auth Required: Yes
Update a context pack. Must be owned by the authenticated user.
Response (200): Updated ContextPack object.
Auth Required: Yes
Delete a context pack and all its items. Must be owned by the authenticated user.
Response (200): Deleted ContextPack object.
Auth Required: Yes
Add an item to a context pack. Must own the pack.
Request Body:
{
"type": "TEXT",
"content": "Always use camelCase for variables.",
"fileId": null,
"sortOrder": 1
}Response (201): ContextPackItem object.
Auth Required: Yes
Remove an item from a context pack. Must own the pack.
Response (200): Deleted ContextPackItem object.
Auth Required: Yes
Upload a file for use in chat conversations.
Request Body:
{
"filename": "data.csv",
"mimeType": "text/csv",
"sizeBytes": 2048576,
"content": "base64-encoded-content..."
}Response (201): File object with ingestionStatus.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_MIME_TYPE | The file's MIME type is not in the allowed list |
| 400 | FILE_TOO_LARGE | File exceeds the 50MB maximum size |
Auth Required: Yes
List the authenticated user's files.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
Response (200): Paginated list of File objects.
Auth Required: Yes
Get a single file's metadata. Must be owned by the authenticated user.
Response (200): File object.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | File not found |
| 403 | FORBIDDEN_FILE_ACCESS | File belongs to another user |
Auth Required: Yes
Delete a file and its chunks. Must be owned by the authenticated user.
Response (200): Deleted File object.
Auth Required: Yes
Download a file. Must be owned by the authenticated user.
Response: File binary stream with appropriate Content-Type and Content-Disposition headers.
Auth Required: Yes
Get all chunks for a file. Must be owned by the authenticated user.
Response (200): Array of FileChunk objects.
[
{
"id": "uuid",
"fileId": "uuid",
"chunkIndex": 0,
"content": "chunk content..."
}
]Auth Required: Yes
List audit log entries.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| action | string | - | Filter by action (e.g., message.completed) |
| severity | string | - | Filter by severity |
| entityType | string | - | Filter by entity type |
| startDate | string | - | ISO 8601 date (inclusive) |
| endDate | string | - | ISO 8601 date (inclusive) |
| search | string | - | Full-text search |
Response (200): Paginated list of AuditLog objects.
Auth Required: Yes
Get aggregated audit statistics.
Response (200): AuditStatsResponse object with event counts and severity breakdowns.
Auth Required: Yes
List usage ledger entries.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| provider | string | - | Filter by provider |
| model | string | - | Filter by model |
| startDate | string | - | ISO 8601 date |
| endDate | string | - | ISO 8601 date |
Response (200): Paginated list of UsageLedger objects.
Auth Required: Yes
Get aggregated usage summary.
Response (200): UsageSummaryResponse object.
Auth Required: Yes
Get cost breakdown by provider and model.
Response (200): CostSummaryResult object.
Auth Required: Yes
Get latency statistics by provider and model.
Response (200): LatencySummaryResult object.
Auth Required: Yes
List local models with optional filters.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| runtime | string | - | Filter by runtime type |
| isInstalled | boolean | - | Filter by installation status |
Response (200): Paginated list of LocalModel objects.
Auth Required: Yes
Pull (download) a model from the Ollama registry.
Request Body:
{
"name": "mistral",
"tag": "7b"
}Response (201): LocalModel object.
Auth Required: Yes
Assign a role to a local model.
Request Body:
{
"modelId": "uuid",
"role": "ROUTER"
}| Field | Type | Required | Values |
|---|---|---|---|
| modelId | string | Yes | UUID of an existing local model |
| role | enum | Yes | ROUTER, FALLBACK_CHAT, REASONING, CODING |
Response (201): LocalModelRoleAssignment object.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | ENTITY_NOT_FOUND | Model with the given ID does not exist |
Auth Required: No (@Public)
Generate text using a local Ollama model. Used internally by routing and memory services.
Request Body:
{
"model": "gemma3:4b",
"prompt": "Classify this message...",
"temperature": 0,
"maxTokens": 500
}Response (200):
{
"response": "Generated text...",
"model": "gemma3:4b",
"done": true,
"promptEvalCount": 45,
"evalCount": 120
}Auth Required: No (@Public)
Check Ollama runtime health.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| runtime | string | OLLAMA | Runtime type to check |
Response (200):
{
"healthy": true,
"runtime": "OLLAMA",
"modelsLoaded": 5,
"latencyMs": 42
}Auth Required: Yes
List all runtime configurations.
Response (200): Array of RuntimeConfig objects.
Auth Required: Yes
List the authenticated user's image generations.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Max 100 |
Response (200): Paginated list of image generation records.
Auth Required: Yes
Get details of a specific image generation.
Response (200): Image generation record.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | IMAGE_NOT_FOUND | Image generation record not found |
Auth Required: Yes
Retry a failed image generation with the same parameters.
Response (200):
{
"generationId": "uuid",
"status": "PENDING"
}Auth Required: Yes
Retry image generation with a different provider or model.
Request Body (optional):
{
"provider": "gemini",
"model": "imagen-3"
}Response (200):
{
"generationId": "uuid",
"status": "PENDING",
"provider": "gemini",
"model": "imagen-3"
}Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | NO_ALTERNATE_MODEL | No alternate image model is available |
Auth Required: No (@Public)
Server-Sent Events for image generation progress.
Response: Event stream with generation status updates.
Auth Required: Yes
List the authenticated user's file generations.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Max 100 |
Response (200): Paginated list of file generation records.
Auth Required: Yes
Get details of a specific file generation.
Response (200): File generation record.
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | FILE_GENERATION_NOT_FOUND | File generation record not found |
Auth Required: Yes
Retry a failed file generation.
Response (200):
{
"generationId": "uuid",
"status": "PENDING"
}Auth Required: No (@Public)
Server-Sent Events for file generation progress.
Response: Event stream with generation status updates.
Auth Required: No (@Public)
Submit frontend log entries.
Request Body:
{
"level": "ERROR",
"message": "Failed to load chat thread",
"component": "ChatPage",
"action": "loadThread",
"userId": "uuid",
"route": "/chat/uuid",
"userAgent": "Mozilla/5.0..."
}Response (201):
{
"id": "objectId",
"acknowledged": true
}Auth Required: Yes | Role: ADMIN or OPERATOR
Search client logs.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| level | string | - | Filter by log level |
| component | string | - | Filter by component name |
| search | string | - | Full-text search |
| startDate | string | - | ISO 8601 date |
| endDate | string | - | ISO 8601 date |
Response (200): Paginated list of ClientLog objects.
Auth Required: Yes | Role: ADMIN or OPERATOR
Get client log statistics.
Response (200): ClientLogStatsResponse object.
Auth Required: No (@Public, for inter-service use)
Submit a single server log entry.
Request Body:
{
"level": "ERROR",
"message": "Database connection failed",
"serviceName": "chat-service",
"module": "ChatMessagesService",
"action": "createMessage",
"requestId": "uuid",
"traceId": "uuid"
}Response (201): CreateServerLogResponse.
Auth Required: No (@Public, for inter-service use)
Submit multiple server log entries at once.
Request Body:
{
"entries": [
{ "level": "INFO", "message": "...", "serviceName": "..." },
{ "level": "WARN", "message": "...", "serviceName": "..." }
]
}Response (201): BatchCreateServerLogsResponse.
Auth Required: Yes
Search server logs with extensive filters.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Items per page |
| level | string | - | Filter by log level |
| serviceName | string | - | Filter by service name |
| module | string | - | Filter by module name |
| controller | string | - | Filter by controller name |
| action | string | - | Filter by action |
| requestId | string | - | Filter by request ID |
| traceId | string | - | Filter by trace ID |
| userId | string | - | Filter by user ID |
| threadId | string | - | Filter by thread ID |
| provider | string | - | Filter by AI provider |
| search | string | - | Full-text search |
| startDate | string | - | ISO 8601 date |
| endDate | string | - | ISO 8601 date |
Response (200): Paginated list of ServerLog objects.
Auth Required: Yes
Get server log statistics.
Response (200): ServerLogStatsResponse object.
Auth Required: No
Aggregated health status of all services.
Response (200):
{
"status": "healthy",
"services": {
"auth": { "status": "healthy", "latencyMs": 12 },
"chat": { "status": "healthy", "latencyMs": 8 },
"connector": { "status": "healthy", "latencyMs": 15 },
"routing": { "status": "healthy", "latencyMs": 10 },
"memory": { "status": "healthy", "latencyMs": 11 },
"file": { "status": "healthy", "latencyMs": 9 },
"audit": { "status": "healthy", "latencyMs": 14 },
"ollama": { "status": "healthy", "latencyMs": 45 },
"client-logs": { "status": "healthy", "latencyMs": 7 },
"server-logs": { "status": "healthy", "latencyMs": 8 }
},
"timestamp": "2026-04-09T10:30:00.000Z"
}All customer and admin routes require a Bearer JWT except gateway webhooks.
User ownership is derived from that JWT; request bodies never accept a userId,
price, or currency as authority.
| Method | Route | Purpose |
|---|---|---|
| GET | /api/v1/billing/plans |
Public active plan and current-price catalog |
| POST | /api/v1/billing/checkout-sessions |
Create a server-priced hosted checkout |
| GET | /api/v1/billing/checkout-sessions/:id |
Read the caller's checkout state |
| POST | /api/v1/billing/payment-method-setup-sessions |
Start purpose-constrained tokenization |
| GET | /api/v1/billing/me |
Current subscription |
| GET | /api/v1/billing/invoices |
Owned immutable invoice list |
| GET | /api/v1/billing/invoices/:id/pdf |
Owned invoice PDF download |
| GET | /api/v1/billing/payment-methods |
Masked saved payment methods |
| DELETE | /api/v1/billing/payment-methods/:id |
Remove an owned method |
| POST | /api/v1/billing/subscription/change/quote |
Create a bounded proration quote |
| POST | /api/v1/billing/subscription/change/confirm |
Confirm one quote exactly once |
| POST | /api/v1/billing/subscription/cancel |
Schedule cancellation |
| POST | /api/v1/billing/subscription/resume |
Resume before period end |
Checkout creation accepts only plan id, billing interval, gateway, and an idempotency key. The service resolves the authenticated owner and immutable price version.
POST /api/v1/payments/webhooks/paypal and the Paymob webhook variants are
public because gateways cannot present a ClawAI JWT. They verify the exact raw
body with the provider signature/HMAC, claim a unique provider event, and return
a generic receipt. nginx applies dedicated body and rate limits.
Every route below requires ADMIN_PLANS_MANAGE.
| Method | Route | Purpose |
|---|---|---|
| GET | /api/v1/admin/plans/:id/price-versions |
Read append-only price history |
| POST | /api/v1/admin/plans/:id/price-versions |
Publish a MONTHLY price; QUARTERLY/SEMIANNUAL/YEARLY are re-derived (a hand-typed one is PLAN_INTERVAL_PRICE_DERIVED) |
| GET | /api/v1/admin/plans/:id/interval-pricing |
The plan's term discounts and the active price per interval |
| PUT | /api/v1/admin/plans/:id/interval-discounts |
Set {quarterlyDiscountBps, semiannualDiscountBps, yearlyDiscountBps} (0-9000) and re-derive prices |
| DELETE | /api/v1/admin/plans/:id |
Retire a plan and migrate assignments |
| GET | /api/v1/admin/billing/dashboard |
Revenue, cost, margin, refund, drift data |
| GET | /api/v1/admin/billing/dashboard/price-version-counts |
Subscribers by immutable price version |
| GET | /api/v1/admin/billing/refunds/refundable-transactions |
Refundable ledger |
| POST | /api/v1/admin/billing/refunds |
Reserve and submit a partial/full refund |
| POST | /api/v1/admin/billing/reconciliation |
Start an owner-safe reconciliation run |
All monetary fields are integer minor units or integer microUSD. Refund writes require an operator idempotency key; cumulative pending/completed refunds may not exceed the captured amount.
Plan deletion is a durable retirement, never a physical history deletion. An
optional { "replacementPlanId": "..." } body selects an active replacement;
without it, auth selects the nearest active plan with a higher display order.
The default plan cannot be retired. Active assignments move immediately so
users receive the replacement without a mid-period charge, while paid rows
remain billing-pending until payment schedules the immutable replacement price
at the existing period boundary.
Payment status and entitlement reads live under /internal/payments/*;
auth-service plan/price/provider-cost projections live under /internal/*.
They require Authorization: Service <INTER_SERVICE_AUTH_TOKEN>, validate
bounded Zod projections, and are intentionally absent from every nginx config.
Plan retirement adds two auth-owned internal contracts used only by the owner-locked payment reconciliation job:
| Method | Route | Purpose |
|---|---|---|
| GET | /api/v1/internal/plans/retirement-migrations/pending?limit=50 |
Read a bounded batch of pending paid moves |
| POST | /api/v1/internal/plans/retirement-migrations/:id/outcome |
CAS-record scheduled/superseded/failed state |
The pending response includes auth-derived source/replacement plan IDs,
replacement slug, user ID, and the opaque payment subscription ID. Outcome
writes apply only while the row is still BILLING_SCHEDULE_PENDING, so a user
override or replay cannot rewrite an already-decided migration.
Routed by nginx under /api/v1/plugins; /api/v1/internal/* is never routed. Every route needs a JWT. Reads need PLUGINS_USE, install writes need PLUGINS_INSTALL, admin routes need PLUGINS_ADMIN. The user id always comes from the JWT. The authoritative description is the service guide; error codes are in the error catalog.
| Method | Route | Purpose |
|---|---|---|
| GET | /api/v1/plugins/catalog |
Browse (q, category, cursor, limit, locale) |
| GET | /api/v1/plugins/catalog/:slug |
Plugin detail with tools, risk levels and installed
|
| POST | /api/v1/plugins/installs |
Install {slug, version?, settings?} under the plan cap |
| GET | /api/v1/plugins/installs |
The caller's installs |
| GET | /api/v1/plugins/installs/mentions |
Installed plugins for the composer mention list |
| GET | /api/v1/plugins/mentions |
Installed plugins and the caller's own MCP servers for the @ picker |
| GET | /api/v1/plugins/installs/:id |
One install |
| PATCH | /api/v1/plugins/installs/:id |
Enable or disable, change settings |
| DELETE | /api/v1/plugins/installs/:id |
Uninstall (consents and credential cascade) |
| PUT | /api/v1/plugins/installs/:id/credential |
Write-only API key (never returned) |
| DELETE | /api/v1/plugins/installs/:id/credential |
Remove the key |
| POST | /api/v1/plugins/installs/:id/consent |
Grant {toolName, level}
|
| DELETE | /api/v1/plugins/installs/:id/consent/:toolName |
Revoke (204, idempotent) |
| GET | /api/v1/plugins/admin/catalog |
Admin catalog |
| GET | /api/v1/plugins/admin/catalog/:slug |
Admin detail |
| PATCH | /api/v1/plugins/admin/catalog/:slug |
Kill switch, featured, per-tool disable |
| GET | /api/v1/plugins/admin/settings |
Admin settings |
| PATCH | /api/v1/plugins/admin/settings |
Update settings, range checked, all or nothing |
| GET | /api/v1/plugins/admin/curated |
Curated entries (health, tools, texts) |
| POST | /api/v1/plugins/admin/curated/preview |
Bounded tools/list probe of a server |
| POST | /api/v1/plugins/admin/curated |
Create a curated entry (publish probes first) |
| PUT | /api/v1/plugins/admin/curated/:slug |
Edit; a changed manifest adds a patch version |
| PATCH | /api/v1/plugins/admin/curated/:slug/state |
Switch on or off |
| POST | /api/v1/plugins/admin/curated/:slug/health-check |
Ping and record status and latency |
| DELETE | /api/v1/plugins/admin/curated/:slug |
Soft delete |
| GET | /api/v1/plugins/admin/servers |
Approval queue of users' servers |
| POST | /api/v1/plugins/admin/servers/:id/approve |
Approve a pending server |
| POST | /api/v1/plugins/admin/servers/:id/reject |
Reject with a reason |
| GET | /api/v1/plugins/admin/rules |
Allow and deny rules |
| POST | /api/v1/plugins/admin/rules |
Add a rule |
| DELETE | /api/v1/plugins/admin/rules/:id |
Remove a rule |
| GET | /api/v1/plugins/admin/audit |
Admin audit log |
| GET | /api/v1/health |
Service health |
ClawAI Wiki · source-controlled under wiki/ · generated/mirrored from the current repository knowledge layer.
ClawAI Wiki
Product
- Product-Vision
- Flagship-Features
- Business-Overview
- Requirements-Register
- Drift-Log
- Runtime-Managed-Connector-Providers
- Threads
- ADR-Index
Architecture
- System-at-a-Glance
- Architecture-Map
- System-Architecture
- Message-Flow
- End-to-End-Data-Flow
- Routing-Engine
- Event-Bus
- Security-Architecture
Platform
- Backend-Services
- Frontend-Architecture
- Shared-Packages
- Database-Reference
- API-Reference
- Provider-Catalog
- Memory-and-Context-Architecture
- File-Support-Architecture
- Research-and-Web-Crawling
Runtime & Ops
- Docker-Guide
- Nginx-Reference
- CI-CD-Pipeline
- Logging-and-Observability
- Operational-Runbooks
- Troubleshooting
Quality
Engineering OS
- AI-Native-Engineering-OS
- Generated-Manifests
- Rules-Catalog
- Skills-Catalog
- Context-Layer
- Engineering-Memory
- Tooling-and-Automation
- Scripts-and-Operations
- ESLint-and-Architecture-Enforcement
- Extended-Work-Skills
Agents
Reference