| title | API Documentation Generation |
|---|---|
| description | Complete guide to generating OpenAPI specifications, Swagger documentation, and API testing tools for DeployStack Backend development. |
| sidebarTitle | Overview |
This document explains how to generate and use the OpenAPI specification for the DeployStack Backend API.
The DeployStack Backend uses Fastify with Swagger plugins to automatically generate OpenAPI 3.0 specifications. Route schemas are defined using reusable JSON Schema constants for type safety and documentation. This provides:
- Interactive Documentation: Swagger UI interface for testing APIs
- Postman Integration: JSON/YAML specs that can be imported into Postman
- Automated Generation: Specifications are generated from actual route code
- Type Safety: TypeScript interfaces provide compile-time checking
IMPORTANT: Before developing any protected API endpoints, read the API Security Best Practices documentation. It covers critical security patterns including:
- Authorization Before Validation: Why
preValidationmust be used instead ofpreHandlerfor authorization - Proper Error Responses: Ensuring unauthorized users get 403 Forbidden, not validation errors
- Security Testing: How to test authorization properly
- Common Pitfalls: Security anti-patterns to avoid
Key Rule: Always use preValidation for authorization checks to prevent information disclosure to unauthorized users.
The DeployStack Backend supports multiple authentication methods for different use cases, allowing web users, CLI users, and satellites to access appropriate endpoints.
-
Cookie Authentication (Web Users)
- Session-based authentication using HTTP cookies
- Automatic for web browser requests
- Uses
sessioncookie set during login
-
OAuth2 Bearer Token Authentication (CLI Users)
- RFC 6749 compliant OAuth2 implementation with PKCE
- Uses
Authorization: Bearer <token>header - Scope-based access control
-
Registration Token Authentication (Satellite Registration)
- Specialized JWT-based tokens for secure satellite pairing
- Uses
Authorization: Bearer deploystack_satellite_*header pattern - Single-use tokens with scope validation (global vs team)
- Exclusively for
/api/satellites/registerendpoint
Use these middleware functions to enable dual authentication on endpoints:
import { requireAuthenticationAny, requireOAuthScope } from '../../middleware/oauthMiddleware';
export default async function yourRoute(server: FastifyInstance) {
server.get('/your-endpoint', {
preValidation: [
requireAuthenticationAny(), // Accept either auth method
requireOAuthScope('your:scope') // Enforce OAuth2 scope
],
schema: {
security: [
{ cookieAuth: [] }, // Cookie authentication
{ bearerAuth: [] } // OAuth2 Bearer token
]
}
}, async (request, reply) => {
// Endpoint accessible via both authentication methods
const authType = request.tokenPayload ? 'oauth2' : 'cookie';
const userId = request.user!.id;
});
}Available OAuth2 scopes for fine-grained access control:
mcp:read- Read MCP server installations and configurationsaccount:read- Read account informationuser:read- Read user profile informationteams:read- Read team memberships and informationoffline_access- Maintain access when not actively using the application
GET /api/oauth2/auth- Authorization endpoint (PKCE required)GET /api/oauth2/consent- User consent pagePOST /api/oauth2/consent- Process consent decisionPOST /api/oauth2/token- Token exchange endpoint
- Client ID:
deploystack-gateway-cli - Redirect URIs:
http://localhost:8976/oauth/callback,http://127.0.0.1:8976/oauth/callback - PKCE: Required (SHA256 method)
- Token Lifetime: 1 hour access tokens, 30 day refresh tokens
Web Users (Cookie Authentication):
curl -b cookies.txt "http://localhost:3000/api/teams/me/default"CLI Users (OAuth2 Bearer Token):
curl -H "Authorization: Bearer <access_token>" \
"http://localhost:3000/api/teams/me/default"npm run api:specThis command:
- Starts a temporary server
- Generates both JSON and YAML specifications
- Saves files to
api-spec.jsonandapi-spec.yaml - Provides URLs for interactive documentation
- Automatically shuts down the server
Output:
api-spec.json- OpenAPI JSON specification (for Postman import)api-spec.yaml- OpenAPI YAML specification
npm run api:spec:jsonRequires the development server to be running (npm run dev).
npm run api:spec:yamlRequires the development server to be running (npm run dev).
cd services/backend
npm run api:spec# Terminal 1: Start the server
cd services/backend
npm run dev
# Terminal 2: Generate specifications
npm run api:spec:json
npm run api:spec:yamlWhen the server is running (npm run dev), you can access:
- Interactive Docs: http://localhost:3000/documentation
- JSON Spec: http://localhost:3000/documentation/json
- YAML Spec: http://localhost:3000/documentation/yaml
- Run
npm run api:specto generate the specification - Open Postman
- Click "Import"
- Select the generated
api-spec.jsonfile - All API endpoints will be imported with proper documentation
IMPORTANT: Every new API endpoint must be created in a separate file following the established directory structure pattern. Do not add route definitions directly to src/routes/index.ts.
- Separate Files: Each route or group of related routes must be in its own file
- Directory Organization: Group related routes in directories (e.g.,
/auth/,/users/,/health/) - Import Pattern: Routes are imported and registered in
src/routes/index.ts - Consistent Naming: Use descriptive names that match the route purpose
- Modular Approach: Keep route files small and focused - aim for 1-3 related methods per file maximum
- Maintainability: Avoid large monolithic route files that become difficult to maintain
services/backend/src/routes/
├── index.ts # Main routes registration (imports only)
├── health/
│ └── index.ts # Health check endpoints
├── auth/
│ ├── loginEmail.ts # Email login endpoint
│ ├── registerEmail.ts # Email registration endpoint
│ └── logout.ts # Logout endpoint
├── db/
│ ├── status.ts # Database status endpoint
│ └── setup.ts # Database setup endpoint
├── users/
│ └── index.ts # User management endpoints
└── teams/
└── index.ts # Team management endpoints
For complex feature areas, break down routes into smaller, focused files:
services/backend/src/routes/mcp/
├── index.ts # Route registration only
├── categories/
│ ├── create.ts # POST /api/mcp/categories (1 method)
│ ├── update.ts # PUT /api/mcp/categories/{id} (1 method)
│ └── delete.ts # DELETE /api/mcp/categories/{id} (1 method)
├── servers/
│ ├── list.ts # GET /api/mcp/servers (1 method)
│ ├── get.ts # GET /api/mcp/servers/{id} (1 method)
│ ├── search.ts # GET /api/mcp/servers/search (1 method)
│ ├── create-global.ts # POST /api/mcp/servers/global (1 method)
│ ├── update-global.ts # PUT /api/mcp/servers/global/{id} (1 method)
│ └── delete-global.ts # DELETE /api/mcp/servers/global/{id} (1 method)
└── versions/
├── list.ts # GET /api/mcp/servers/{id}/versions (1 method)
├── create.ts # POST /api/mcp/servers/{id}/versions (1 method)
└── update.ts # PUT /api/mcp/servers/{id}/versions/{versionId} (1 method)
Benefits of Modular Approach:
- Easier Maintenance: Small files are easier to understand and modify
- Better Testing: Individual route files can be tested in isolation
- Team Collaboration: Multiple developers can work on different routes without conflicts
- Clear Responsibility: Each file has a single, clear purpose
- Reduced Complexity: Avoid hundreds of lines in single files
MANDATORY PATTERN: For route directories that implement complete CRUD operations on a single entity (Create, Read, Update, Delete), you must create a shared schemas.ts file to eliminate duplication and ensure consistency.
Create a schemas.ts file when your route directory contains:
- Multiple endpoints operating on the same core entity
- Duplicate schema definitions across route files
- Common response structures (error responses, entity objects)
- Shared parameter validation (ID parameters, common fields)
Always Share:
- Error response schemas that appear in multiple files
- Core entity object schemas used in responses
- Common parameter schemas (ID validation, shared fields)
- Shared TypeScript interfaces for consistent typing
Keep Endpoint-Specific:
- Request body schemas with different requirements (create vs. update)
- Endpoint-specific success response wrappers
- Unique validation rules specific to individual operations
- File Naming: Use
schemas.tsin the route directory - Export Pattern: Export const schemas and TypeScript interfaces
- Import Pattern: Import shared components into individual route files
- Documentation: Include clear comments explaining shared vs. specific schemas
- Single Source of Truth: Entity structure defined once
- Elimination of Duplication: Removes copy-pasted schema definitions
- Improved Maintainability: Changes happen in one place
- Enhanced Consistency: All endpoints use identical shared structures
- Better Type Safety: Shared interfaces prevent type drift
/mcp/categories/- Complete category management CRUD/mcp/servers/- Server management operations/teams/- Team management functionality- Any directory with 3+ routes operating on the same entity
Each route file should follow this recommended pattern:
import { type FastifyInstance } from 'fastify'
// Reusable Schema Constants
const REQUEST_SCHEMA = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
description: 'Name is required'
},
email: {
type: 'string',
format: 'email',
description: 'Valid email required'
}
},
required: ['name', 'email'],
additionalProperties: false
} as const;
const SUCCESS_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: { type: 'boolean' },
message: { type: 'string' }
},
required: ['success', 'message']
} as const;
const ERROR_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: { type: 'boolean', default: false },
error: { type: 'string' }
},
required: ['success', 'error']
} as const;
// TypeScript interfaces for type safety
interface RequestBody {
name: string;
email: string;
}
interface SuccessResponse {
success: boolean;
message: string;
}
interface ErrorResponse {
success: boolean;
error: string;
}
export default async function yourRoute(server: FastifyInstance) {
server.post('/your-endpoint', {
preValidation: requirePermission('your.permission'), // Authorization FIRST
schema: {
tags: ['Your Category'],
summary: 'Brief description',
description: 'Detailed description. Requires Content-Type: application/json header when sending request body.',
security: [{ cookieAuth: [] }],
// Fastify validation schema
body: REQUEST_SCHEMA,
// OpenAPI documentation (same schema, reused)
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
response: {
200: {
...SUCCESS_RESPONSE_SCHEMA,
description: 'Success'
},
400: {
...ERROR_RESPONSE_SCHEMA,
description: 'Bad Request'
},
401: {
...ERROR_RESPONSE_SCHEMA,
description: 'Unauthorized'
},
403: {
...ERROR_RESPONSE_SCHEMA,
description: 'Forbidden'
}
}
}
}, async (request, reply) => {
// TypeScript type assertion (Fastify has already validated)
const { name, email } = request.body as RequestBody;
// Your route logic
const successResponse: SuccessResponse = {
success: true,
message: `User ${name} processed successfully`
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);
});
}Key Requirements:
- Parameter name:
server: FastifyInstance(notfastify) - Method calls:
server.post()(notfastify.post()) - preValidation first: Authorization before schema
- Reusable schemas: Define schema constants at the top
- Single source: Same schema for both
bodyandrequestBody - TypeScript interfaces: Provide type safety
- Manual JSON serialization: Use
JSON.stringify()for responses
Import and register your route in src/routes/index.ts:
// Import your route
import yourRoute from './your-directory'
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// Register your route
await apiInstance.register(yourRoute);
// Other route registrations...
}, { prefix: '/api' });
}// DON'T: Add routes directly to index.ts
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// ❌ BAD: Inline route definition
apiInstance.get('/my-endpoint', {
schema: { /* ... */ }
}, async () => {
return { message: 'This should be in a separate file!' }
});
}, { prefix: '/api' });
}// GOOD: Import and register separate route files
import myEndpointRoute from './my-endpoint'
export const registerRoutes = (server: FastifyInstance): void => {
server.register(async (apiInstance) => {
// GOOD: Register imported route
await apiInstance.register(myEndpointRoute);
}, { prefix: '/api' });
}- Maintainability: Each endpoint is self-contained and easy to find
- Scalability: Adding new endpoints doesn't clutter the main routes file
- Testing: Individual route files can be tested in isolation
- Code Organization: Related functionality is grouped together
- Team Collaboration: Multiple developers can work on different routes without conflicts
IMPORTANT: The Content-Type: application/json header is required for specific HTTP methods when sending request body data.
- POST requests with request body data
- PUT requests with request body data
- PATCH requests with request body data
- GET requests (no request body)
- DELETE requests (typically no request body)
- HEAD requests (no request body)
function makeRequest(method, path, data = null, cookies = null) {
const options = {
method,
headers: { 'Accept': 'application/json' }
};
// Set Content-Type for methods that send request body data
if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) && data !== null) {
options.headers['Content-Type'] = 'application/json';
}
// Rest of implementation...
}// UNCLEAR: This doesn't indicate WHICH methods need Content-Type
if (data) {
options.headers['Content-Type'] = 'application/json';
}When defining route schemas, explicitly document Content-Type requirements for POST/PUT/PATCH endpoints:
// For endpoints that require Content-Type
const routeSchema = {
tags: ['Category'],
summary: 'Create new item',
description: 'Creates a new item. Requires Content-Type: application/json header when sending request body.',
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
// ... rest of schema
};CRITICAL: The DeployStack Backend uses snake_case for all API request and response field names.
- Query parameters:
category_id,sort_by,user_agent - Response fields:
has_more,total_requests,created_at - Request body fields:
team_id,server_id,process_id
- Consistency: Matches database column naming conventions
- REST API Standards: snake_case is common in RESTful APIs
- Satellite Integration: Ensures compatibility with satellite event system
// Query parameters (snake_case)
interface QueryParams {
category_id?: string;
sort_by?: 'name' | 'github_stars';
user_agent?: string;
}
// Response fields (snake_case)
interface Response {
has_more: boolean;
total_requests: number;
created_at: string;
}Note: TypeScript interfaces and JavaScript variables can use camelCase internally, but all external API fields MUST use snake_case.
For real-time updates, use the /stream suffix pattern:
- REST endpoint:
/api/{resource}/{action} - SSE endpoint:
/api/{resource}/{action}/stream
Example:
GET /api/users/me/mcp/client-activity- REST API with paginationGET /api/users/me/mcp/client-activity/stream- SSE real-time stream
See Server-Sent Events (SSE) for complete SSE documentation.
The DeployStack Backend uses reusable JSON Schema constants for both validation and documentation generation. This approach provides a single source of truth for API schemas.
import { type FastifyInstance } from 'fastify';
// 1. Define reusable JSON Schema constants
const REQUEST_SCHEMA = {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 3,
description: 'The name of the item (min 3 chars)'
},
count: {
type: 'number',
minimum: 1,
description: 'How many items (must be positive)'
},
type: {
type: 'string',
enum: ['postgresql', 'mysql'],
description: 'Database engine type'
}
},
required: ['name', 'count', 'type'],
additionalProperties: false
} as const;
const SUCCESS_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
description: 'Indicates if the operation was successful'
},
itemId: {
type: 'string',
description: 'The UUID of the created/affected item'
},
message: {
type: 'string',
description: 'Optional success message'
}
},
required: ['success', 'itemId']
} as const;
const ERROR_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
default: false,
description: 'Indicates failure'
},
error: {
type: 'string',
description: 'Error message detailing what went wrong'
}
},
required: ['success', 'error']
} as const;
// 2. Define TypeScript interfaces for type safety
interface RequestBody {
name: string;
count: number;
type: 'postgresql' | 'mysql';
}
interface SuccessResponse {
success: boolean;
itemId: string;
message?: string;
}
interface ErrorResponse {
success: boolean;
error: string;
}
// 3. Use schemas in route definition
export default async function yourRoute(server: FastifyInstance) {
server.post('/your-route', {
preValidation: requirePermission('your.permission'),
schema: {
tags: ['Category'], // Your API category
summary: 'Brief description of your endpoint',
description: 'Detailed description of what this endpoint does, its parameters, and expected outcomes. Requires Content-Type: application/json header when sending request body.',
security: [{ cookieAuth: [] }], // Include if authentication is required
// Single schema for both validation AND documentation
body: REQUEST_SCHEMA,
// OpenAPI documentation (same schema, reused)
requestBody: {
required: true,
content: {
'application/json': {
schema: REQUEST_SCHEMA
}
}
},
response: {
200: {
...SUCCESS_RESPONSE_SCHEMA,
description: 'Successful operation'
},
400: {
...ERROR_RESPONSE_SCHEMA,
description: 'Bad Request - Invalid input'
},
401: {
...ERROR_RESPONSE_SCHEMA,
description: 'Unauthorized'
},
403: {
...ERROR_RESPONSE_SCHEMA,
description: 'Forbidden'
}
}
}
}, async (request, reply) => {
// TypeScript type assertion (Fastify has already validated)
const { name, count, type } = request.body as RequestBody;
// Your route handler logic here
const successResponse: SuccessResponse = {
success: true,
itemId: 'some-uuid-v4-here',
message: `Item ${name} processed successfully with ${count} items using ${type}.`
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);
});
}- Single Source of Truth: JSON Schema constants define both validation AND documentation
- Automatic Validation: Fastify automatically validates requests before your handler runs
- No Manual Validation: Remove all manual validation calls and field checks
- Better Error Messages: Fastify provides detailed validation errors automatically
- Type Safety: Handlers receive properly typed, validated data
- Cleaner Code: No redundant validation logic in handlers
- Schema Reuse: Same schema serves both validation and documentation
CRITICAL: All API responses must use manual JSON serialization to ensure consistent JSON output.
// CORRECT: Manual JSON serialization
const successResponse: SuccessResponse = {
success: true,
message: 'Operation completed successfully',
data: { /* your data */ }
};
const jsonString = JSON.stringify(successResponse);
return reply.status(200).type('application/json').send(jsonString);// ❌ WRONG: Direct object response (can cause serialization issues)
return reply.status(200).send({
success: true,
message: 'This might not serialize correctly'
});
// ❌ WRONG: Using reply.send() without JSON.stringify()
const response = { success: true, message: 'Test' };
return reply.status(200).send(response);All error responses must also use manual JSON serialization:
// CORRECT: Error response with manual serialization
const errorResponse: ErrorResponse = {
success: false,
error: 'Detailed error message'
};
const jsonString = JSON.stringify(errorResponse);
return reply.status(400).type('application/json').send(jsonString);Authentication middleware and hooks must also use this pattern:
// CORRECT: Authentication error with manual serialization
const errorResponse = {
success: false,
error: 'Unauthorized: Authentication required.'
};
const jsonString = JSON.stringify(errorResponse);
return reply.status(401).type('application/json').send(jsonString);The manual JSON serialization pattern ensures:
- Consistent, parseable JSON responses
- Proper
success/errorproperties in all responses - Reliable client-server communication
- Passing e2e tests
- No
"[object Object]"serialization issues
Important: You need BOTH properties for complete functionality:
body: Enables Fastify's automatic request validation using the JSON schemarequestBody: Ensures proper OpenAPI specification generation with Content-Type documentation
Without body, validation won't work. Without requestBody, your API specification won't properly document the application/json Content-Type requirement.
❌ Don't do manual validation in handlers:
// BAD: Manual validation (redundant)
if (!request.body.name || !request.body.count) {
return reply.status(400).send({ error: 'Required fields missing' });
}
// BAD: Manual field checks (redundant)
if (request.body.name.length < 3) {
return reply.status(400).send({ error: 'Name too short' });
}
// BAD: Manual enum validation (redundant)
if (request.body.type !== 'postgresql' && request.body.type !== 'mysql') {
return reply.status(400).send({ error: 'Invalid database type' });
}Do trust Fastify's automatic validation:
// GOOD: Trust the validation - if handler runs, data is valid
const { name, count, type } = request.body as RequestBody; // Already validated by FastifyThe validation chain works as follows:
- JSON Schema: Define validation rules using JSON Schema
- Fastify Validation: Fastify automatically validates incoming requests
- Handler: Receives validated, typed data
If validation fails, Fastify automatically returns a 400 error before your handler runs.
See these files for complete examples of proper validation:
src/routes/admin/email/test.ts- REFERENCE IMPLEMENTATION showing complete reusable schema constants approachsrc/routes/db/setup.ts- Database setup with enum validationsrc/routes/db/status.ts- Simple GET endpoint with response schemassrc/routes/auth/loginEmail.ts- Login with required string fieldssrc/routes/auth/registerEmail.ts- Registration with complex validation rules
The logout route (/api/auth/logout) demonstrates proper documentation:
const LOGOUT_RESPONSE_SCHEMA = {
type: 'object',
properties: {
success: {
type: 'boolean',
description: 'Indicates if the logout operation was successful'
},
message: {
type: 'string',
description: 'Human-readable message about the logout result'
}
},
required: ['success', 'message'],
examples: [
{
success: true,
message: 'Logged out successfully.'
}
]
} as const;
const logoutSchema = {
tags: ['Authentication'],
summary: 'User logout',
description: 'Invalidates the current user session and clears authentication cookies',
security: [{ cookieAuth: [] }],
response: {
200: LOGOUT_RESPONSE_SCHEMA
}
};The Fastify server is configured with custom AJV options to ensure compatibility with JSON Schema validation. This configuration is in src/server.ts:
const server = fastify({
logger: loggerConfig,
disableRequestLogging: true,
ajv: {
customOptions: {
strict: false, // Allows unknown keywords in schemas
strictTypes: false, // Disables strict type checking
strictTuples: false // Disables strict tuple checking
}
}
})Why these AJV options are required:
strict: false: AJV v8+ runs in strict mode by default, which rejects schemas containing unknown keywords. This setting allows more flexible schema definitions.strictTypes: false: Prevents strict type validation errors that can occur with complex schemas.strictTuples: false: Allows more flexible tuple handling for array schemas.
Important: These settings don't affect validation behavior - they only allow the schema compilation to succeed. All validation rules defined in your JSON schemas still work exactly as expected.
The Swagger documentation configuration is also in src/server.ts:
await server.register(fastifySwagger, {
openapi: {
openapi: '3.0.0',
info: {
title: 'DeployStack Backend API',
description: 'API documentation for DeployStack Backend',
version: '0.20.5'
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
],
components: {
securitySchemes: {
cookieAuth: {
type: 'apiKey',
in: 'cookie',
name: 'auth_session'
}
}
}
}
});This happens when trying to manually add routes that Swagger UI already provides. The /documentation/json and /documentation/yaml endpoints are automatically created.
Ensure the server is fully started before trying to fetch the specification. The generation script includes a 2-second delay to allow for complete initialization.
Routes without schema definitions will appear in the specification but with minimal documentation. Add schema objects to routes for complete documentation.
To extend API documentation:
- Add schema definitions to more routes using reusable constants
- Define reusable components in the OpenAPI configuration
- Add request body schemas for POST/PUT endpoints
- Include error response schemas (400, 401, 500, etc.)
- Add parameter validation schemas
All plugin routes are automatically namespaced under /api/plugin/<plugin-name>/ for security and isolation:
- Core Routes:
/api/auth/*,/api/users/*,/api/settings/*(protected from plugins) - Plugin Routes:
/api/plugin/<plugin-name>/*(isolated per plugin)
For a plugin with ID example-plugin:
GET /api/plugin/example-plugin/examples
GET /api/plugin/example-plugin/examples/:id
POST /api/plugin/example-plugin/examples
PUT /api/plugin/example-plugin/examples/:id
DELETE /api/plugin/example-plugin/examples/:id- Route Isolation: Plugins cannot interfere with core routes or each other
- Predictable Structure: All plugin APIs follow the same pattern
- Easy Identification: Plugin ownership is clear from the URL
- Automatic Namespacing: No manual prefix management required
Plugins register routes using the PluginRouteManager:
// In plugin's routes.ts file
export async function registerRoutes(routeManager: PluginRouteManager, db: AnyDatabase | null) {
// This becomes /api/plugin/my-plugin/data
routeManager.get('/data', async () => {
return { message: 'Hello from plugin!' };
});
}api-spec.json- Complete OpenAPI 3.0 specification in JSON formatapi-spec.yaml- Complete OpenAPI 3.0 specification in YAML format- Interactive documentation available at
/documentationwhen server is running