# 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 <access_token>
```

**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**

