API Overview

docsfy exposes a REST API built with FastAPI for generating, managing, and serving AI-powered documentation sites. All endpoints accept and return JSON, and long-running generation work happens asynchronously in the background.

Base URL

By default the server listens on http://0.0.0.0:8000. The host and port are configurable through environment variables:

HOST=0.0.0.0   # Bind address
PORT=8000      # Bind port

The interactive API docs generated by FastAPI are available at /docs (Swagger UI) and /redoc (ReDoc) while the server is running.

Request & Response Format

All API endpoints under /api/ communicate using JSON. Requests that include a body must set Content-Type: application/json. Responses always return JSON unless explicitly noted (e.g., the download endpoint returns a gzipped tarball).

Standard Success Response

Successful responses return a 200 OK status with a JSON body:

{
  "projects": [
    {
      "name": "my-project",
      "repo_url": "https://github.com/org/my-project.git",
      "status": "ready",
      "last_commit_sha": "a1b2c3d4",
      "last_generated": "2026-03-05 14:30:00",
      "page_count": 12
    }
  ]
}

Error Response

Errors follow the standard FastAPI format with a detail field:

{"detail": "Project 'my-project' not found"}

Validation errors (422) include structured location information:

{
  "detail": [
    {
      "type": "value_error",
      "loc": ["body", "repo_url"],
      "msg": "Value error, Invalid git repository URL: 'not-a-url'",
      "input": "not-a-url"
    }
  ]
}

HTTP Status Codes

Code Meaning Used By
200 Success All GET and DELETE endpoints
202 Accepted POST /api/generate — generation started in background
400 Bad Request Invalid project name; project not ready for download
404 Not Found Project or file does not exist
409 Conflict Project is already being generated
422 Unprocessable Entity Request body validation failure

Endpoints at a Glance

Method Path Description
GET /health Health check
POST /api/generate Start documentation generation
GET /api/status List all projects with status
GET /api/projects/{name} Get a single project's details
DELETE /api/projects/{name} Delete a project
GET /api/projects/{name}/download Download generated site as .tar.gz
GET /docs/{project}/{path} Serve generated documentation files

Background Generation with 202 Accepted

Documentation generation is a long-running process — the AI must analyze the repository, plan the documentation structure, and generate every page. Rather than blocking the caller, docsfy uses an asynchronous pattern built on 202 Accepted responses and status polling.

How It Works

Client                              docsfy
  │                                    │
  │  POST /api/generate                │
  │  {"repo_url": "https://..."}       │
  ├───────────────────────────────────►│
  │                                    │ ── validate request
  │         202 Accepted               │ ── save project (status: "generating")
  │  {"project":"x","status":"..."}    │ ── launch background task
  │◄───────────────────────────────────┤
  │                                    │
  │                                    │ ┌─ Background ──────────────┐
  │                                    │ │ 1. Clone repository       │
  │  GET /api/projects/x  (poll)       │ │ 2. Run AI planner         │
  ├───────────────────────────────────►│ │ 3. Generate pages (5 max  │
  │  {"status":"generating",           │ │    concurrently)           │
  │   "page_count": 3}                 │ │ 4. Render HTML site        │
  │◄───────────────────────────────────┤ └───────────────────────────┘
  │                                    │
  │  GET /api/projects/x  (poll)       │
  ├───────────────────────────────────►│
  │  {"status":"ready",                │
  │   "page_count": 12}               │
  │◄───────────────────────────────────┤

Step 1: Submit a Generation Request

Send a POST to /api/generate with either a remote URL or a local path:

# Remote repository
curl -X POST http://localhost:8000/api/generate \
  -H "Content-Type: application/json" \
  -d '{"repo_url": "https://github.com/org/my-project.git"}'

# Local repository
curl -X POST http://localhost:8000/api/generate \
  -H "Content-Type: application/json" \
  -d '{"repo_path": "/home/user/my-project"}'

The server responds immediately with 202 Accepted:

{"project": "my-project", "status": "generating"}

Note: You must provide exactly one of repo_url or repo_path — not both. Sending both or neither returns a 422 validation error.

Step 2: Poll for Status

While the background task runs, poll the project endpoint to track progress:

curl http://localhost:8000/api/projects/my-project

During generation, the page_count field increments as pages complete:

{
  "name": "my-project",
  "repo_url": "https://github.com/org/my-project.git",
  "status": "generating",
  "page_count": 5,
  "plan_json": "{\"project_name\": \"my-project\", ...}",
  "error_message": null,
  "created_at": "2026-03-05 14:00:00",
  "updated_at": "2026-03-05 14:02:30"
}

Tip: The plan_json field is populated early in the process — after the planner stage but before page generation finishes. You can parse it to show the planned documentation structure to users while pages are still being generated.

Step 3: Generation Complete

The status field transitions to one of two terminal states:

  • ready — generation succeeded; the site is available to serve or download
  • error — generation failed; check error_message for details
{
  "name": "my-project",
  "status": "ready",
  "page_count": 12,
  "last_commit_sha": "a1b2c3d4e5f6",
  "last_generated": "2026-03-05 14:05:00"
}

Conflict Prevention

Only one generation can run per project at a time. Submitting a request for a project that is already generating returns 409 Conflict:

{"detail": "Project 'my-project' is already being generated"}

This is tracked via an in-memory set in main.py:

_generating: set[str] = set()

if project_name in _generating:
    raise HTTPException(
        status_code=409,
        detail=f"Project '{project_name}' is already being generated",
    )

Incremental Updates

When you resubmit a generation request for an existing project, docsfy compares the current commit SHA against the stored last_commit_sha. If the repository has not changed and the project status is ready, generation is skipped entirely:

existing = await get_project(project_name)
if (
    existing
    and existing.get("last_commit_sha") == commit_sha
    and existing.get("status") == "ready"
):
    logger.info(f"[{project_name}] Project is up to date at {commit_sha[:8]}")
    await update_project_status(project_name, status="ready")
    return

To force a full regeneration regardless of cache state, set force: true in the request body:

curl -X POST http://localhost:8000/api/generate \
  -H "Content-Type: application/json" \
  -d '{"repo_url": "https://github.com/org/my-project.git", "force": true}'

Warning: Using force: true deletes the entire page cache for the project and regenerates all pages from scratch. This is significantly slower than an incremental update.

The Generation Request Body

The POST /api/generate endpoint accepts a GenerateRequest model with the following fields:

Field Type Required Default Description
repo_url string One of repo_url / repo_path null Git repository URL (HTTPS or SSH)
repo_path string One of repo_url / repo_path null Absolute path to a local git repository
ai_provider string No Server default AI provider: "claude", "gemini", or "cursor"
ai_model string No Server default Model identifier to use
ai_cli_timeout integer No Server default Timeout per AI call in seconds (must be > 0)
force boolean No false Force full regeneration, ignoring cache

URL validation accepts two formats:

  • HTTPS: https://github.com/org/repo.git
  • SSH: git@github.com:org/repo.git

The project name is automatically extracted from the last path component of the URL or directory name.

AI Provider Configuration

The AI provider and model can be configured at the server level via environment variables or overridden per request. Server defaults are defined in config.py:

class Settings(BaseSettings):
    ai_provider: str = "claude"
    ai_model: str = "claude-opus-4-6[1m]"
    ai_cli_timeout: int = Field(default=60, gt=0)

Per-request overrides take precedence:

curl -X POST http://localhost:8000/api/generate \
  -H "Content-Type: application/json" \
  -d '{
    "repo_url": "https://github.com/org/my-project.git",
    "ai_provider": "gemini",
    "ai_model": "gemini-2.5-pro",
    "ai_cli_timeout": 120
  }'

Project Lifecycle

A project moves through these states during its lifetime:

                ┌──────────────┐
  POST /api/    │              │
  generate ────►│  generating  │
                │              │
                └──────┬───────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
     ┌────────────┐    ┌────────────┐
     │   ready    │    │   error    │
     └────────────┘    └────────────┘
         │                   │
         │  POST /api/       │  POST /api/
         │  generate         │  generate
         ▼                   ▼
     ┌──────────────┐
     │  generating  │  (re-enters the cycle)
     └──────────────┘
Status Meaning
generating Background task is running; page_count increases as pages complete
ready Documentation site is built and available for serving or download
error Generation failed; see error_message for details

The Generation Pipeline

When a generation request is accepted, the background task executes these stages:

  1. AI CLI check — verifies the configured AI provider is available
  2. Repository setup — clones the remote repo (shallow, --depth 1) or validates the local path
  3. Planning — sends the repository to the AI planner, which returns a structured DocPlan (navigation groups and pages)
  4. Page generation — generates each page concurrently (up to 5 at a time via MAX_CONCURRENT_PAGES), caching results to disk
  5. Site rendering — converts markdown pages to HTML, copies static assets, and builds the search index

Pages are generated concurrently with a bounded semaphore to avoid overwhelming the AI provider:

MAX_CONCURRENT_PAGES = 5

results = await run_parallel_with_limit(
    coroutines, max_concurrency=MAX_CONCURRENT_PAGES
)

If any individual page fails, the pipeline continues and substitutes a fallback message rather than aborting the entire build:

if not success:
    output = f"# {title}\n\n*Documentation generation failed. Please re-run.*"

Authentication

The docsfy API does not currently implement authentication. All endpoints are publicly accessible without API keys or tokens.

Warning: If you deploy docsfy in a shared or production environment, place it behind a reverse proxy or API gateway that handles authentication and access control.