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_urlorrepo_path— not both. Sending both or neither returns a422validation 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_jsonfield 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 downloaderror— generation failed; checkerror_messagefor 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: truedeletes 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:
- AI CLI check — verifies the configured AI provider is available
- Repository setup — clones the remote repo (shallow,
--depth 1) or validates the local path - Planning — sends the repository to the AI planner, which returns a structured
DocPlan(navigation groups and pages) - Page generation — generates each page concurrently (up to 5 at a time via
MAX_CONCURRENT_PAGES), caching results to disk - 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.