Chatbook Features API Documentation

February 27, 2026 · View on GitHub

Overview

This document describes the current, implemented API surface for Chatbook-adjacent features in the multi-user API: Chat Dictionaries, World Books (lorebooks), Document Generator, and Chatbooks (import/export). It reflects the code as of v0.1.0 in tldw_Server_API and corrects any previously published mismatches.

Auth + Rate Limits

  • Single-user: X-API-KEY: <key>
  • Multi-user: Authorization: Bearer <JWT>
  • Standard limits apply; background jobs (export/import, document generator) may have additional concurrency limits.

Notes on conventions used here:

  • Base API prefix is /api/v1.
  • Authentication uses either X-API-KEY (single-user) or Authorization: Bearer <JWT> (multi-user).
  • Pagination uses limit and offset where applicable and responses include a total field in the body (no page/per_page headers).
  • Rate limits are applied via RG ingress policies and may differ by route.

Table of Contents

  1. Chat Dictionary API
  2. World Book Manager API
  3. Chat Tools (Slash Commands)
  4. Document Generator API
  5. Chatbooks Import/Export API

Chat Dictionary API

Pattern-based text replacement for conversations. Supports literal and regex patterns with probability and optional grouping.

Base URL

/api/v1/chat/dictionaries

Endpoints

1. Create Dictionary

POST /api/v1/chat/dictionaries

Creates a new chat dictionary for the authenticated user.

Request body:

{
  "name": "Fantasy Terms",
  "description": "Convert modern terms to fantasy equivalents"
}

Response body (ChatDictionaryResponse):

{
  "id": 1,
  "name": "Fantasy Terms",
  "description": "Convert modern terms to fantasy equivalents",
  "is_active": true,
  "version": 1,
  "entry_count": 0,
  "created_at": "2024-01-01T00:00:00Z",
  "updated_at": "2024-01-01T00:00:00Z"
}

2. List Dictionaries

GET /api/v1/chat/dictionaries

Lists all dictionaries for the authenticated user.

Query parameters:

  • include_inactive (boolean): Include inactive dictionaries (default: false)

Response body:

{
  "dictionaries": [
    {
      "id": 1,
      "name": "Fantasy Terms",
      "description": "Convert modern terms to fantasy equivalents",
      "is_active": true,
      "version": 1,
      "entry_count": 25,
      "created_at": "2024-01-01T00:00:00Z",
      "updated_at": "2024-01-01T00:00:00Z"
    }
  ],
  "total": 1,
  "active_count": 1,
  "inactive_count": 0
}

3. Get Dictionary (with entries)

GET /api/v1/chat/dictionaries/{dictionary_id}

Response body:

{
  "id": 1,
  "name": "Fantasy Terms",
  "description": "Convert modern terms to fantasy equivalents",
  "is_active": true,
  "entries": [
    {
      "id": 1,
      "pattern": "car",
      "replacement": "carriage",
      "type": "literal",
      "probability": 1.0,
      "max_replacements": 0,
      "enabled": true,
      "case_sensitive": true,
      "group": null,
      "timed_effects": null
    }
  ],
  "created_at": "2024-01-01T00:00:00Z"
}

4. Add Entry

POST /api/v1/chat/dictionaries/{dictionary_id}/entries

Notes:

  • type must be literal or regex.
  • probability is a float in [0.0, 1.0].
  • case_sensitive applies to literal matching.
  • max_replacements of 0 means “unlimited”.
  • timed_effects supports { sticky, cooldown, delay } in seconds (optional).

Request body:

{
  "pattern": "phone",
  "replacement": "sending stone",
  "type": "literal",
  "probability": 1.0,
  "max_replacements": 0,
  "enabled": true,
  "case_sensitive": true
}

Response body:

{
  "id": 2,
  "dictionary_id": 1,
  "pattern": "phone",
  "replacement": "sending stone",
  "probability": 1.0,
  "group": null,
  "timed_effects": null,
  "max_replacements": 0,
  "type": "literal",
  "enabled": true,
  "case_sensitive": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

5. Update Entry

PUT /api/v1/chat/dictionaries/entries/{entry_id}

Request body (all fields optional):

{
  "pattern": "telephone",
  "replacement": "communication crystal",
  "probability": 0.9,
  "type": "literal",
  "enabled": true,
  "case_sensitive": true,
  "group": "tech",
  "max_replacements": 0
}

6. Delete Entry

DELETE /api/v1/chat/dictionaries/entries/{entry_id}

7. Process Text

POST /api/v1/chat/dictionaries/process

Request body:

{
  "text": "I'll call you on my phone from the car",
  "token_budget": 1000,
  "dictionary_id": 1,
  "max_iterations": 5
}

Response body:

{
  "original_text": "I'll call you on my phone from the car",
  "processed_text": "I'll call you on my sending stone from the carriage",
  "replacements": 2,
  "iterations": 1,
  "entries_used": [3, 5],
  "token_budget_exceeded": false,
  "processing_time_ms": 2.4
}

8. List Entries

GET /api/v1/chat/dictionaries/{dictionary_id}/entries

Query parameters:

  • group (string, optional)

Response body:

{
  "entries": [
    {
      "id": 1,
      "dictionary_id": 1,
      "pattern": "car",
      "replacement": "carriage",
      "probability": 1.0,
      "group": null,
      "timed_effects": null,
      "max_replacements": 0,
      "type": "literal",
      "enabled": true,
      "case_sensitive": true,
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-01T00:00:00Z"
    }
  ],
  "total": 1,
  "dictionary_id": 1,
  "group": null
}

9. Import Dictionary (Markdown)

POST /api/v1/chat/dictionaries/import

Notes:

  • Markdown import is intended for lightweight interoperability and may not preserve all advanced entry settings.
  • Use JSON import/export for full-fidelity round trips.

Request body:

{
  "name": "Fantasy Terms",
  "content": "# Fantasy Terms\n\n## Entry: AI\n- **Type**: literal\n- **Replacement**: Artificial Intelligence\n- **Enabled**: true\n",
  "activate": true
}

10. Import Dictionary (JSON)

POST /api/v1/chat/dictionaries/import/json

Request body:

{
  "data": {
    "name": "Fantasy Terms",
    "entries": [
      {"pattern": "car", "replacement": "carriage", "type": "literal"}
    ]
  },
  "activate": true
}

11. Export Dictionary (Markdown)

GET /api/v1/chat/dictionaries/{dictionary_id}/export

Notes:

  • Markdown export is human-readable, but advanced fields such as timed effects, max_replacements, and other non-standard metadata may not round-trip fully.
  • Prefer JSON export when you need guaranteed full-fidelity backup/restore behavior.

Response body:

{
  "name": "Fantasy Terms",
  "content": "# Fantasy Terms\n\ncar: carriage\nphone: sending stone\n",
  "entry_count": 25,
  "group_count": 3
}

12. Export Dictionary (JSON)

GET /api/v1/chat/dictionaries/{dictionary_id}/export/json

Notes:

  • JSON export is the canonical full-fidelity format for dictionary backup, migration, and re-import.

Response body:

{
  "name": "Fantasy Terms",
  "description": "Convert modern terms to fantasy equivalents",
  "entries": [
    {"pattern": "car", "replacement": "carriage", "type": "literal", "probability": 1.0}
  ]
}

13. Update Dictionary

PUT /api/v1/chat/dictionaries/{dictionary_id}

Update dictionary metadata (name, description, is_active).

14. Delete Dictionary

DELETE /api/v1/chat/dictionaries/{dictionary_id}

15. Dictionary Statistics

GET /api/v1/chat/dictionaries/{dictionary_id}/statistics

Aggregate statistics for a dictionary (counts by type, groups, average probability, usage if available).

16. Validate Dictionary (New)

POST /api/v1/chat/dictionaries/validate

Validate dictionary structure, regex safety, and template syntax. Returns a structured report.

Request body:

{
  "data": {
    "name": "Example",
    "entries": [
      {"type": "literal", "pattern": "today", "replacement": "It is {{ now('%B %d') }}."},
      {"type": "regex", "pattern": "User:(\\w+)", "replacement": "Hello, {{ match.group(1) }}!"}
    ]
  },
  "schema_version": 1,
  "strict": false
}

Response body:

{
  "ok": true,
  "schema_version": 1,
  "errors": [],
  "warnings": [{"code": "regex_ambiguous", "field": "entries[1].pattern", "message": "…"}],
  "entry_stats": {"total": 2, "regex": 1, "literal": 1},
  "suggested_fixes": []
}

Semantics:

  • strict=false (default) returns a report with errors and warnings but does not cause the API call itself to fail; callers decide how to handle issues.
  • strict=true is intended for server-side workflows (e.g., Chatbooks import) where certain error classes (schema, regex safety/timeouts, template parse/forbidden constructs, output/size limits) should be treated as fatal.
  • Unknown or unsupported schema_version values still return HTTP 200 with a schema_invalid error code in the response payload; HTTP 400 is reserved for malformed request bodies that do not match ValidateDictionaryRequest.

Planned additions (not yet implemented): Clone dictionary, toggle active status, bulk add/update entries, search entries across dictionaries.


World Book Manager API

Contextual lore injection for character conversations. Supports keyword-based entry matching and character-specific attachments.

Base URL

/api/v1/characters/world-books

Endpoints

1. Create World Book

POST /api/v1/characters/world-books

Request body:

{
  "name": "Fantasy Campaign",
  "description": "D&D campaign setting",
  "scan_depth": 3,
  "token_budget": 1000,
  "recursive_scanning": true,
  "enabled": true
}

Response body:

{
  "id": 1,
  "name": "Fantasy Campaign",
  "description": "D&D campaign setting",
  "scan_depth": 3,
  "token_budget": 1000,
  "recursive_scanning": true,
  "enabled": true,
  "version": 1,
  "entry_count": 0,
  "created_at": "2024-01-01T00:00:00Z",
  "last_modified": "2024-01-01T00:00:00Z"
}

2. List World Books

GET /api/v1/characters/world-books

Query parameters:

  • include_disabled (boolean): Include disabled world books

Response body:

{
  "world_books": [
    {
      "id": 1,
      "name": "Fantasy Campaign",
      "description": "D&D campaign setting",
      "entry_count": 50,
      "enabled": true,
      "created_at": "2024-01-01T00:00:00Z"
    }
  ],
  "total": 1,
  "enabled_count": 1,
  "disabled_count": 0
}

3. Get World Book

GET /api/v1/characters/world-books/{world_book_id}

Response body:

{
  "id": 1,
  "name": "Fantasy Campaign",
  "entries": [
    {
      "id": 1,
      "keywords": ["dragon", "wyrm"],
      "content": "Dragons are ancient magical beings",
      "priority": 100,
      "enabled": true
    }
  ]
}

4. Add Entry

POST /api/v1/characters/world-books/{world_book_id}/entries

Request body:

{
  "keywords": ["magic", "wizard", "spell"],
  "content": "Magic in this world comes from ley lines",
  "priority": 90,
  "enabled": true
}

5. Update Entry

PUT /api/v1/characters/world-books/entries/{entry_id}

6. Delete Entry

DELETE /api/v1/characters/world-books/entries/{entry_id}

7. Attach to Character

POST /api/v1/characters/{character_id}/world-books

Request body:

{
  "world_book_id": 1,
  "enabled": true,
  "priority": 0
}

8. Detach from Character

DELETE /api/v1/characters/{character_id}/world-books/{world_book_id}

9. Get Character World Books

GET /api/v1/characters/{character_id}/world-books

10. Process Context

POST /api/v1/characters/world-books/process

Request body:

{
  "text": "Tell me about dragons and magic",
  "character_id": 1,
  "max_tokens": 1000
}

Response body:

{
  "injected_content": "[World Info: Dragons are ancient magical beings...]\n[World Info: Magic in this world comes from ley lines...]",
  "entries_matched": 2,
  "tokens_used": 200,
  "books_used": 1,
  "entry_ids": [1, 2]
}

2. Attach World Book to Character

POST /api/v1/characters/{character_id}/world-books

Body:

{ "world_book_id": 10, "enabled": true, "priority": 100 }

3. Detach World Book from Character

DELETE /api/v1/characters/{character_id}/world-books/{world_book_id}

11. Search Entries

Not implemented. (Planned)

12. Import World Book

POST /api/v1/characters/world-books/import

Request body:

{
  "world_book": {"name": "Imported World", "description": "Imported lore"},
  "entries": [
    {"keywords": ["test"], "content": "Test content", "priority": 100}
  ],
  "merge_on_conflict": false
}

13. Export World Book

GET /api/v1/characters/world-books/{world_book_id}/export

14. Clone World Book

Not implemented. (Planned)

15. Delete World Book

DELETE /api/v1/characters/world-books/{world_book_id}

16. Bulk Update Entries

POST /api/v1/characters/world-books/entries/bulk

Request body:

{
  "entry_ids": [1, 2, 3],
  "operation": "disable"
}

17. Get Statistics

GET /api/v1/characters/world-books/{world_book_id}/statistics


Document Generator API

Creates structured documents from conversations. Supports multiple document types and async job management.

Base URL

/api/v1/chat/documents

Document Types

  • timeline
  • study_guide
  • briefing
  • summary
  • q_and_a
  • meeting_notes

Endpoints

1. Generate Document

POST /api/v1/chat/documents/generate

Request body (GenerateDocumentRequest):

{
  "conversation_id": 123,
  "document_type": "timeline",
  "provider": "openai",
  "model": "gpt-4o",
  "api_key": "...",
  "specific_message": null,
  "custom_prompt": "Focus on technical decisions",
  "stream": false,
  "async_generation": false
}

Response (synchronous):

{
  "document_id": 42,
  "conversation_id": 123,
  "document_type": "timeline",
  "title": "Conversation Timeline",
  "content": "Timeline...",
  "provider": "openai",
  "model": "gpt-4o",
  "generation_time_ms": 2500,
  "created_at": "2024-01-01T00:00:00Z"
}

Response (asynchronous):

{
  "job_id": "job_456",
  "status": "pending",
  "conversation_id": 123,
  "document_type": "timeline",
  "message": "Document generation job created",
  "created_at": "2024-01-01T00:00:00Z"
}

2. Get Document

GET /api/v1/chat/documents/{document_id}

3. List Documents

GET /api/v1/chat/documents

4. Delete Document

DELETE /api/v1/chat/documents/{document_id}

5. Get Generation Job Status

GET /api/v1/chat/documents/jobs/{job_id}

6. Cancel Generation Job

DELETE /api/v1/chat/documents/jobs/{job_id}

7. Save Prompt Configuration

POST /api/v1/chat/documents/prompts

8. Get Prompt Configuration

GET /api/v1/chat/documents/prompts/{document_type}

Query parameters:

  • conversation_id (integer, optional)
  • document_type (enum, optional)
  • limit (integer, default 50)

4. Delete Document

DELETE /api/v1/chat/documents/{document_id}

5. Bulk Generate

POST /api/v1/chat/documents/bulk

Request body (BulkGenerateRequest):

{
  "conversation_ids": [123, 456],
  "document_types": ["timeline", "summary", "q_and_a"],
  "provider": "openai",
  "model": "gpt-4o",
  "api_key": "...",
  "async_generation": true
}

6. Get Job Status

GET /api/v1/chat/documents/jobs/{job_id}

Response body (JobStatusResponse):

{
  "job_id": "job_456",
  "status": "completed",
  "conversation_id": 123,
  "document_type": "timeline",
  "provider": "openai",
  "model": "gpt-4o",
  "result_content": "...",
  "created_at": "2024-01-01T00:00:00Z",
  "started_at": "2024-01-01T00:00:01Z",
  "completed_at": "2024-01-01T00:00:05Z",
  "progress_percentage": 100
}

7. Cancel Job

DELETE /api/v1/chat/documents/jobs/{job_id}

8. Save Prompt Config

POST /api/v1/chat/documents/prompts

Request body:

{
  "document_type": "timeline",
  "system_prompt": "You are a helpful document generator...",
  "user_prompt": "Write a timeline...",
  "temperature": 0.7,
  "max_tokens": 2000
}

9. Get Prompt Config

GET /api/v1/chat/documents/prompts/{document_type}

10. Get Statistics

GET /api/v1/chat/documents/statistics


Chatbooks Import/Export API

Export/import collections of chat-related content with job management and secure downloads.

Interplay with Chatbook Tools (Dictionaries & Templates)

Chatbooks can include chat dictionaries and other template-aware content whose behavior is defined in the Chatbook Tools PRD and related APIs:

  • Embedded dictionaries are validated during POST /api/v1/chatbooks/import using the same validator exposed at POST /api/v1/chat/dictionaries/validate. Validation findings (schema/regex/template issues) appear as per-item warnings/errors in Chatbook import job results.

  • The strict flag in /chat/dictionaries/validate is not forwarded directly from Chatbooks; instead, import always calls the validator with strict=false and uses the CHATBOOKS_IMPORT_DICT_STRICT environment flag to decide whether dictionaries with fatal errors are skipped or imported with warnings.

  • Template-related manifest metadata (metadata.template_mode, metadata.template_defaults, metadata.template_timezone, metadata.template_locale) controls template evaluation during Chatbook exports and imports. The template_mode field accepts three values:

    • pass_through (default) — template expressions (e.g. {{ project }}) are preserved verbatim in the artifact. No rendering occurs.
    • render_on_export — template expressions are evaluated at export time using the variables in template_defaults, the timezone from template_timezone (default UTC), and the locale from template_locale. The rendered result replaces the raw template in-place in the exported artifact (note markdown files, dictionary JSON files); the original raw template text is not retained alongside the rendered output.
    • render_on_import — same rendering behavior, but applied at import time. The content stored in the database contains the rendered text, not the raw template.

    Affected fields: note titles and content, dictionary names, descriptions, and entry replacement/content values. For dictionary entry rendering, the CHAT_DICT_TEMPLATES_ENABLED environment flag must also be set to true.

    Failure semantics: rendering uses a fail-safe strategy. If template evaluation fails for any reason (syntax error, undefined variable, timeout, output exceeding the size cap), the original raw template text is preserved unchanged in the artifact — the export or import is never aborted due to a render failure. Failures are logged at DEBUG level and tracked via the template_render_failure_total metrics counter (labeled by source and reason), but no error codes, warnings, or error metadata are surfaced to the client in the job result. To detect silent render failures, operators should monitor the template_render_failure_total and template_render_timeout_total metrics.

Base URL

/api/v1/chatbooks

Endpoints

1. Export Chatbook

POST /api/v1/chatbooks/export

Request body (CreateChatbookRequest):

{
  "name": "My Chatbook Export",
  "description": "Backup of my conversations",
  "content_selections": {
    "conversation": ["conv123", "conv456"],
    "note": ["note789"],
    "character": ["char001"],
    "world_book": [1],
    "dictionary": [2],
    "generated_document": [42]
  },
  "author": "Jane Doe",
  "include_media": false,
  "media_quality": "compressed",
  "include_embeddings": false,
  "include_generated_content": true,
  "tags": ["backup"],
  "categories": ["personal"],
  "async_mode": true
}

Response body (async mode):

{
  "success": true,
  "job_id": "0c9d9a3a-6d1c-4c8f-9c84-9a0c2c2d8f77",
  "status": "pending",
  "message": "Export job created successfully"
}

2. Preview Chatbook (Upload)

POST /api/v1/chatbooks/preview

Preview an uploaded chatbook file (no import).

Request (multipart/form-data):

  • file: ZIP archive

Response body (manifest summary):

{
  "manifest": {
    "version": "1.0",
    "name": "My Chatbook Export",
    "total_conversations": 10,
    "total_notes": 5,
    "total_world_books": 2,
    "total_dictionaries": 1,
    "total_documents": 3,
    "include_media": false
  }
}

3. Get Export Job Status

GET /api/v1/chatbooks/export/jobs/{job_id}

Response body (ExportJobResponse):

{
  "job_id": "0c9d9a3a-6d1c-4c8f-9c84-9a0c2c2d8f77",
  "status": "completed",
  "chatbook_name": "My Chatbook Export",
  "download_url": "/api/v1/chatbooks/download/0c9d9a3a-6d1c-4c8f-9c84-9a0c2c2d8f77",
  "file_size_bytes": 15900000,
  "created_at": "2024-01-01T00:00:00Z",
  "completed_at": "2024-01-01T00:00:30Z"
}

4. Download Export

GET /api/v1/chatbooks/download/{job_id}

Returns a ZIP file with secure headers.

5. Import Chatbook

POST /api/v1/chatbooks/import

Request (multipart/form-data):

  • file: The chatbook archive file (ZIP)
  • Additional fields (ImportChatbookRequest via form fields):
    • conflict_resolution: one of skip, overwrite, rename, merge
    • prefix_imported: boolean
    • import_media: boolean
    • import_embeddings: boolean
    • async_mode: boolean

Response body (async mode):

{
  "success": true,
  "job_id": "import_job_456",
  "status": "pending",
  "message": "Import job created successfully"
}

6. Get Import Job Status

GET /api/v1/chatbooks/import/jobs/{job_id}

Response body (ImportJobResponse):

{
  "job_id": "import_job_456",
  "status": "completed",
  "items_imported": 55,
  "conflicts_found": 5,
  "successful_items": 50,
  "failed_items": 3,
  "skipped_items": 2,
  "conflicts": [],
  "created_at": "2024-01-01T00:00:00Z",
  "completed_at": "2024-01-01T00:01:00Z"
}

7. Validate Chatbook

No dedicated /validate endpoint. Use /chatbooks/preview (upload) to inspect a manifest without importing.

8. List Export Jobs

GET /api/v1/chatbooks/export/jobs

Query parameters:

  • limit (integer, default 100)
  • offset (integer, default 0)

9. List Import Jobs

GET /api/v1/chatbooks/import/jobs

10. Cancel Export Job

DELETE /api/v1/chatbooks/export/jobs/{job_id}

11. Cancel Import Job

DELETE /api/v1/chatbooks/import/jobs/{job_id}

12. Remove Export Job

DELETE /api/v1/chatbooks/export/jobs/{job_id}/remove

13. Remove Import Job

DELETE /api/v1/chatbooks/import/jobs/{job_id}/remove

14. Clean Old Exports

POST /api/v1/chatbooks/cleanup

15. Service Health

GET /api/v1/chatbooks/health

Lightweight health indicator for the Chatbooks subsystem.


Error Responses

Endpoints return standard FastAPI error responses with meaningful HTTP status codes and a detail message. Some success responses include a success boolean for convenience (e.g., Chatbooks export/import). Domain-specific errors may include additional fields (see response schemas).

Rate Limiting

Endpoint-specific limits are enforced with RG policies where applied:

  • Chatbooks POST /export: 5/minute
  • Chatbooks POST /import: 5/minute
  • Chatbooks POST /preview: 10/minute
  • Chatbooks GET /download/{job_id}: 20/minute

A global limiter may also be active depending on environment. When rate limits are hit, a 429 is returned.

Pagination

List endpoints use limit and offset query parameters and include a total count in the response body.

Webhooks

Webhook notifications for Chatbooks are planned but not yet implemented. See the Chatbook Developer Guide for design notes.

SDK Examples

Python

import json
from urllib.request import Request, urlopen

class ChatbookAPI:
    def __init__(self, base_url, token):
        self.base_url = base_url.rstrip("/")
        self.headers = {"Authorization": f"Bearer {token}"}

    def _request_json(self, method, path, payload):
        url = f"{self.base_url}{path}"
        data = json.dumps(payload).encode("utf-8")
        headers = {"Content-Type": "application/json", **self.headers}
        req = Request(url, data=data, headers=headers, method=method)
        with urlopen(req) as resp:
            return json.loads(resp.read().decode("utf-8"))

    def create_dictionary(self, name, description):
        return self._request_json(
            "POST",
            "/api/v1/chat/dictionaries",
            {"name": name, "description": description},
        )

    def process_text(self, text, token_budget=1000, dictionary_id=None):
        return self._request_json(
            "POST",
            "/api/v1/chat/dictionaries/process",
            {"text": text, "token_budget": token_budget, "dictionary_id": dictionary_id},
        )

JavaScript

class ChatbookAPI {
  constructor(baseUrl, token) {
    this.baseUrl = baseUrl;
    this.headers = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
  }

  async exportChatbook(req) {
    const response = await fetch(`${this.baseUrl}/api/v1/chatbooks/export`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(req)
    });
    return response.json();
  }

  async getExportStatus(jobId) {
    const response = await fetch(
      `${this.baseUrl}/api/v1/chatbooks/export/jobs/${jobId}`,
      { headers: this.headers }
    );
    return response.json();
  }
}

Migration Guide

For users migrating from the TUI application:

  1. Authentication is required for all API calls.
  2. User content is isolated per-user (per-user DBs, per-user export/import paths).
  3. Large operations support async jobs with status polling and secure download URLs.
  4. Import conflict resolution supports skip, overwrite, rename, and merge.
  5. Endpoint-level rate limiting is enforced; plan for 429 handling.

Chat Tools (Slash Commands)

Discovery endpoint for slash commands that run before LLM dispatch (e.g., /time, /weather, /skills, /skill).

Injection behavior (configurable):

  • CHAT_COMMAND_INJECTION_MODE=system (default): insert command result as a separate system message and strip the /command token from the user's text.
  • CHAT_COMMAND_INJECTION_MODE=preface: prefix the user's text with the command result.
  • CHAT_COMMAND_INJECTION_MODE=replace: replace the user's text entirely with the command result.

List Commands (New)

GET /api/v1/chat/commands

Returns available commands (RBAC-filtered where applicable).

Response body:

{
  "commands": [
    {
      "name": "time",
      "description": "Show the current time (optional TZ).",
      "required_permission": "chat.commands.time",
      "usage": "/time [timezone]",
      "args": ["timezone"],
      "requires_api_key": true,
      "rate_limit": "per-user 10/min, global 100/min",
      "rbac_required": true
    },
    {
      "name": "weather",
      "description": "Show current weather for a location.",
      "required_permission": "chat.commands.weather",
      "usage": "/weather [location]",
      "args": ["location"],
      "requires_api_key": true,
      "rate_limit": "per-user 10/min, global 100/min",
      "rbac_required": true
    },
    {
      "name": "skills",
      "description": "List invocable skills for this user.",
      "required_permission": "chat.commands.skills",
      "usage": "/skills [filter]",
      "args": ["filter"],
      "requires_api_key": true,
      "rate_limit": "per-user 10/min, global 100/min",
      "rbac_required": true
    },
    {
      "name": "skill",
      "description": "Execute an invocable skill by name.",
      "required_permission": "chat.commands.skill",
      "usage": "/skill <name> [args]",
      "args": ["name", "args"],
      "requires_api_key": true,
      "rate_limit": "per-user 10/min, global 100/min",
      "rbac_required": true
    }
  ]
}

Field definitions:

  • requires_api_key — whether invoking this command requires authenticated API access.

Notes:

  • The commands list is filtered per-user based on AuthNZ/RBAC and deployment configuration.
  • /skills only lists invocable skills (user_invocable=true and disable_model_invocation=false) for the authenticated user.
  • /skill returns explicit error text for missing name, unknown skills, and non-invocable skills.
  • Clients should treat GET /api/v1/chat/commands as the per-session source of truth and avoid caching the list long-term, since RBAC or configuration changes can add or remove commands at any time.