From dc9dba2c9eef50a320c6476fcde9360033d154c2 Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Sat, 6 Dec 2025 23:37:48 +0000 Subject: [PATCH] API Refernce orginal --- docs/API/API-COMPLETE-REFERENCE.md | 1389 ++++++++++++++++++++++++++++ 1 file changed, 1389 insertions(+) create mode 100644 docs/API/API-COMPLETE-REFERENCE.md diff --git a/docs/API/API-COMPLETE-REFERENCE.md b/docs/API/API-COMPLETE-REFERENCE.md new file mode 100644 index 00000000..6ef0bc17 --- /dev/null +++ b/docs/API/API-COMPLETE-REFERENCE.md @@ -0,0 +1,1389 @@ +# IGNY8 API Complete Reference v1.0 + +**Base URL**: `https://api.igny8.com/api/v1/` +**Version**: 1.0.0 +**Last Updated**: 2025-01-XX +**Status**: ✅ **100% IMPLEMENTED** - All endpoints use unified format + +**Purpose**: Complete, unified reference for IGNY8 API covering authentication, endpoints, response formats, error handling, rate limiting, permissions, and integration examples. + +--- + +## Table of Contents + +1. [Quick Start](#quick-start) +2. [Overview & Architecture](#overview--architecture) +3. [Authentication & Authorization](#authentication--authorization) +4. [Response Format Standard](#response-format-standard) +5. [Error Handling](#error-handling) +6. [Rate Limiting](#rate-limiting) +7. [Pagination](#pagination) +8. [Roles & Permissions](#roles--permissions) +9. [Tenant / Site / Sector Scoping](#tenant--site--sector-scoping) +10. [Complete Endpoint Reference](#complete-endpoint-reference) +11. [Integration Examples](#integration-examples) +12. [Testing & Debugging](#testing--debugging) +13. [Change Management](#change-management) + +--- + +## Quick Start + +### Interactive Documentation + +- **Swagger UI**: `https://api.igny8.com/api/docs/` +- **ReDoc**: `https://api.igny8.com/api/redoc/` +- **OpenAPI Schema**: `https://api.igny8.com/api/schema/` + +### Basic Example + +```python +import requests + +BASE_URL = "https://api.igny8.com/api/v1" + +# 1. 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'] + + # 2. Use token for authenticated requests + headers = { + 'Authorization': f'Bearer {token}', + 'Content-Type': 'application/json' + } + + # 3. Get keywords + response = requests.get( + f"{BASE_URL}/planner/keywords/", + headers=headers + ) + result = response.json() + + if result['success']: + keywords = result['results'] # Paginated results + print(f"Found {result['count']} keywords") +``` + +--- + +## Overview & Architecture + +### API Standard v1.0 - Key Principles + +1. **Unified Response Format**: All endpoints return consistent JSON structure +2. **Layered Authorization**: Authentication → Tenant Access → Role → Site/Sector +3. **Centralized Error Handling**: All errors wrapped in unified format +4. **Scoped Rate Limiting**: Different limits for different operation types +5. **Tenant Isolation**: All resources scoped by account/site/sector +6. **Request Tracking**: Every request has a unique ID for debugging + +### Base URL Structure + +``` +Production: https://api.igny8.com/api/v1/ +Development: http://localhost:8000/api/v1/ +``` + +### Module Namespaces + +``` +/api/v1/ +├── auth/ # Authentication and user management +├── planner/ # Keywords, clusters, content ideas +├── writer/ # Tasks, content, images +├── system/ # Settings, prompts, integrations +└── billing/ # Credits, transactions, usage +``` + +### Technology Stack + +- **Framework**: Django REST Framework (DRF) +- **Authentication**: JWT Bearer tokens (primary), Session (fallback), Basic (fallback) +- **Pagination**: CustomPageNumberPagination (default: 10, max: 100) +- **Rate Limiting**: Scoped throttles per module/operation type +- **OpenAPI**: drf-spectacular for schema generation + +### Implementation Status + +✅ **100% Complete** - All endpoints implemented with: +- Unified response format +- Proper authentication and authorization +- Rate limiting configured +- Error handling standardized +- Request ID tracking +- Complete Swagger/OpenAPI documentation + +--- + +## Authentication & Authorization + +### Authentication Methods + +#### Primary: JWT Bearer Token + +``` +Authorization: Bearer +``` + +**Token Characteristics:** +- Contains `user_id` and `account_id` +- Type: `access` (15-minute expiry) +- Automatically sets `request.account` via middleware +- Resolves account → tenant context automatically + +**Token Payload:** +```json +{ + "user_id": 1, + "account_id": 1, + "type": "access", + "exp": 1234567890 +} +``` + +#### Fallback Methods + +1. **Session Authentication** (admin panel) + - Class: `CSRFExemptSessionAuthentication` + - Use case: Django admin panel (`/admin/`) + +2. **Basic Authentication** (debug/testing) + - Class: `rest_framework.authentication.BasicAuthentication` + - Use case: API testing tools (Postman, curl) + +### Authentication Order + +1. JWT Token Authentication (tried first) +2. Session Authentication (fallback) +3. Basic Authentication (last fallback) +4. If all fail: 401 Unauthorized + +### 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", + "account": { ... } + }, + "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + }, + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Token Expiration + +- **Access Token**: 15 minutes +- **Refresh Token**: 7 days + +**Refresh Token:** +```http +POST /api/v1/auth/refresh/ +Content-Type: application/json + +{ + "refresh": "your_refresh_token" +} +``` + +### Public Endpoints (No Authentication Required) + +- `POST /api/v1/auth/register/` - User registration +- `POST /api/v1/auth/login/` - User login +- `GET /api/v1/auth/plans/` - List plans +- `GET /api/v1/auth/industries/` - List industries +- `GET /api/v1/system/status/` - System health check +- `GET /api/v1/system/ping/` - Health check endpoint + +**All other endpoints require JWT authentication.** + +### Authorization Layers + +Every endpoint enforces layered authorization: + +1. **User Authentication**: User must be authenticated +2. **Tenant Access**: User must belong to the tenant/account +3. **Role Authorization**: User must have appropriate role +4. **Site/Sector Access**: User must have access to requested site/sector + +--- + +## Response Format Standard + +### Mandatory Format + +**This is the global standard for all endpoints - no exceptions.** + +### Success Response + +```json +{ + "success": true, + "data": { + "id": 1, + "name": "Example Keyword", + "status": "active" + }, + "message": "Optional human-readable success message", + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Paginated Response + +```json +{ + "success": true, + "count": 120, + "next": "http://api.igny8.com/api/v1/planner/keywords/?page=3", + "previous": "http://api.igny8.com/api/v1/planner/keywords/?page=1", + "results": [ + {"id": 1, "name": "Keyword 1"}, + {"id": 2, "name": "Keyword 2"}, + ... + ], + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Error Response + +```json +{ + "success": false, + "error": "Readable top-level error message", + "errors": { + "field_name": ["Field-specific error messages"] + }, + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Response Helper Functions + +**File:** `backend/igny8_core/api/response.py` + +```python +from igny8_core.api.response import success_response, error_response, paginated_response + +# Success response +return success_response( + data={"id": 1, "name": "Example"}, + message="Resource created successfully", + status_code=status.HTTP_201_CREATED +) + +# Error response +return error_response( + error="Validation failed", + errors={"email": ["Invalid email format"]}, + status_code=status.HTTP_400_BAD_REQUEST +) + +# Paginated response +paginator = CustomPageNumberPagination() +page = paginator.paginate_queryset(queryset, request) +serializer = MySerializer(page, many=True) +paginated_data = paginator.get_paginated_response(serializer.data).data +return paginated_response(paginated_data, message="Resources retrieved successfully") +``` + +--- + +## 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 | + +### Centralized Exception Handler + +**File:** `backend/igny8_core/api/exception_handlers.py` + +All exceptions are handled by a centralized exception handler that: +- Wraps all errors in unified format +- Uses proper HTTP status codes +- Includes sanitized validation errors under `errors` +- Always attaches `request_id` for error tracking +- Logs full exception details +- In DEBUG mode: includes traceback + request context + +### Error Response Examples + +**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" +} +``` + +**Rate Limit (429):** +```json +{ + "success": false, + "error": "Rate limit exceeded", + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Server-side Logging + +- All 4xx errors logged as **warning** +- All 5xx errors logged as **error** +- Structured format with timestamp, request_id, endpoint, user_id, account_id, status_code, error_message +- Rotating log files +- Sentry integration hooks for production + +--- + +## Rate Limiting + +### Rate Limiting Configuration + +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 +Retry-After: 60 + +{ + "success": false, + "error": "Rate limit exceeded", + "request_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Debug Mode Bypass + +Set `IGNY8_DEBUG_THROTTLE=True` or `DEBUG=True` to bypass throttling in development. + +--- + +## Pagination + +### Pagination Configuration + +**Default Settings:** +- Default page size: **10** +- Maximum page size: **100** +- Query parameter: `page_size` (optional) +- Page parameter: `page` (default: 1) + +### Query Parameters + +**Pagination:** +``` +?page=2&page_size=25 +``` + +**Filtering:** +``` +?status=active +?site_id=1 +?sector_id=2 +?cluster_id=5 +``` + +**Search:** +``` +?search=keyword +``` + +**Ordering:** +``` +?ordering=-created_at +?ordering=name,status +``` + +### Pagination Response Format + +```json +{ + "success": true, + "count": 150, + "next": "http://api.igny8.com/api/v1/planner/keywords/?page=3&page_size=25", + "previous": "http://api.igny8.com/api/v1/planner/keywords/?page=1&page_size=25", + "results": [ + // Array of results + ], + "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 + +--- + +## Roles & Permissions + +### Role Hierarchy + +``` +owner > admin > editor > viewer > system_bot +``` + +### Standard Permission Classes + +**File:** `backend/igny8_core/api/permissions.py` + +| Permission Class | Description | Use Case | +|-----------------|------------|----------| +| `IsAuthenticatedAndActive` | User authenticated and active | Base permission for most endpoints | +| `HasTenantAccess` | User belongs to tenant/account | Tenant isolation | +| `IsViewerOrAbove` | Viewer, editor, admin, or owner | Read-only operations | +| `IsEditorOrAbove` | Editor, admin, or owner | Content operations | +| `IsAdminOrOwner` | Admin or owner only | Settings, keys, billing | + +### Permission Matrix by Endpoint Type + +| Endpoint Type | Required Permissions | Roles Allowed | +|--------------|---------------------|---------------| +| Public (register, login) | `AllowAny` | Anyone | +| Read-only (list, retrieve) | `IsAuthenticatedAndActive` + `HasTenantAccess` | All authenticated users | +| Content operations | `IsAuthenticatedAndActive` + `HasTenantAccess` + `IsEditorOrAbove` | Editor, Admin, Owner | +| User management | `IsAuthenticatedAndActive` + `HasTenantAccess` + `IsAdminOrOwner` | Admin, Owner | +| Billing/Transactions | `IsAuthenticatedAndActive` + `HasTenantAccess` + `IsAdminOrOwner` | Admin, Owner | +| Integration settings | `IsAuthenticatedAndActive` + `HasTenantAccess` + `IsAdminOrOwner` | Admin, Owner | + +--- + +## Tenant / Site / Sector Scoping + +### Scoping Rules + +Every resource created or fetched must be scoped by: + +1. **Account/Tenant** - User's account +2. **Site** - Specific site within account +3. **Sector** - Specific sector within site + +### Enforcement + +**Base Classes:** +- `AccountModelViewSet` - Handles account isolation +- `SiteSectorModelViewSet` - Filters queries by site/sector + +**Requirements:** +- All custom actions must use `.get_queryset()` to avoid bypassing filters +- Any ID list must be verified to belong to the authenticated tenant +- Site/sector access validated based on user role + +### Scoping Example + +```python +class KeywordViewSet(SiteSectorModelViewSet): + # Automatically filters by: + # 1. account (from request.account) + # 2. site_id (from query params or request) + # 3. sector_id (from query params or request) + + queryset = Keyword.objects.all() + serializer_class = KeywordSerializer + + def get_queryset(self): + # Base class handles account/site/sector filtering + queryset = super().get_queryset() + # Additional filtering can be added here + return queryset +``` + +--- + +## Complete Endpoint Reference + +### Authentication Endpoints + +**Base Path**: `/api/v1/auth/` + +#### POST `/api/v1/auth/register/` +**Purpose**: User registration +**Authentication**: None (AllowAny) + +**Request:** +```json +{ + "email": "user@example.com", + "password": "password123", + "password_confirm": "password123" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "user": { + "id": 1, + "email": "user@example.com", + "role": "owner", + "account": { ... } + } + }, + "message": "Registration successful" +} +``` + +#### POST `/api/v1/auth/login/` +**Purpose**: User login +**Authentication**: None (AllowAny) + +**Request:** +```json +{ + "email": "user@example.com", + "password": "password123" +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "user": { ... }, + "access": "eyJ0eXAiOiJKV1QiLCJhbGc...", + "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..." + }, + "message": "Login successful" +} +``` + +#### POST `/api/v1/auth/refresh/` +**Purpose**: Refresh access token +**Authentication**: None (requires refresh token) + +**Request:** +```json +{ + "refresh": "your_refresh_token" +} +``` + +#### POST `/api/v1/auth/change-password/` +**Purpose**: Change user password +**Authentication**: Required (IsAuthenticated) + +**Request:** +```json +{ + "old_password": "oldpass123", + "new_password": "newpass123", + "new_password_confirm": "newpass123" +} +``` + +#### GET `/api/v1/auth/me/` +**Purpose**: Get current user information +**Authentication**: Required (IsAuthenticated) + +**Response:** +```json +{ + "success": true, + "data": { + "user": { + "id": 1, + "email": "user@example.com", + "role": "owner", + "account": { ... }, + "accessible_sites": [ ... ] + } + } +} +``` + +### User Management Endpoints + +**Base Path**: `/api/v1/auth/users/` +**Permission**: IsOwnerOrAdmin + +**Standard CRUD:** +- `GET /api/v1/auth/users/` - List users +- `POST /api/v1/auth/users/` - Create user +- `GET /api/v1/auth/users/{id}/` - Get user +- `PUT /api/v1/auth/users/{id}/` - Update user +- `DELETE /api/v1/auth/users/{id}/` - Delete user + +**Custom Actions:** +- `POST /api/v1/auth/users/invite/` - Invite user +- `POST /api/v1/auth/users/{id}/activate/` - Activate user + +### Account Management Endpoints + +**Base Path**: `/api/v1/auth/accounts/` +**Permission**: IsOwnerOrAdmin + +**Standard CRUD:** +- `GET /api/v1/auth/accounts/` - List accounts +- `POST /api/v1/auth/accounts/` - Create account +- `GET /api/v1/auth/accounts/{id}/` - Get account +- `PUT /api/v1/auth/accounts/{id}/` - Update account +- `DELETE /api/v1/auth/accounts/{id}/` - Delete account + +### Site Management Endpoints + +**Base Path**: `/api/v1/auth/sites/` +**Permission**: IsEditorOrAbove + +**Standard CRUD:** +- `GET /api/v1/auth/sites/` - List sites +- `POST /api/v1/auth/sites/` - Create site +- `GET /api/v1/auth/sites/{id}/` - Get site +- `PUT /api/v1/auth/sites/{id}/` - Update site +- `DELETE /api/v1/auth/sites/{id}/` - Delete site + +**Custom Actions:** +- `GET /api/v1/auth/sites/{id}/sectors/` - Get site sectors +- `POST /api/v1/auth/sites/{id}/set_active/` - Set active site +- `POST /api/v1/auth/sites/{id}/select_sectors/` - Select sectors + +### Sector Management Endpoints + +**Base Path**: `/api/v1/auth/sectors/` +**Permission**: IsEditorOrAbove + +**Standard CRUD:** +- `GET /api/v1/auth/sectors/` - List sectors +- `POST /api/v1/auth/sectors/` - Create sector +- `GET /api/v1/auth/sectors/{id}/` - Get sector +- `PUT /api/v1/auth/sectors/{id}/` - Update sector +- `DELETE /api/v1/auth/sectors/{id}/` - Delete sector + +### Planner Module Endpoints + +**Base Path**: `/api/v1/planner/` + +#### Keyword Management + +**Base Path**: `/api/v1/planner/keywords/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/planner/keywords/` - List keywords (paginated) +- `POST /api/v1/planner/keywords/` - Create keyword +- `GET /api/v1/planner/keywords/{id}/` - Get keyword +- `PUT /api/v1/planner/keywords/{id}/` - Update keyword +- `DELETE /api/v1/planner/keywords/{id}/` - Delete keyword + +**Filtering:** +- `status` - Filter by status +- `cluster_id` - Filter by cluster +- `seed_keyword__intent` - Filter by intent +- `seed_keyword_id` - Filter by seed keyword ID +- `difficulty_min`, `difficulty_max` - Difficulty range +- `volume_min`, `volume_max` - Volume range +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Search:** +- `search` - Search by keyword text + +**Ordering:** +- `ordering` - Order by `created_at`, `seed_keyword__volume`, `seed_keyword__difficulty` +- Default: `-created_at` (newest first) + +**Custom Actions:** +- `POST /api/v1/planner/keywords/bulk_delete/` - Bulk delete keywords + - Request: `{ "ids": [1, 2, 3] }` +- `POST /api/v1/planner/keywords/bulk_update_status/` - Bulk update status + - Request: `{ "ids": [1, 2, 3], "status": "active" }` +- `POST /api/v1/planner/keywords/bulk_add_from_seed/` - Add keywords from seed library + - Request: `{ "seed_keyword_ids": [1, 2, 3], "site_id": 1, "sector_id": 1 }` +- `GET /api/v1/planner/keywords/export/` - Export keywords to CSV +- `POST /api/v1/planner/keywords/import_keywords/` - Import keywords from CSV +- `POST /api/v1/planner/keywords/auto_cluster/` - Auto-cluster keywords using AI + - Request: `{ "ids": [1, 2, 3, ...], "sector_id": 1 }` + - Max Keywords: 20 per batch + - Returns: Celery task ID for progress tracking + +#### Cluster Management + +**Base Path**: `/api/v1/planner/clusters/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/planner/clusters/` - List clusters +- `POST /api/v1/planner/clusters/` - Create cluster +- `GET /api/v1/planner/clusters/{id}/` - Get cluster +- `PUT /api/v1/planner/clusters/{id}/` - Update cluster +- `DELETE /api/v1/planner/clusters/{id}/` - Delete cluster + +**Filtering:** +- `status` - Filter by status +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Search:** +- `search` - Search by cluster name + +**Custom Actions:** +- `POST /api/v1/planner/clusters/bulk_delete/` - Bulk delete clusters +- `POST /api/v1/planner/clusters/auto_generate_ideas/` - Auto-generate content ideas + - Request: `{ "ids": [1] }` (max 1 cluster per batch) + - Returns: Celery task ID for progress tracking + +#### Content Ideas Management + +**Base Path**: `/api/v1/planner/ideas/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/planner/ideas/` - List content ideas +- `POST /api/v1/planner/ideas/` - Create content idea +- `GET /api/v1/planner/ideas/{id}/` - Get content idea +- `PUT /api/v1/planner/ideas/{id}/` - Update content idea +- `DELETE /api/v1/planner/ideas/{id}/` - Delete content idea + +**Filtering:** +- `status` - Filter by status +- `cluster_id` - Filter by cluster +- `content_type` - Filter by content type +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Custom Actions:** +- `POST /api/v1/planner/ideas/bulk_delete/` - Bulk delete ideas +- `POST /api/v1/planner/ideas/bulk_queue_to_writer/` - Queue ideas to writer (create tasks) + - Request: `{ "ids": [1, 2, 3] }` + +### Writer Module Endpoints + +**Base Path**: `/api/v1/writer/` + +#### Task Management + +**Base Path**: `/api/v1/writer/tasks/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/writer/tasks/` - List tasks +- `POST /api/v1/writer/tasks/` - Create task +- `GET /api/v1/writer/tasks/{id}/` - Get task +- `PUT /api/v1/writer/tasks/{id}/` - Update task +- `DELETE /api/v1/writer/tasks/{id}/` - Delete task + +**Filtering:** +- `status` - Filter by status (draft, in_progress, review, completed, archived) +- `cluster_id` - Filter by cluster +- `content_type` - Filter by content type +- `content_structure` - Filter by content structure +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Search:** +- `search` - Search by title or keywords + +**Custom Actions:** +- `POST /api/v1/writer/tasks/bulk_delete/` - Bulk delete tasks +- `POST /api/v1/writer/tasks/bulk_update/` - Bulk update task status +- `POST /api/v1/writer/tasks/auto_generate_content/` - Auto-generate content using AI + - Request: `{ "ids": [1, 2, 3, ...] }` (max 50 tasks per batch) + - Returns: Celery task ID for progress tracking + +#### Content Management + +**Base Path**: `/api/v1/writer/content/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/writer/content/` - List content +- `POST /api/v1/writer/content/` - Create content +- `GET /api/v1/writer/content/{id}/` - Get content +- `PUT /api/v1/writer/content/{id}/` - Update content +- `DELETE /api/v1/writer/content/{id}/` - Delete content + +**Filtering:** +- `status` - Filter by status +- `content_type` - Filter by content type +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Custom Actions:** +- `POST /api/v1/writer/content/generate_image_prompts/` - Generate image prompts from content + - Request: `{ "ids": [1, 2, 3] }` + - Returns: Celery task ID for progress tracking + +#### Image Management + +**Base Path**: `/api/v1/writer/images/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: SiteSectorModelViewSet + +**Standard CRUD:** +- `GET /api/v1/writer/images/` - List images +- `POST /api/v1/writer/images/` - Create image +- `GET /api/v1/writer/images/{id}/` - Get image +- `PUT /api/v1/writer/images/{id}/` - Update image +- `DELETE /api/v1/writer/images/{id}/` - Delete image + +**Filtering:** +- `image_type` - Filter by type (featured, in_article, desktop, mobile) +- `status` - Filter by status +- `content_id` - Filter by content +- `task_id` - Filter by task +- `site_id` - Filter by site (query param) +- `sector_id` - Filter by sector (query param) + +**Custom Actions:** +- `GET /api/v1/writer/images/{id}/file/` - Get image file URL +- `GET /api/v1/writer/images/content_images/` - Get images for content + - Query Params: `content_id` (required) +- `POST /api/v1/writer/images/generate_images/` - Generate images using AI + - Request: `{ "ids": [1, 2, 3, ...] }` + - Returns: Celery task ID for progress tracking +- `POST /api/v1/writer/images/bulk_update/` - Bulk update image status + +### System Module Endpoints + +**Base Path**: `/api/v1/system/` + +#### AI Prompt Management + +**Base Path**: `/api/v1/system/prompts/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess +**Inherits**: AccountModelViewSet + +**Standard CRUD:** +- `GET /api/v1/system/prompts/` - List prompts +- `POST /api/v1/system/prompts/` - Create prompt +- `GET /api/v1/system/prompts/{id}/` - Get prompt +- `PUT /api/v1/system/prompts/{id}/` - Update prompt +- `DELETE /api/v1/system/prompts/{id}/` - Delete prompt + +**Custom Actions:** +- `GET /api/v1/system/prompts/by_type/{prompt_type}/` - Get prompt by type +- `POST /api/v1/system/prompts/save/` - Save prompt (requires editor/admin) +- `POST /api/v1/system/prompts/reset/` - Reset prompt to default + +#### Integration Settings + +**Base Path**: `/api/v1/system/settings/integrations/` +**Permission**: IsAdminOrOwner + +**Custom URL Patterns:** +- `GET /api/v1/system/settings/integrations/{pk}/` - Get integration settings +- `POST /api/v1/system/settings/integrations/{pk}/save/` - Save integration settings +- `PUT /api/v1/system/settings/integrations/{pk}/` - Update integration settings +- `POST /api/v1/system/settings/integrations/{pk}/test/` - Test connection + - Request: `{ "provider": "openai" }` or `{ "provider": "runware" }` +- `POST /api/v1/system/settings/integrations/{pk}/generate/` - Test image generation +- `GET /api/v1/system/settings/task_progress/{task_id}/` - Get Celery task progress +- `GET /api/v1/system/integrations/image_generation/` - Get image generation settings + +#### System Status + +**Base Path**: `/api/v1/system/` + +- `GET /api/v1/system/status/` - System health check (AllowAny) +- `GET /api/v1/system/ping/` - Health check endpoint (AllowAny) +- `GET /api/v1/system/request-metrics/{request_id}/` - Get request metrics for debugging + +### Billing Module Endpoints + +**Base Path**: `/api/v1/billing/` + +#### Credit Balance + +**Base Path**: `/api/v1/billing/credits/balance/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess + +- `GET /api/v1/billing/credits/balance/balance/` - Get credit balance + +**Response:** +```json +{ + "success": true, + "data": { + "credits": 1000, + "plan_credits_per_month": 500, + "credits_used_this_month": 250, + "credits_remaining": 750 + } +} +``` + +#### Credit Usage + +**Base Path**: `/api/v1/billing/credits/usage/` +**Permission**: IsAuthenticatedAndActive + HasTenantAccess + +**Standard CRUD:** +- `GET /api/v1/billing/credits/usage/` - List usage logs (paginated) +- `GET /api/v1/billing/credits/usage/{id}/` - Get usage log + +**Filtering:** +- `operation_type` - Filter by operation type +- `start_date` - Filter by start date (YYYY-MM-DD) +- `end_date` - Filter by end date (YYYY-MM-DD) + +**Custom Actions:** +- `GET /api/v1/billing/credits/usage/summary/` - Get usage summary +- `GET /api/v1/billing/credits/usage/limits/` - Get usage limits + +#### Credit Transactions + +**Base Path**: `/api/v1/billing/credits/transactions/` +**Permission**: IsAdminOrOwner + +**Standard CRUD:** +- `GET /api/v1/billing/credits/transactions/` - List transactions (paginated) +- `GET /api/v1/billing/credits/transactions/{id}/` - Get transaction + +**Filtering:** +- `transaction_type` - Filter by type +- `start_date` - Filter by start date +- `end_date` - Filter by end date + +--- + +## Integration 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/?page=1&page_size=25", + headers=headers + ) + result = response.json() + + if result['success']: + keywords = result['results'] + print(f"Found {result['count']} keywords") + else: + print(f"Error: {result['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/?page=1&page_size=25`, + { 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); + } +} +``` + +### 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" +``` + +### Frontend Integration (TypeScript) + +```typescript +// Using fetchAPI wrapper (automatically extracts data from unified format) +import { fetchAPI } from './services/api'; + +// List keywords +const response = await fetchAPI('/v1/planner/keywords/?page=1&page_size=25'); +// response is already extracted: { count, next, previous, results } + +// Create keyword +const newKeyword = await fetchAPI('/v1/planner/keywords/', { + method: 'POST', + body: JSON.stringify({ + keyword: 'example keyword', + site_id: 1, + sector_id: 2, + status: 'active' + }) +}); + +// Error handling +try { + const data = await fetchAPI('/v1/planner/keywords/'); +} catch (error) { + // Error is already parsed from unified format + console.error(error.message); + if (error.errors) { + // Handle field-specific errors + Object.keys(error.errors).forEach(field => { + console.error(`${field}: ${error.errors[field].join(', ')}`); + }); + } +} +``` + +### WordPress Plugin Integration (PHP) + +```php +class Igny8API { + private $base_url = 'https://api.igny8.com/api/v1'; + private $token; + + public function login($email, $password) { + $response = wp_remote_post($this->base_url . '/auth/login/', [ + 'headers' => ['Content-Type' => 'application/json'], + 'body' => json_encode(['email' => $email, 'password' => $password]) + ]); + + $data = json_decode(wp_remote_retrieve_body($response), true); + + if ($data['success']) { + $this->token = $data['data']['access']; + return true; + } + + return false; + } + + public function getKeywords($page = 1, $page_size = 25) { + $response = wp_remote_get( + $this->base_url . '/planner/keywords/?page=' . $page . '&page_size=' . $page_size, + [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->token, + 'Content-Type' => 'application/json' + ] + ] + ); + + $data = json_decode(wp_remote_retrieve_body($response), true); + + if ($data['success']) { + return $data['results']; + } + + return []; + } +} +``` + +--- + +## Testing & Debugging + +### Request ID Tracking + +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`) + +### Progress Tracking (AI Functions) + +All AI functions return a Celery task ID for progress tracking: + +**Request:** +```json +POST /api/v1/planner/keywords/auto_cluster/ +{ + "ids": [1, 2, 3, 4, 5], + "sector_id": 1 +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "task_id": "abc123-def456-ghi789" + } +} +``` + +**Poll Progress:** +```json +GET /api/v1/system/settings/task_progress/abc123-def456-ghi789/ +``` + +**Progress Response:** +```json +{ + "state": "PROGRESS", + "meta": { + "phase": "AI_CALL", + "percentage": 50, + "message": "Processing keywords...", + "request_steps": [...], + "response_steps": [...], + "cost": 0.05, + "tokens": 1500 + } +} +``` + +### Error Handling Best Practices + +**401 Unauthorized**: Trigger logout → redirect to login +**403 Forbidden**: Show permission alert +**429 Too Many Requests**: Show rate limit warning with retry time +**4xx/5xx**: Display error message from `error` field + +### Rate Limit Monitoring + +```typescript +const throttleLimit = response.headers.get('X-Throttle-Limit'); +const throttleRemaining = response.headers.get('X-Throttle-Remaining'); + +if (parseInt(throttleRemaining) < 5) { + showNotification('Approaching rate limit', 'warning'); +} +``` + +--- + +## Change Management + +### Versioning Strategy + +- **v1** remains stable long-term +- Breaking changes require **v2** +- Deprecations allowed only with explicit timeline +- Non-breaking changes can be added to v1 + +### Breaking Changes + +**Definition:** Changes that require client code updates + +**Examples:** +- Removing an endpoint +- Changing response structure +- Changing authentication method +- Removing a field from response + +**Process:** +1. Document breaking change +2. Provide migration guide +3. Deprecate in v1 with timeline +4. Implement in v2 +5. Maintain v1 for deprecation period + +### Non-Breaking Changes + +**Definition:** Changes that don't require client code updates + +**Examples:** +- Adding new endpoints +- Adding optional fields to response +- Adding new query parameters +- Performance improvements + +### Changelog + +All API changes are documented in `CHANGELOG.md` with: +- Version number +- Date +- Type (Added, Changed, Fixed, Deprecated, Removed, Security) +- Description +- Affected areas +- Migration notes (if applicable) + +--- + +## Summary + +### Implementation Status + +✅ **100% Complete** - All endpoints implemented with: +- Unified response format (`{success, data, message, errors, request_id}`) +- Proper authentication and authorization (JWT Bearer tokens) +- Rate limiting configured (scoped by operation type) +- Error handling standardized (centralized exception handler) +- Request ID tracking (every request has unique ID) +- Complete Swagger/OpenAPI documentation +- Tenant/site/sector scoping (automatic filtering) +- Pagination standardized (default: 10, max: 100) + +### Key Features + +- **100+ endpoints** across 5 modules +- **Unified response format** for all endpoints +- **Scoped rate limiting** (10-100 requests/minute depending on operation) +- **Layered authorization** (Authentication → Tenant → Role → Site/Sector) +- **Complete OpenAPI documentation** (Swagger UI, ReDoc) +- **Request ID tracking** for debugging +- **Progress tracking** for AI functions +- **Comprehensive error handling** with clear messages + +### Access Points + +- **Interactive Documentation**: `https://api.igny8.com/api/docs/` +- **ReDoc**: `https://api.igny8.com/api/redoc/` +- **OpenAPI Schema**: `https://api.igny8.com/api/schema/` + +--- + +**Last Updated**: 2025-01-XX +**API Version**: 1.0.0 +**Status**: ✅ **100% IMPLEMENTED** +