Integrate OpenAPI/Swagger documentation using drf-spectacular, enhancing API documentation with comprehensive guides and schema generation. Add multiple documentation files covering authentication, error codes, rate limiting, and migration strategies. Update settings and URLs to support new documentation endpoints and schema configurations.
This commit is contained in:
545
docs/API-DOCUMENTATION.md
Normal file
545
docs/API-DOCUMENTATION.md
Normal file
@@ -0,0 +1,545 @@
|
||||
# IGNY8 API Documentation v1.0
|
||||
|
||||
**Base URL**: `https://api.igny8.com/api/v1/`
|
||||
**Version**: 1.0.0
|
||||
**Last Updated**: 2025-11-16
|
||||
|
||||
## Quick Links
|
||||
|
||||
- [Interactive API Documentation (Swagger UI)](#swagger-ui)
|
||||
- [Authentication Guide](#authentication)
|
||||
- [Response Format](#response-format)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Rate Limiting](#rate-limiting)
|
||||
- [Pagination](#pagination)
|
||||
- [Endpoint Reference](#endpoint-reference)
|
||||
|
||||
---
|
||||
|
||||
## Swagger UI
|
||||
|
||||
Interactive API documentation is available at:
|
||||
- **Swagger UI**: `https://api.igny8.com/api/docs/`
|
||||
- **ReDoc**: `https://api.igny8.com/api/redoc/`
|
||||
- **OpenAPI Schema**: `https://api.igny8.com/api/schema/`
|
||||
|
||||
The Swagger UI provides:
|
||||
- Interactive endpoint testing
|
||||
- Request/response examples
|
||||
- Authentication testing
|
||||
- Schema definitions
|
||||
- Code samples in multiple languages
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
### JWT Bearer Token
|
||||
|
||||
All endpoints require JWT Bearer token authentication except:
|
||||
- `POST /api/v1/auth/login/` - User login
|
||||
- `POST /api/v1/auth/register/` - User registration
|
||||
|
||||
### Getting an Access Token
|
||||
|
||||
**Login Endpoint:**
|
||||
```http
|
||||
POST /api/v1/auth/login/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "your_password"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": 1,
|
||||
"email": "user@example.com",
|
||||
"username": "user",
|
||||
"role": "owner"
|
||||
},
|
||||
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
},
|
||||
"request_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
### Using the Token
|
||||
|
||||
Include the token in the `Authorization` header:
|
||||
|
||||
```http
|
||||
GET /api/v1/planner/keywords/
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Token Expiration
|
||||
|
||||
- **Access Token**: 15 minutes
|
||||
- **Refresh Token**: 7 days
|
||||
|
||||
Use the refresh token to get a new access token:
|
||||
```http
|
||||
POST /api/v1/auth/refresh/
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"refresh": "your_refresh_token"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Response Format
|
||||
|
||||
### Success Response
|
||||
|
||||
All successful responses follow this unified format:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "Example",
|
||||
...
|
||||
},
|
||||
"message": "Optional success message",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
### Paginated Response
|
||||
|
||||
List endpoints return paginated data:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"count": 100,
|
||||
"next": "https://api.igny8.com/api/v1/planner/keywords/?page=2",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{"id": 1, "name": "Keyword 1"},
|
||||
{"id": 2, "name": "Keyword 2"},
|
||||
...
|
||||
],
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
All error responses follow this unified format:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Validation failed",
|
||||
"errors": {
|
||||
"email": ["This field is required"],
|
||||
"password": ["Password too short"]
|
||||
},
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### HTTP Status Codes
|
||||
|
||||
| Code | Meaning | Description |
|
||||
|------|---------|-------------|
|
||||
| 200 | OK | Request successful |
|
||||
| 201 | Created | Resource created successfully |
|
||||
| 204 | No Content | Resource deleted successfully |
|
||||
| 400 | Bad Request | Validation error or invalid request |
|
||||
| 401 | Unauthorized | Authentication required |
|
||||
| 403 | Forbidden | Permission denied |
|
||||
| 404 | Not Found | Resource not found |
|
||||
| 409 | Conflict | Resource conflict (e.g., duplicate) |
|
||||
| 422 | Unprocessable Entity | Validation failed |
|
||||
| 429 | Too Many Requests | Rate limit exceeded |
|
||||
| 500 | Internal Server Error | Server error |
|
||||
|
||||
### Error Response Structure
|
||||
|
||||
All errors include:
|
||||
- `success`: Always `false`
|
||||
- `error`: Top-level error message
|
||||
- `errors`: Field-specific errors (for validation errors)
|
||||
- `request_id`: Unique request ID for debugging
|
||||
|
||||
### Example Error Responses
|
||||
|
||||
**Validation Error (400):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Validation failed",
|
||||
"errors": {
|
||||
"email": ["Invalid email format"],
|
||||
"password": ["Password must be at least 8 characters"]
|
||||
},
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Authentication Error (401):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Authentication required",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Permission Error (403):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Permission denied",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Not Found (404):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Resource not found",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Rate Limit (429):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Rate limit exceeded",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Rate limits are scoped by operation type. Check response headers for limit information:
|
||||
|
||||
- `X-Throttle-Limit`: Maximum requests allowed
|
||||
- `X-Throttle-Remaining`: Remaining requests in current window
|
||||
- `X-Throttle-Reset`: Time when limit resets (Unix timestamp)
|
||||
|
||||
### Rate Limit Scopes
|
||||
|
||||
| Scope | Limit | Description |
|
||||
|-------|-------|-------------|
|
||||
| `ai_function` | 10/min | AI content generation, clustering |
|
||||
| `image_gen` | 15/min | Image generation |
|
||||
| `content_write` | 30/min | Content creation, updates |
|
||||
| `content_read` | 100/min | Content listing, retrieval |
|
||||
| `auth` | 20/min | Login, register, password reset |
|
||||
| `auth_strict` | 5/min | Sensitive auth operations |
|
||||
| `planner` | 60/min | Keyword, cluster, idea operations |
|
||||
| `planner_ai` | 10/min | AI-powered planner operations |
|
||||
| `writer` | 60/min | Task, content management |
|
||||
| `writer_ai` | 10/min | AI-powered writer operations |
|
||||
| `system` | 100/min | Settings, prompts, profiles |
|
||||
| `system_admin` | 30/min | Admin-only system operations |
|
||||
| `billing` | 30/min | Credit queries, usage logs |
|
||||
| `billing_admin` | 10/min | Credit management (admin) |
|
||||
| `default` | 100/min | Default for endpoints without scope |
|
||||
|
||||
### Handling Rate Limits
|
||||
|
||||
When rate limited (429), the response includes:
|
||||
- Error message: "Rate limit exceeded"
|
||||
- Headers with reset time
|
||||
- Wait until `X-Throttle-Reset` before retrying
|
||||
|
||||
**Example:**
|
||||
```http
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
X-Throttle-Limit: 60
|
||||
X-Throttle-Remaining: 0
|
||||
X-Throttle-Reset: 1700123456
|
||||
|
||||
{
|
||||
"success": false,
|
||||
"error": "Rate limit exceeded",
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pagination
|
||||
|
||||
List endpoints support pagination with query parameters:
|
||||
|
||||
- `page`: Page number (default: 1)
|
||||
- `page_size`: Items per page (default: 10, max: 100)
|
||||
|
||||
### Example Request
|
||||
|
||||
```http
|
||||
GET /api/v1/planner/keywords/?page=2&page_size=20
|
||||
```
|
||||
|
||||
### Paginated Response
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"count": 100,
|
||||
"next": "https://api.igny8.com/api/v1/planner/keywords/?page=3&page_size=20",
|
||||
"previous": "https://api.igny8.com/api/v1/planner/keywords/?page=1&page_size=20",
|
||||
"results": [
|
||||
{"id": 21, "name": "Keyword 21"},
|
||||
{"id": 22, "name": "Keyword 22"},
|
||||
...
|
||||
],
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination Fields
|
||||
|
||||
- `count`: Total number of items
|
||||
- `next`: URL to next page (null if last page)
|
||||
- `previous`: URL to previous page (null if first page)
|
||||
- `results`: Array of items for current page
|
||||
|
||||
---
|
||||
|
||||
## Endpoint Reference
|
||||
|
||||
### Authentication Endpoints
|
||||
|
||||
#### Login
|
||||
```http
|
||||
POST /api/v1/auth/login/
|
||||
```
|
||||
|
||||
#### Register
|
||||
```http
|
||||
POST /api/v1/auth/register/
|
||||
```
|
||||
|
||||
#### Refresh Token
|
||||
```http
|
||||
POST /api/v1/auth/refresh/
|
||||
```
|
||||
|
||||
### Planner Endpoints
|
||||
|
||||
#### List Keywords
|
||||
```http
|
||||
GET /api/v1/planner/keywords/
|
||||
```
|
||||
|
||||
#### Create Keyword
|
||||
```http
|
||||
POST /api/v1/planner/keywords/
|
||||
```
|
||||
|
||||
#### Get Keyword
|
||||
```http
|
||||
GET /api/v1/planner/keywords/{id}/
|
||||
```
|
||||
|
||||
#### Update Keyword
|
||||
```http
|
||||
PUT /api/v1/planner/keywords/{id}/
|
||||
PATCH /api/v1/planner/keywords/{id}/
|
||||
```
|
||||
|
||||
#### Delete Keyword
|
||||
```http
|
||||
DELETE /api/v1/planner/keywords/{id}/
|
||||
```
|
||||
|
||||
#### Auto Cluster Keywords
|
||||
```http
|
||||
POST /api/v1/planner/keywords/auto_cluster/
|
||||
```
|
||||
|
||||
### Writer Endpoints
|
||||
|
||||
#### List Tasks
|
||||
```http
|
||||
GET /api/v1/writer/tasks/
|
||||
```
|
||||
|
||||
#### Create Task
|
||||
```http
|
||||
POST /api/v1/writer/tasks/
|
||||
```
|
||||
|
||||
### System Endpoints
|
||||
|
||||
#### System Status
|
||||
```http
|
||||
GET /api/v1/system/status/
|
||||
```
|
||||
|
||||
#### List Prompts
|
||||
```http
|
||||
GET /api/v1/system/prompts/
|
||||
```
|
||||
|
||||
### Billing Endpoints
|
||||
|
||||
#### Credit Balance
|
||||
```http
|
||||
GET /api/v1/billing/credits/balance/balance/
|
||||
```
|
||||
|
||||
#### Usage Summary
|
||||
```http
|
||||
GET /api/v1/billing/credits/usage/summary/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Examples
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
BASE_URL = "https://api.igny8.com/api/v1"
|
||||
|
||||
# Login
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/auth/login/",
|
||||
json={"email": "user@example.com", "password": "password"}
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
if data['success']:
|
||||
token = data['data']['access']
|
||||
|
||||
# Use token for authenticated requests
|
||||
headers = {
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
# Get keywords
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/planner/keywords/",
|
||||
headers=headers
|
||||
)
|
||||
keywords_data = response.json()
|
||||
|
||||
if keywords_data['success']:
|
||||
keywords = keywords_data['results']
|
||||
print(f"Found {keywords_data['count']} keywords")
|
||||
else:
|
||||
print(f"Error: {keywords_data['error']}")
|
||||
else:
|
||||
print(f"Login failed: {data['error']}")
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```javascript
|
||||
const BASE_URL = 'https://api.igny8.com/api/v1';
|
||||
|
||||
// Login
|
||||
const loginResponse = await fetch(`${BASE_URL}/auth/login/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: 'user@example.com',
|
||||
password: 'password'
|
||||
})
|
||||
});
|
||||
|
||||
const loginData = await loginResponse.json();
|
||||
|
||||
if (loginData.success) {
|
||||
const token = loginData.data.access;
|
||||
|
||||
// Use token for authenticated requests
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
// Get keywords
|
||||
const keywordsResponse = await fetch(
|
||||
`${BASE_URL}/planner/keywords/`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
const keywordsData = await keywordsResponse.json();
|
||||
|
||||
if (keywordsData.success) {
|
||||
const keywords = keywordsData.results;
|
||||
console.log(`Found ${keywordsData.count} keywords`);
|
||||
} else {
|
||||
console.error('Error:', keywordsData.error);
|
||||
}
|
||||
} else {
|
||||
console.error('Login failed:', loginData.error);
|
||||
}
|
||||
```
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
# Login
|
||||
curl -X POST https://api.igny8.com/api/v1/auth/login/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"user@example.com","password":"password"}'
|
||||
|
||||
# Get keywords (with token)
|
||||
curl -X GET https://api.igny8.com/api/v1/planner/keywords/ \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request ID
|
||||
|
||||
Every API request includes a unique `request_id` in the response. Use this ID for:
|
||||
- Debugging issues
|
||||
- Log correlation
|
||||
- Support requests
|
||||
|
||||
The `request_id` is included in:
|
||||
- All success responses
|
||||
- All error responses
|
||||
- Response headers (`X-Request-ID`)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For API support:
|
||||
- Check the [Interactive Documentation](https://api.igny8.com/api/docs/)
|
||||
- Review [Error Codes Reference](ERROR-CODES.md)
|
||||
- Contact support with your `request_id`
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-16
|
||||
**API Version**: 1.0.0
|
||||
|
||||
Reference in New Issue
Block a user