# 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 ├── account/ # Account settings, team, and usage analytics ├── integration/ # Site integrations and sync ├── system/ # Settings, prompts, integrations ├── admin/billing/ # Admin-only billing management ├── billing/ # Credits, transactions, usage ├── planner/ # Keywords, clusters, content ideas ├── writer/ # Tasks, content, images ├── automation/ # Automation configuration and runs ├── linker/ # Internal linking operations ├── optimizer/ # Content optimization operations └── publisher/ # Publishing records and deployments ``` ### Module → Tag Map (Swagger/ReDoc) - Authentication → `Authentication` - Account → `Account` - Integration → `Integration` - System → `System` - Admin Billing → `Admin Billing` - Billing → `Billing` - Planner → `Planner` - Writer → `Writer` - Automation → `Automation` - Linker → `Linker` - Optimizer → `Optimizer` - Publisher → `Publisher` Tag display order (docs): Authentication, Account, Integration, System, Admin Billing, Billing, Planner, Writer, Automation, Linker, Optimizer, Publisher ### 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 ### Admin Billing & Credits (Admin-only, Unified) **Base Path**: `/api/v1/admin/billing/` (all admin billing/credits live here) - `GET /api/v1/admin/billing/stats/` - System billing stats (admin-only) - `GET /api/v1/admin/billing/invoices/` - Admin invoice listing (all accounts) - `GET /api/v1/admin/billing/payments/` - Admin payment listing (all accounts) - `GET /api/v1/admin/billing/pending_payments/` - Pending manual payments (admin review queue) - `POST /api/v1/admin/billing/{id}/approve_payment/` - Approve manual payment (admin-only) - `POST /api/v1/admin/billing/{id}/reject_payment/` - Reject manual payment (admin-only) - `GET /api/v1/admin/billing/payment-method-configs/` - List payment method configs (country-level); query: `country_code`, `payment_method` - `POST /api/v1/admin/billing/payment-method-configs/` - Create payment method config - `GET /api/v1/admin/billing/payment-method-configs/{id}/` - Retrieve payment method config - `PATCH/PUT /api/v1/admin/billing/payment-method-configs/{id}/` - Update payment method config - `DELETE /api/v1/admin/billing/payment-method-configs/{id}/` - Delete payment method config - `GET /api/v1/admin/billing/account-payment-methods/` - List account payment methods (query `account_id` to scope) - `POST /api/v1/admin/billing/account-payment-methods/` - Create account payment method - `GET /api/v1/admin/billing/account-payment-methods/{id}/` - Retrieve account payment method - `PATCH/PUT /api/v1/admin/billing/account-payment-methods/{id}/` - Update account payment method - `DELETE /api/v1/admin/billing/account-payment-methods/{id}/` - Delete account payment method - `POST /api/v1/admin/billing/account-payment-methods/{id}/set_default/` - Set default account payment method - `GET /api/v1/admin/credit-costs/` - List credit cost configurations (admin-only) - `POST /api/v1/admin/credit-costs/` - Update credit cost configurations (admin-only) - `GET /api/v1/admin/users/` - List users/accounts with credit info (admin-only) - `POST /api/v1/admin/users/{user_id}/adjust-credits/` - Adjust user credits (admin-only) > Non-standard/legacy endpoints to deprecate and remove (do not use): > - `/api/v1/billing/admin/stats/` > - `/api/v1/billing/admin/invoices/` > - `/api/v1/billing/admin/payments/` > - `/api/v1/billing/admin/pending_payments/` > - `/api/v1/billing/admin/{id}/approve_payment/` > - `/api/v1/billing/admin/{id}/reject_payment/` ### 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/` - 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 #### Credit Packages **Base Path**: `/api/v1/billing/credit-packages/` **Permission**: IsAuthenticated - `GET /api/v1/billing/credit-packages/` - List available credit packages - `POST /api/v1/billing/credit-packages/{id}/purchase/` - Purchase a credit package #### Invoices **Base Path**: `/api/v1/billing/invoices/` **Permission**: IsAuthenticated - `GET /api/v1/billing/invoices/` - List invoices - `GET /api/v1/billing/invoices/{id}/` - Get invoice detail - `GET /api/v1/billing/invoices/{id}/download_pdf/` - Download invoice PDF #### Payment Methods **Base Path**: `/api/v1/billing/payment-methods/` **Permission**: IsAuthenticated - `GET /api/v1/billing/payment-methods/` - List payment methods - `POST /api/v1/billing/payment-methods/` - Create payment method - `GET /api/v1/billing/payment-methods/{id}/` - Get payment method - `PUT /api/v1/billing/payment-methods/{id}/` - Update payment method - `PATCH /api/v1/billing/payment-methods/{id}/` - Partial update payment method - `DELETE /api/v1/billing/payment-methods/{id}/` - Delete payment method - `POST /api/v1/billing/payment-methods/{id}/set_default/` - Set default payment method - `GET /api/v1/billing/payment-methods/available/` - List available payment methods (config-driven) #### Payments **Base Path**: `/api/v1/billing/payments/` **Permission**: IsAuthenticated - `GET /api/v1/billing/payments/` - List payments - `POST /api/v1/billing/payments/manual/` - Submit manual payment for approval #### Transactions (alias-free) **Base Path**: `/api/v1/billing/transactions/` **Permission**: IsAuthenticated - `GET /api/v1/billing/transactions/` - List transactions (with current balance included) --- ## 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) ### Endpoint Change & Documentation Checklist (Unified API) 1) Design - Map to an existing module/tag; if new, add to Module Namespaces and Tag Map. - Choose path under the correct base (`/api/v1/{module}/...`); avoid new sub-namespaces unless justified. 2) Implement - Use unified response helpers and proper permissions/rate limits. - Add `extend_schema` tags matching the module tag. 3) Schema & Docs - Ensure swagger tag exists in `SPECTACULAR_SETTINGS` with the agreed order. - Regenerate/reload the API (server restart) so `/api/schema/` reflects changes. - Verify in Swagger UI (`/api/docs/`) and ReDoc (`/api/redoc/`) that the operation is under the right tag. 4) Reference Updates - Update this reference file with the new endpoint(s) under the correct module section. - Update `CHANGELOG.md` (type, summary, impacted clients). 5) Deprecation (if applicable) - Mark legacy routes, add timeline, and keep compatibility shims only temporarily. --- ## 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**