# SuiteX API Architecture

## Table of Contents

1. [Overview & Purpose](#overview--purpose)
   - [Goals](#goals)
   - [Audience](#audience)
2. [Current State Analysis](#current-state-analysis)
   - [Existing API Endpoints](#existing-api-endpoints)
   - [Current Authentication](#current-authentication)
   - [Existing Web Controllers](#existing-web-controllers)
3. [API Architecture](#api-architecture)
   - [Core Principles](#core-principles)
   - [Directory Structure](#directory-structure)
   - [Controller Organization Decision](#controller-organization-decision)
   - [Shared Code Strategy](#shared-code-strategy)
4. [Versioning Strategy](#versioning-strategy)
   - [Version Format](#version-format)
   - [When to Version](#when-to-version)
   - [Version Implementation](#version-implementation)
   - [Version Lifecycle](#version-lifecycle)
5. [Base API Controller](#base-api-controller)
   - [Location](#location)
   - [Core Responsibilities](#core-responsibilities)
   - [Implementation Specification](#implementation-specification)
   - [Usage Example](#usage-example)
6. [RESTful Resource Endpoints](#restful-resource-endpoints)
   - [Standard REST Operations](#standard-rest-operations)
   - [Laravel API Resource Routes](#laravel-api-resource-routes)
   - [Resource Controller Template](#resource-controller-template)
   - [Nested Resources](#nested-resources)
7. [Use-Case Specific Endpoints](#use-case-specific-endpoints)
   - [Examples from Current System](#examples-from-current-system)
   - [When to Use Use-Case Endpoints](#when-to-use-use-case-endpoints)
   - [Naming Conventions](#naming-conventions)
8. [Request/Response Standards](#requestresponse-standards)
   - [Request Format](#request-format)
   - [Response Format](#response-format)
   - [HTTP Status Codes](#http-status-codes)
   - [Headers](#headers)
9. [Authentication & Authorization](#authentication--authorization)
   - [Current State: Session-Based Authentication](#current-state-session-based-authentication)
   - [Authorization Pattern](#authorization-pattern)
   - [Future: Token-Based Authentication (Roadmap)](#future-token-based-authentication-roadmap)
10. [Error Handling](#error-handling)
    - [Error Response Standards](#error-response-standards)
    - [Exception Handling Pattern](#exception-handling-pattern)
    - [Logging Strategy](#logging-strategy)
    - [Error Messages](#error-messages)
11. [Decision Trees](#decision-trees)
    - [Decision Tree 1: Should I Create an API Endpoint?](#decision-tree-1-should-i-create-an-api-endpoint)
    - [Decision Tree 2: Which API Version Should I Use?](#decision-tree-2-which-api-version-should-i-use)
    - [Decision Tree 3: REST Resource vs Use-Case Endpoint?](#decision-tree-3-rest-resource-vs-use-case-endpoint)
    - [Decision Tree 4: Where Should This Controller Live?](#decision-tree-4-where-should-this-controller-live)
    - [Decision Tree 5: What Middleware Should I Apply?](#decision-tree-5-what-middleware-should-i-apply)
    - [Decision Tree 6: How Should I Handle Pagination?](#decision-tree-6-how-should-i-handle-pagination)
12. [Roadmap](#roadmap)
    - [Phase 1: Foundation (Current - 3 Months)](#phase-1-foundation-current---3-months)
    - [Phase 2: Enhancement (3-6 Months)](#phase-2-enhancement-3-6-months)
    - [Phase 3: Documentation & Testing (6-9 Months)](#phase-3-documentation--testing-6-9-months)
    - [Phase 4: Token Authentication (9-12 Months)](#phase-4-token-authentication-9-12-months)
    - [Phase 5: External Access (12-18 Months)](#phase-5-external-access-12-18-months)
    - [Phase 6: Advanced Features (18+ Months)](#phase-6-advanced-features-18-months)
13. [Appendix: Quick Reference](#appendix-quick-reference)
    - [Common Patterns Cheat Sheet](#common-patterns-cheat-sheet)
    - [HTTP Status Code Reference](#http-status-code-reference)
    - [Route Examples](#route-examples)
14. [Document Maintenance](#document-maintenance)

---

## Overview & Purpose

This document defines the architectural guidelines and standards for SuiteX's API layer. It serves as the authoritative reference for:

- Frontend developers consuming API endpoints
- Backend developers implementing new API functionality
- System architects making structural decisions
- Future maintainers understanding design rationale

### Goals

1. **Consistency**: Establish repeatable patterns across all API endpoints
2. **Scalability**: Support growth from internal SPA to external third-party access
3. **Maintainability**: Clean separation of concerns between web UI and API layers
4. **Developer Experience**: Predictable, well-documented interfaces
5. **Performance**: Efficient data transfer and query optimization

### Audience

- **Primary**: Same-domain SPA (React/Vue) making authenticated requests
- **Future**: Mobile applications, third-party integrations, external developers

---

## Current State Analysis

### Existing API Endpoints

Currently, SuiteX has several API implementations:

#### v1 Enhanced Import API (`/api/v1/import/*`)
- **Location**: `src/App/Http/Controllers/Api/v1/EnhancedImportController.php`
- **Purpose**: Comprehensive import status and dependency preview for React UI
- **Middleware**: `web`, `auth`, `throttle:200,1`
- **Patterns**: Standardized response methods, comprehensive error handling, input validation

#### v1 Flow Metrics API (`/api/v1/ipaas/metrics/*`)
- **Location**: `src/App/Http/Controllers/Api/v1/FlowMetricsController.php`
- **Purpose**: iPaaS flow execution history and metrics
- **Middleware**: `web`, `auth`
- **Patterns**: Pagination, success/error responses, detailed logging

#### v2 Search Fields API (`/api/v2/search-fields/*`)
- **Location**: `src/App/Http/Controllers/SearchFieldConfigController.php`
- **Purpose**: Tenant-scoped search field configuration
- **Middleware**: `web`, `auth`, `throttle:100,1`
- **Patterns**: Direct JSON responses

#### Legacy Search API (`/api/search/*`)
- **Location**: `src/App/Http/Controllers/SearchController.php`
- **Purpose**: Search functionality
- **Middleware**: `web`, `auth`
- **Patterns**: Mixed implementation

#### Unversioned RecordRef API (`/api/recordref/*`)
- **Location**: `src/App/Http/Controllers/Api/RecordRefController.php`
- **Purpose**: Record reference lookups with pagination
- **Middleware**: `web`, `auth`
- **Patterns**: Basic pagination, direct JSON responses

### Current Authentication

All existing API routes use **session-based authentication** via Laravel's `web` and `auth` middleware:
- Suitable for same-domain SPA applications
- Shares session state with main application
- CSRF protection enabled
- Cookie-based session management

### Existing Web Controllers

SuiteX has extensive web controllers for domain models:
- **Base**: `RecordController` - Handles standard CRUD operations with view rendering
- **Domain-Specific**: `ProjectController`, `CustomerController`, `VendorController`, etc.
- **Pattern**: Extend `RecordController`, return Blade views
- **Location**: `src/App/Http/Controllers/`

**Key Characteristics**:
- Tightly coupled to view rendering
- Use Actions pattern (CreateProject, UpdateProject, DeleteProject)
- Permission checking via `hasPermissionForRecordType()`
- Return Blade views or redirect responses
- Handle both AJAX and form submissions

---

## API Architecture

### Core Principles

1. **Separation of Concerns**: API controllers are separate from web controllers
2. **Version-First**: All API routes must specify a version
3. **JSON-First**: APIs return JSON, never HTML/views
4. **Stateless Design**: APIs should not rely on session state (prepare for token auth)
5. **Resource-Oriented**: Follow REST principles where applicable
6. **Use-Case Support**: Allow specialized endpoints when REST is insufficient

### Directory Structure

```
src/App/Http/Controllers/
├── Api/
│   ├── BaseApiController.php           # Shared functionality
│   ├── v1/                             # Version 1 endpoints
│   │   ├── EnhancedImportController.php
│   │   ├── FlowMetricsController.php
│   │   ├── ProjectController.php       # RESTful resource
│   │   ├── CustomerController.php      # RESTful resource
│   │   ├── ProjectTaskController.php   # RESTful resource
│   │   └── ...
│   ├── v2/                             # Version 2 endpoints (future)
│   └── RecordRefController.php         # Shared utility (no version)
├── ProjectController.php               # Web controller
├── CustomerController.php              # Web controller
└── ...

routes/
├── api.php                             # API route definitions
└── web.php                             # Web route definitions
```

### Controller Organization Decision

**✅ Use Separate API Controllers (Not Existing Web Controllers)**

#### Rationale

| Concern | Web Controllers | API Controllers |
|---------|-----------------|-----------------|
| **Response Format** | HTML views, redirects | JSON only |
| **State Management** | Session-based, stateful | Stateless, token-ready |
| **Middleware** | `web`, CSRF, session | `api`, rate limiting, optional CSRF |
| **Versioning** | Not versioned | Explicit versioning |
| **Error Handling** | Flash messages, redirects | Structured JSON errors |
| **Testing** | Browser/feature tests | API/integration tests |
| **Evolution** | UI-driven changes | API contract stability |

#### Benefits of Separation

1. **Independent Evolution**: Web UI and API can change independently
2. **Clean Contracts**: API defines clear, versioned contracts
3. **Better Testing**: API tests don't require view rendering
4. **Simplified Logic**: Each controller focuses on one concern
5. **Future-Ready**: Easy to add token authentication without affecting web layer
6. **Code Clarity**: Reduced conditional logic (`if ($request->expectsJson())`)

#### Implementation Pattern

**DO**: Create dedicated API controllers

```php
// src/App/Http/Controllers/Api/v1/ProjectController.php
namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Api\BaseApiController;

class ProjectController extends BaseApiController
{
    // API-specific implementation
}
```

**DON'T**: Reuse web controllers with conditional logic

```php
// ❌ Avoid this pattern
class ProjectController extends RecordController
{
    public function index(Request $request)
    {
        if ($request->expectsJson()) {
            return response()->json($data); // API logic
        }
        return view('projects.index', $data); // Web logic
    }
}
```

### Shared Code Strategy

While controllers are separate, **business logic should be shared**:

```php
// API Controller
namespace App\Http\Controllers\Api\v1;

class ProjectController extends BaseApiController
{
    protected $createAction;

    public function __construct(CreateProject $createAction)
    {
        $this->createAction = $createAction;
    }

    public function store(Request $request)
    {
        $validated = $this->validate($request, [
            'title' => 'required|string',
            // ...
        ]);

        $result = $this->createAction->create($validated);

        return $this->successResponse($result['record'], 201);
    }
}

// Web Controller (unchanged)
namespace App\Http\Controllers;

class ProjectController extends RecordController
{
    // Uses same CreateProject action
}
```

**Benefits**:
- Business logic (Actions) is reused
- Validation can be shared via FormRequests
- Models, Services, Repositories are shared
- Only presentation layer differs

---

## Versioning Strategy

### Version Format

- **Format**: `/api/v{major}/...`
- **Examples**: `/api/v1/projects`, `/api/v2/projects`
- **Increment**: Major version only when breaking changes occur

### When to Version

**Create New Version When**:
- Changing response structure (removing/renaming fields)
- Changing request validation rules (more restrictive)
- Changing endpoint behavior significantly
- Removing endpoints

**Don't Version When**:
- Adding optional fields to responses
- Adding optional parameters to requests
- Adding new endpoints
- Fixing bugs
- Improving performance

### Version Implementation

#### Route Organization

```php
// routes/api.php

// v1 Routes
Route::prefix('v1')->middleware(['web', 'auth', 'throttle:200,1'])->group(function () {
    // Resource endpoints
    Route::apiResource('projects', ProjectController::class);
    Route::apiResource('customers', CustomerController::class);
    Route::apiResource('vendors', VendorController::class);

    // Use-case endpoints
    Route::prefix('import')->group(function () {
        Route::get('status/{jobId}', [EnhancedImportController::class, 'getStatus']);
        Route::get('preview', [EnhancedImportController::class, 'getPreview']);
    });
});

// v2 Routes (future)
Route::prefix('v2')->middleware(['api', 'auth:sanctum', 'throttle:300,1'])->group(function () {
    // Updated implementations
});
```

#### Controller Namespace

```php
// Version in namespace
namespace App\Http\Controllers\Api\v1;

// Version in directory
src/App/Http/Controllers/Api/v1/ProjectController.php
src/App/Http/Controllers/Api/v2/ProjectController.php
```

### Version Lifecycle

1. **Alpha**: Development, unstable, may change
2. **Beta**: Testing, mostly stable, minor changes possible
3. **Stable**: Production-ready, guaranteed compatibility
4. **Deprecated**: Marked for removal, sunset date announced
5. **Sunset**: Removed from codebase

**Deprecation Process**:
1. Announce deprecation 6 months before sunset
2. Add `X-API-Deprecated: true` header to responses
3. Log deprecation warnings
4. Provide migration guide to new version
5. Remove after sunset date

---

## Base API Controller

All API controllers should extend `BaseApiController` for consistent functionality.

### Location

`src/App/Http/Controllers/Api/BaseApiController.php`

### Core Responsibilities

1. **Standardized Response Formats**
2. **Error Handling**
3. **Pagination Helpers**
4. **Validation Shortcuts**
5. **Tenant Context**
6. **Request Parsing**
7. **Resource Transformation**

### Implementation Specification

```php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

/**
 * Base API Controller
 *
 * Provides common functionality for all API controllers including:
 * - Standardized response formats
 * - Error handling
 * - Pagination helpers
 * - Tenant context utilities
 * - Request parsing
 *
 * All API controllers should extend this base controller.
 */
abstract class BaseApiController extends Controller
{
    /**
     * Return successful response
     *
     * @param mixed $data Response data
     * @param int $status HTTP status code
     * @param array $meta Additional metadata
     * @return JsonResponse
     */
    protected function successResponse($data, int $status = 200, array $meta = []): JsonResponse
    {
        $response = [
            'success' => true,
            'data' => $data,
            'timestamp' => now()->toISOString(),
        ];

        if (!empty($meta)) {
            $response['meta'] = $meta;
        }

        return response()->json($response, $status);
    }

    /**
     * Return error response
     *
     * @param string $message Error message
     * @param int $status HTTP status code
     * @param array $errors Detailed error information
     * @return JsonResponse
     */
    protected function errorResponse(string $message, int $status = 400, array $errors = []): JsonResponse
    {
        $response = [
            'success' => false,
            'message' => $message,
            'timestamp' => now()->toISOString(),
        ];

        if (!empty($errors)) {
            $response['errors'] = $errors;
        }

        return response()->json($response, $status);
    }

    /**
     * Return conflict response (optimistic concurrency failure)
     *
     * @param array $record Record identifier (type + id)
     * @param string $currentVersion Server's current version
     * @param array|null $updatedBy Who updated (id + name)
     * @return JsonResponse
     */
    protected function conflictResponse(array $record, string $currentVersion, ?array $updatedBy = null): JsonResponse
    {
        return $this->errorResponse('The record was updated more recently.', 409, [
            'code' => 'RESOURCE_CONFLICT',
            'current_version' => $currentVersion,
            'updated_by' => $updatedBy,
            'record' => $record,
        ]);
    }

    /**
     * Return validation error response
     *
     * @param mixed $errors Validation errors (from validator)
     * @return JsonResponse
     */
    protected function validationErrorResponse($errors): JsonResponse
    {
        return response()->json([
            'success' => false,
            'message' => 'Validation failed',
            'errors' => $errors,
            'timestamp' => now()->toISOString(),
        ], 422);
    }

    /**
     * Return paginated response
     *
     * @param \Illuminate\Pagination\LengthAwarePaginator $paginator
     * @param callable|null $transformer Optional data transformer
     * @return JsonResponse
     */
    protected function paginatedResponse($paginator, ?callable $transformer = null): JsonResponse
    {
        $data = $transformer
            ? $paginator->map($transformer)
            : $paginator->items();

        return response()->json([
            'success' => true,
            'data' => $data,
            'pagination' => [
                'current_page' => $paginator->currentPage(),
                'per_page' => $paginator->perPage(),
                'total' => $paginator->total(),
                'total_pages' => $paginator->lastPage(),
                'from' => $paginator->firstItem(),
                'to' => $paginator->lastItem(),
                'has_next_page' => $paginator->hasMorePages(),
                'has_prev_page' => $paginator->currentPage() > 1,
                'next_page' => $paginator->hasMorePages() ? $paginator->currentPage() + 1 : null,
                'prev_page' => $paginator->currentPage() > 1 ? $paginator->currentPage() - 1 : null,
                'next_page_url' => $paginator->nextPageUrl(),
                'prev_page_url' => $paginator->previousPageUrl(),
            ],
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Get current tenant ID
     *
     * @return int|null
     */
    protected function getCurrentTenantId(): ?int
    {
        try {
            $user = auth()->user();
            if ($user && method_exists($user, 'getCurrentTenantId')) {
                $tenantId = $user->getCurrentTenantId();
                return is_string($tenantId) ? (int) $tenantId : $tenantId;
            }

            $tenantService = app(\App\Services\TenantService::class);
            $tenantId = $tenantService->getCurrentTenantId();
            return is_string($tenantId) ? (int) $tenantId : $tenantId;
        } catch (\Exception $e) {
            Log::warning('Failed to get tenant ID', ['error' => $e->getMessage()]);
            return null;
        }
    }

    /**
     * Parse pagination parameters from request
     *
     * @param Request $request
     * @param int $defaultLimit Default items per page
     * @param int $maxLimit Maximum items per page
     * @return array ['limit' => int, 'page' => int]
     */
    protected function parsePaginationParams(Request $request, int $defaultLimit = 25, int $maxLimit = 100): array
    {
        $limit = min((int) $request->get('limit', $defaultLimit), $maxLimit);
        $page = max((int) $request->get('page', 1), 1);

        return compact('limit', 'page');
    }

    /**
     * Parse sorting parameters from request
     *
     * @param Request $request
     * @param string $defaultSort Default sort field
     * @param string $defaultDirection Default sort direction
     * @return array ['sort' => string, 'direction' => string]
     */
    protected function parseSortParams(Request $request, string $defaultSort = 'id', string $defaultDirection = 'asc'): array
    {
        $sort = $request->get('sort', $defaultSort);
        $direction = strtolower($request->get('direction', $defaultDirection));
        $direction = in_array($direction, ['asc', 'desc']) ? $direction : 'asc';

        return compact('sort', 'direction');
    }

    /**
     * Parse filter parameters from request
     *
     * @param Request $request
     * @return array Associative array of filters
     */
    protected function parseFilterParams(Request $request): array
    {
        $filters = $request->get('filters', []);

        if (!is_array($filters)) {
            return [];
        }

        // Remove empty values
        return array_filter($filters, function ($value) {
            return $value !== null && $value !== '';
        });
    }

    /**
     * Log API error with context
     *
     * @param string $message Error message
     * @param \Throwable $exception Exception object
     * @param array $context Additional context
     * @return void
     */
    protected function logError(string $message, \Throwable $exception, array $context = []): void
    {
        Log::error($message, array_merge([
            'error' => $exception->getMessage(),
            'trace' => $exception->getTraceAsString(),
            'tenant_id' => $this->getCurrentTenantId(),
            'user_id' => auth()->id(),
        ], $context));
    }

    /**
     * Validate required permissions for record type
     *
     * @param string $permission Permission name (can_read, can_create, can_update, can_delete)
     * @param string $recordType Record type identifier
     * @return bool
     */
    protected function checkPermission(string $permission, string $recordType): bool
    {
        $user = auth()->user();

        if (!$user) {
            return false;
        }

        if (!method_exists($user, 'hasPermissionForRecordType')) {
            return false;
        }

        return $user->hasPermissionForRecordType($permission, $recordType);
    }

    /**
     * Return forbidden response
     *
     * @param string $message Optional custom message
     * @return JsonResponse
     */
    protected function forbiddenResponse(string $message = 'You are not authorized to perform this action.'): JsonResponse
    {
        return $this->errorResponse($message, 403);
    }

    /**
     * Return not found response
     *
     * @param string $message Optional custom message
     * @return JsonResponse
     */
    protected function notFoundResponse(string $message = 'Resource not found.'): JsonResponse
    {
        return $this->errorResponse($message, 404);
    }

    /**
     * Return conflict response (optimistic concurrency failure)
     *
     * Used when a record update fails due to stale client data.
     * Part of the overwrite protection system (docs/designs/overwrite-protection.md).
     *
     * @param array $record Record identifier (type + id)
     * @param string $currentVersion Server's current version (ISO 8601 timestamp)
     * @param array|null $updatedBy Who updated last (id + name), if available
     * @return JsonResponse
     */
    protected function conflictResponse(array $record, string $currentVersion, ?array $updatedBy = null): JsonResponse
    {
        return $this->errorResponse('The record was updated more recently.', 409, [
            'code' => 'RESOURCE_CONFLICT',
            'current_version' => $currentVersion,
            'updated_by' => $updatedBy,
            'record' => $record,
        ]);
    }
}
```

### Usage Example

```php
namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Api\BaseApiController;
use Domain\Projects\Models\Project;
use Domain\Projects\Actions\CreateProject;
use Domain\Projects\Actions\UpdateProject;
use Illuminate\Http\Request;

class ProjectController extends BaseApiController
{
    /**
     * List all projects
     */
    public function index(Request $request)
    {
        if (!$this->checkPermission('can_read', 'project')) {
            return $this->forbiddenResponse();
        }

        try {
            $pagination = $this->parsePaginationParams($request);
            $sort = $this->parseSortParams($request, 'title', 'asc');
            $filters = $this->parseFilterParams($request);

            $query = Project::query();

            // Apply filters
            foreach ($filters as $field => $value) {
                $query->where($field, $value);
            }

            // Apply sorting
            $query->orderBy($sort['sort'], $sort['direction']);

            // Paginate
            $projects = $query->paginate($pagination['limit']);

            return $this->paginatedResponse($projects);

        } catch (\Exception $e) {
            $this->logError('Failed to list projects', $e);
            return $this->errorResponse('Failed to retrieve projects', 500);
        }
    }

    /**
     * Create new project
     */
    public function store(Request $request, CreateProject $createAction)
    {
        if (!$this->checkPermission('can_create', 'project')) {
            return $this->forbiddenResponse();
        }

        try {
            $validated = $request->validate([
                'title' => 'required|string|max:255',
                'customer' => 'required|integer',
                // ... more validation
            ]);

            $result = $createAction->create($validated);

            if ($result['success']) {
                return $this->successResponse($result['record'], 201);
            }

            return $this->errorResponse($result['message'], 400);

        } catch (\Illuminate\Validation\ValidationException $e) {
            return $this->validationErrorResponse($e->errors());
        } catch (\Exception $e) {
            $this->logError('Failed to create project', $e);
            return $this->errorResponse('Failed to create project', 500);
        }
    }

    /**
     * Update existing project (with optimistic concurrency control)
     */
    public function update(Request $request, int $id, UpdateProject $updateAction)
    {
        if (!$this->checkPermission('can_update', 'project')) {
            return $this->forbiddenResponse();
        }

        try {
            $project = Project::findOrFail($id);

            // Optimistic concurrency check
            if ($request->has('client_version')) {
                $clientVersion = $request->input('client_version');
                $serverVersion = $project->updated_at->toJSON();

                if ($clientVersion !== $serverVersion) {
                    return $this->conflictResponse(
                        record: ['type' => 'project', 'id' => $project->id],
                        currentVersion: $serverVersion,
                        updatedBy: $project->updated_by ? [
                            'id' => $project->updatedByUser->id,
                            'name' => $project->updatedByUser->name,
                        ] : null
                    );
                }
            }

            $validated = $request->validate([
                'title' => 'sometimes|string|max:255',
                // ... more validation
            ]);

            $result = $updateAction->update($project, $validated);

            if ($result['success']) {
                return $this->successResponse($result['record']);
            }

            return $this->errorResponse($result['message'], 400);

        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            return $this->notFoundResponse();
        } catch (\Illuminate\Validation\ValidationException $e) {
            return $this->validationErrorResponse($e->errors());
        } catch (\Exception $e) {
            $this->logError('Failed to update project', $e, ['id' => $id]);
            return $this->errorResponse('Failed to update project', 500);
        }
    }
}
```

---

## RESTful Resource Endpoints

### Standard REST Operations

All domain models should provide standard REST endpoints following this pattern:

| Method | URI | Action | Description |
|--------|-----|--------|-------------|
| GET | `/api/v1/{resource}` | index | List all resources (paginated) |
| GET | `/api/v1/{resource}/{id}` | show | Get single resource |
| POST | `/api/v1/{resource}` | store | Create new resource |
| PUT/PATCH | `/api/v1/{resource}/{id}` | update | Update existing resource |
| DELETE | `/api/v1/{resource}/{id}` | destroy | Delete resource |

### Laravel API Resource Routes

Use Laravel's `apiResource` helper for standard REST endpoints:

```php
Route::prefix('v1')->middleware(['web', 'auth', 'throttle:200,1'])->group(function () {
    Route::apiResource('projects', ProjectController::class);
    Route::apiResource('customers', CustomerController::class);
    Route::apiResource('vendors', VendorController::class);
    Route::apiResource('items', ItemController::class);
});
```

### Resource Controller Template

```php
namespace App\Http\Controllers\Api\v1;

use App\Http\Controllers\Api\BaseApiController;
use Domain\{Domain}\Models\{Model};
use Domain\{Domain}\Actions\Create{Model};
use Domain\{Domain}\Actions\Update{Model};
use Domain\{Domain}\Actions\Delete{Model};
use Illuminate\Http\Request;

class {Model}Controller extends BaseApiController
{
    protected string $recordType = '{record_type}';

    /**
     * List all {resources}
     *
     * GET /api/v1/{resources}
     *
     * Query parameters:
     * - limit: Items per page (default: 25, max: 100)
     * - page: Page number (default: 1)
     * - sort: Sort field (default: id)
     * - direction: Sort direction (asc|desc, default: asc)
     * - filters[field]: Filter by field value
     */
    public function index(Request $request)
    {
        if (!$this->checkPermission('can_read', $this->recordType)) {
            return $this->forbiddenResponse();
        }

        try {
            $pagination = $this->parsePaginationParams($request);
            $sort = $this->parseSortParams($request);
            $filters = $this->parseFilterParams($request);

            $query = {Model}::query();

            foreach ($filters as $field => $value) {
                $query->where($field, $value);
            }

            $query->orderBy($sort['sort'], $sort['direction']);
            $results = $query->paginate($pagination['limit']);

            return $this->paginatedResponse($results);

        } catch (\Exception $e) {
            $this->logError('Failed to list {resources}', $e);
            return $this->errorResponse('Failed to retrieve {resources}', 500);
        }
    }

    /**
     * Get single {resource}
     *
     * GET /api/v1/{resources}/{id}
     */
    public function show(int $id)
    {
        if (!$this->checkPermission('can_read', $this->recordType)) {
            return $this->forbiddenResponse();
        }

        try {
            $record = {Model}::findOrFail($id);
            return $this->successResponse($record);

        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            return $this->notFoundResponse();
        } catch (\Exception $e) {
            $this->logError('Failed to retrieve {resource}', $e, ['id' => $id]);
            return $this->errorResponse('Failed to retrieve {resource}', 500);
        }
    }

    /**
     * Create new {resource}
     *
     * POST /api/v1/{resources}
     */
    public function store(Request $request, Create{Model} $createAction)
    {
        if (!$this->checkPermission('can_create', $this->recordType)) {
            return $this->forbiddenResponse();
        }

        try {
            $validated = $request->validate([
                // Add validation rules
            ]);

            $result = $createAction->create($validated);

            if ($result['success']) {
                return $this->successResponse($result['record'], 201);
            }

            return $this->errorResponse($result['message'], 400, $result['errors'] ?? []);

        } catch (\Illuminate\Validation\ValidationException $e) {
            return $this->validationErrorResponse($e->errors());
        } catch (\Exception $e) {
            $this->logError('Failed to create {resource}', $e);
            return $this->errorResponse('Failed to create {resource}', 500);
        }
    }

    /**
     * Update existing {resource}
     *
     * PUT/PATCH /api/v1/{resources}/{id}
     */
    public function update(Request $request, int $id, Update{Model} $updateAction)
    {
        if (!$this->checkPermission('can_update', $this->recordType)) {
            return $this->forbiddenResponse();
        }

        try {
            $record = {Model}::findOrFail($id);

            $validated = $request->validate([
                // Add validation rules
            ]);

            $result = $updateAction->update($record, $validated);

            if ($result['success']) {
                return $this->successResponse($result['record']);
            }

            return $this->errorResponse($result['message'], 400, $result['errors'] ?? []);

        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            return $this->notFoundResponse();
        } catch (\Illuminate\Validation\ValidationException $e) {
            return $this->validationErrorResponse($e->errors());
        } catch (\Exception $e) {
            $this->logError('Failed to update {resource}', $e, ['id' => $id]);
            return $this->errorResponse('Failed to update {resource}', 500);
        }
    }

    /**
     * Delete {resource}
     *
     * DELETE /api/v1/{resources}/{id}
     */
    public function destroy(int $id, Delete{Model} $deleteAction)
    {
        if (!$this->checkPermission('can_delete', $this->recordType)) {
            return $this->forbiddenResponse();
        }

        try {
            $record = {Model}::findOrFail($id);

            $result = $deleteAction->delete($record);

            if ($result['success']) {
                return $this->successResponse(null, 204);
            }

            return $this->errorResponse($result['message'], 400);

        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            return $this->notFoundResponse();
        } catch (\Exception $e) {
            $this->logError('Failed to delete {resource}', $e, ['id' => $id]);
            return $this->errorResponse('Failed to delete {resource}', 500);
        }
    }
}
```

### Nested Resources

For nested relationships (e.g., project tasks under projects):

```php
// Route definition
Route::prefix('v1')->group(function () {
    Route::apiResource('projects.tasks', ProjectTaskController::class);
});

// Controller implementation
public function index(Request $request, int $projectId)
{
    $project = Project::findOrFail($projectId);
    $tasks = $project->projectTasks()->paginate(25);
    return $this->paginatedResponse($tasks);
}
```

---

## Use-Case Specific Endpoints

Not all API operations fit RESTful patterns. Use-case specific endpoints are appropriate for:

- Complex operations involving multiple resources
- Business processes that don't map to CRUD
- Read-only aggregated data
- Status/health checks
- Bulk operations

### Examples from Current System

#### Import Status Endpoint
```php
// Use-case: Get comprehensive import status with metrics
GET /api/v1/import/status/{jobId}

// Better as use-case endpoint than:
// GET /api/v1/imports/{jobId} (too simple for complex operation)
```

#### Dependency Preview Endpoint
```php
// Use-case: Calculate dependency graph before import
GET /api/v1/import/preview?integration_id=1&record_type_ids[]=1&record_type_ids[]=2

// Not a resource operation, but a computation
```

#### Flow Metrics Endpoint
```php
// Use-case: Get execution history for specific flow
GET /api/v1/ipaas/metrics/flows/{flowId}/history

// Complex aggregation, not simple resource retrieval
```

#### Record Version Polling Endpoint
```php
// Use-case: Check for record updates when websockets unavailable
GET /api/v1/records/{type}/{id}/version

// Returns current version and updater info for optimistic concurrency
// Part of overwrite protection system (docs/designs/overwrite-protection/)
```

#### Presence Tracking Endpoints
```php
// Use-case: Track who's viewing/editing records for collaboration features
POST   /api/v1/presence/{type}/{id}/heartbeat    # Update tab presence
GET    /api/v1/presence/{type}/{id}/viewers      # Get current viewers
DELETE /api/v1/presence/{type}/{id}/unregister   # Remove tab presence

// Part of overwrite protection system Phase 3 (docs/designs/overwrite-protection/)
```

### When to Use Use-Case Endpoints

**Use Use-Case Endpoints When**:
- Operation spans multiple resources
- Complex business logic involved
- Result is computed/aggregated, not stored
- Operation name better describes action than REST verb
- Endpoint is primarily for a specific UI feature

**Examples**:
- `/api/v1/reports/sales-summary` - Aggregated report
- `/api/v1/projects/{id}/duplicate` - Business operation
- `/api/v1/search/advanced` - Complex query
- `/api/v1/export/projects` - Data export
- `/api/v1/bulk/delete` - Bulk operation

### Naming Conventions

Use clear, descriptive names:
- Verb-based: `/duplicate`, `/export`, `/archive`
- Noun-based for computed results: `/summary`, `/metrics`, `/preview`
- Grouped by domain: `/import/*`, `/export/*`, `/reports/*`

---

## Request/Response Standards

### Request Format

#### Content Type
All API requests with body data must use:
```
Content-Type: application/json
```

#### Request Structure
```json
{
  "field1": "value1",
  "field2": "value2",
  "nested": {
    "field3": "value3"
  }
}
```

#### Query Parameters

**Pagination**:
```
?page=1&limit=25
```

**Sorting**:
```
?sort=title&direction=asc
```

**Filtering**:
```
?filters[status]=active&filters[customer]=123
```

**Combined**:
```
?page=2&limit=50&sort=created_at&direction=desc&filters[status]=active
```

### Response Format

#### Success Response Structure

**Single Resource**:
```json
{
  "success": true,
  "data": {
    "id": 123,
    "title": "Project ABC",
    "status": "active"
  },
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

**Collection (Paginated)**:
```json
{
  "success": true,
  "data": [
    {
      "id": 123,
      "title": "Project ABC"
    },
    {
      "id": 124,
      "title": "Project XYZ"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 25,
    "total": 150,
    "total_pages": 6,
    "from": 1,
    "to": 25,
    "has_next_page": true,
    "has_prev_page": false,
    "next_page": 2,
    "prev_page": null,
    "next_page_url": "/api/v1/projects?page=2",
    "prev_page_url": null
  },
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

**With Metadata**:
```json
{
  "success": true,
  "data": { /* ... */ },
  "meta": {
    "generated_at": "2025-11-07T10:30:00Z",
    "version": "1.0.0",
    "tenant_id": 42
  },
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

#### Error Response Structure

**Validation Error** (422):
```json
{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "title": [
      "The title field is required."
    ],
    "customer": [
      "The customer field must be an integer."
    ]
  },
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

**General Error** (400, 500, etc.):
```json
{
  "success": false,
  "message": "Failed to create project",
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

**Error with Details**:
```json
{
  "success": false,
  "message": "Failed to process import",
  "errors": {
    "code": "DEPENDENCY_MISSING",
    "details": "Customer record must be imported first"
  },
  "timestamp": "2025-11-07T10:30:00.000000Z"
}
```

**Optimistic Concurrency Conflict** (409):
```json
{
  "success": false,
  "message": "The record was updated more recently.",
  "errors": {
    "code": "RESOURCE_CONFLICT",
    "current_version": "2026-01-21T18:15:22Z",
    "updated_by": { "id": 42, "name": "Alice" },
    "record": { "type": "project", "id": 123 }
  },
  "timestamp": "2026-01-21T18:15:22.000000Z"
}
```

### HTTP Status Codes

Use appropriate HTTP status codes:

| Code | Meaning | Usage |
|------|---------|-------|
| 200 | OK | Successful GET, PUT, PATCH, DELETE |
| 201 | Created | Successful POST creating new resource |
| 204 | No Content | Successful DELETE with no response body |
| 400 | Bad Request | Invalid request format or parameters |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Authenticated but lacking permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Request conflicts with current state of resource |
| 422 | Unprocessable Entity | Validation failed |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
| 503 | Service Unavailable | Temporary outage |

### Headers

**Required Headers**:
```
Content-Type: application/json
Accept: application/json
```

**Authentication** (current):
```
Cookie: laravel_session=...
X-CSRF-TOKEN: ...
```

**Authentication** (future with Sanctum):
```
Authorization: Bearer {token}
```

**Optional Headers**:
```
X-Request-ID: unique-request-id    # For tracking
X-API-Version: v1                   # Version preference
X-Socket-ID: socket-id              # Laravel Echo socket ID (for broadcast filtering)
X-Tab-ID: tab-id                    # Browser tab identifier (for multi-tab scenarios)
```

**Broadcasting & Concurrency Headers**:
- `X-Socket-ID`: Sent by Laravel Echo, used by `BroadcastHelper::toOthers()` to exclude originating socket from broadcasts
- `X-Tab-ID`: Generated by client per browser tab, used for multi-tab self-notification filtering and presence tracking

**Response Headers**:
```
Content-Type: application/json
X-RateLimit-Limit: 200              # Rate limit
X-RateLimit-Remaining: 195          # Remaining requests
X-RateLimit-Reset: 1699363200       # Reset timestamp
```

---

## Authentication & Authorization

### Current State: Session-Based Authentication

All current API routes use Laravel's session-based authentication:

```php
Route::middleware(['web', 'auth'])->group(function () {
    // API routes
});
```

**Characteristics**:
- Cookie-based session management
- CSRF protection via `X-CSRF-TOKEN` header
- Suitable for same-domain SPA applications
- Shares authentication state with web application
- User context automatically available via `auth()->user()`

**Usage in SPA**:
```javascript
// Frontend makes request to API
fetch('/api/v1/projects', {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
  },
  credentials: 'same-origin'
})
```

### Authorization Pattern

All endpoints must check permissions using the tenant-specific permission system:

```php
public function index(Request $request)
{
    // Check permission before proceeding
    if (!$this->checkPermission('can_read', 'project')) {
        return $this->forbiddenResponse();
    }

    // Permission granted, proceed
}
```

**Permission Types**:
- `can_read` - View resources
- `can_create` - Create new resources
- `can_update` - Modify existing resources
- `can_delete` - Delete resources

**Record Types**: Each domain model has a record type identifier (e.g., `project`, `customer`, `vendor`)

### Future: Token-Based Authentication (Roadmap)

**Target**: 1+ years out for external API access

#### Implementation Approach: Laravel Sanctum

**Why Sanctum**:
- Built for Laravel, minimal setup
- Supports both SPA and API token authentication
- Lightweight compared to Passport
- Perfect for mobile apps and third-party integrations
- Can coexist with session auth

#### Migration Strategy

**Phase 1: Add Token Support (No Breaking Changes)**
```php
// Support both auth methods
Route::middleware(['auth:sanctum,web'])->group(function () {
    // Works with both sessions and tokens
});
```

**Phase 2: Create Dedicated Token Endpoints**
```php
// New v2 routes with token-only auth
Route::prefix('v2')->middleware(['api', 'auth:sanctum'])->group(function () {
    // Token-based endpoints
});

// Keep v1 routes with session auth
Route::prefix('v1')->middleware(['web', 'auth'])->group(function () {
    // Session-based endpoints (existing SPAs)
});
```

**Phase 3: Token Management Endpoints**
```php
// Token generation and management
POST   /api/auth/tokens          # Create new token
GET    /api/auth/tokens          # List user's tokens
DELETE /api/auth/tokens/{id}     # Revoke token
```

#### Token Scopes (Future)

Define granular permissions via token scopes:
```php
$token = $user->createToken('mobile-app', [
    'projects:read',
    'projects:write',
    'customers:read'
]);
```

#### External Developer Access (Future)

When opening API to third parties:
1. **Developer Portal**: Self-service token generation
2. **API Documentation**: OpenAPI/Swagger specification
3. **Rate Limiting**: Per-token rate limits
4. **Usage Analytics**: Track API consumption
5. **Webhook Management**: Register webhooks for events

---

## Error Handling

### Error Response Standards

All errors must use standardized response format from `BaseApiController`:

```php
// General error
return $this->errorResponse('Failed to process request', 400);

// Validation error
return $this->validationErrorResponse($validator->errors());

// Permission error
return $this->forbiddenResponse();

// Not found error
return $this->notFoundResponse();
```

### Exception Handling Pattern

```php
public function store(Request $request)
{
    try {
        // Validate input
        $validated = $request->validate([...]);

        // Execute business logic
        $result = $this->action->execute($validated);

        // Return success
        return $this->successResponse($result, 201);

    } catch (\Illuminate\Validation\ValidationException $e) {
        // Validation errors (422)
        return $this->validationErrorResponse($e->errors());

    } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
        // Resource not found (404)
        return $this->notFoundResponse();

    } catch (\Illuminate\Auth\Access\AuthorizationException $e) {
        // Permission denied (403)
        return $this->forbiddenResponse($e->getMessage());

    } catch (\Exception $e) {
        // Unexpected errors (500)
        $this->logError('Operation failed', $e);
        return $this->errorResponse('Internal server error', 500);
    }
}
```

### Logging Strategy

**Always Log**:
- 500 errors with full stack trace
- Authentication/authorization failures
- Rate limit violations
- External API failures

**Context to Include**:
```php
$this->logError('Failed to create project', $exception, [
    'user_id' => auth()->id(),
    'tenant_id' => $this->getCurrentTenantId(),
    'input' => $request->except(['password', 'token']), // Sanitize sensitive data
    'request_id' => $request->header('X-Request-ID'),
]);
```

**Don't Log**:
- Validation errors (expected user errors)
- 404 errors (normal operation)
- Successful requests (unless debugging)

### Error Messages

**User-Facing Messages** (included in response):
- Clear, actionable
- No technical jargon
- No sensitive system information
- "Failed to create project" ✅
- "Database connection failed at line 142" ❌

**Internal Messages** (logged only):
- Technical details
- Stack traces
- Query information
- Full context

---

## Decision Trees

### Decision Tree 1: Should I Create an API Endpoint?

```
START: Need to expose functionality to frontend?
  │
  ├─→ Is this for same-domain SPA?
  │     │
  │     ├─→ YES → Can existing web controller handle AJAX?
  │     │           │
  │     │           ├─→ YES → Use existing web controller
  │     │           │          (Simple forms, basic CRUD)
  │     │           │
  │     │           └─→ NO  → Create API endpoint
  │     │                     (Complex data, needs versioning,
  │     │                      needs rate limiting)
  │     │
  │     └─→ NO (mobile/external) → Create API endpoint
  │                                  (Future-proof)
  │
  └─→ Is this internal-only operation?
        │
        ├─→ YES → Use web controller or Livewire
        │
        └─→ NO  → Create API endpoint
```

### Decision Tree 2: Which API Version Should I Use?

```
START: Creating new endpoint?
  │
  ├─→ Is this a breaking change to existing endpoint?
  │     │
  │     ├─→ YES → Create new version (v2, v3, etc.)
  │     │          Maintain old version temporarily
  │     │
  │     └─→ NO  → Can I add to existing version?
  │               │
  │               ├─→ YES (backward compatible) → Add to current version
  │               │                                (New optional fields,
  │               │                                 new endpoints)
  │               │
  │               └─→ NO (incompatible) → Create new version
  │
  └─→ Is this brand new functionality?
        │
        └─→ Add to latest stable version (currently v1)
```

### Decision Tree 3: REST Resource vs Use-Case Endpoint?

```
START: What type of endpoint do I need?
  │
  ├─→ Does it operate on a single domain model?
  │     │
  │     ├─→ YES → Is it standard CRUD?
  │     │           │
  │     │           ├─→ YES → Use REST resource endpoint
  │     │           │          (ProjectController::index, store, etc.)
  │     │           │
  │     │           └─→ NO  → Is it model-specific operation?
  │     │                     │
  │     │                     ├─→ YES → Add to resource controller
  │     │                     │          (ProjectController::duplicate)
  │     │                     │
  │     │                     └─→ NO  → Use use-case endpoint
  │     │
  │     └─→ NO (multi-model or computed) → Use use-case endpoint
  │                                          (ImportController, MetricsController)
  │
  └─→ Examples:
        │
        ├─→ REST Resource: GET /api/v1/projects (list projects)
        ├─→ Model Operation: POST /api/v1/projects/{id}/duplicate
        └─→ Use-Case: GET /api/v1/import/status/{jobId}
```

### Decision Tree 4: Where Should This Controller Live?

```
START: Creating API controller?
  │
  ├─→ Which version?
  │     │
  │     ├─→ v1 → src/App/Http/Controllers/Api/v1/{Controller}.php
  │     ├─→ v2 → src/App/Http/Controllers/Api/v2/{Controller}.php
  │     └─→ Unversioned utility → src/App/Http/Controllers/Api/{Controller}.php
  │                                (e.g., RecordRefController)
  │
  ├─→ Does it follow REST pattern?
  │     │
  │     ├─→ YES → Name after domain model
  │     │          ProjectController, CustomerController, etc.
  │     │
  │     └─→ NO  → Name after use case
  │               EnhancedImportController, FlowMetricsController, etc.
  │
  └─→ Domain organization:
        │
        ├─→ Model: Domain\Projects\Models\Project
        ├─→ Actions: Domain\Projects\Actions\{Create|Update|Delete}Project
        ├─→ API Controller: App\Http\Controllers\Api\v1\ProjectController
        └─→ Route: /api/v1/projects
```

### Decision Tree 5: What Middleware Should I Apply?

```
START: Configuring route middleware?
  │
  ├─→ Authentication required?
  │     │
  │     ├─→ YES → Current (session): ['web', 'auth']
  │     │          Future (token): ['api', 'auth:sanctum']
  │     │
  │     └─→ NO  → Public API: ['api']
  │               (Rare - most endpoints need auth)
  │
  ├─→ Rate limiting needed?
  │     │
  │     ├─→ Standard → 'throttle:200,1'    (200 req/min)
  │     ├─→ High traffic → 'throttle:300,1' (300 req/min)
  │     ├─→ Heavy operations → 'throttle:60,1'  (60 req/min)
  │     └─→ Public endpoints → 'throttle:60,1'  (Conservative)
  │
  ├─→ CSRF protection needed?
  │     │
  │     ├─→ Session auth → YES (automatic with 'web' middleware)
  │     └─→ Token auth → NO (tokens are CSRF-proof)
  │
  └─→ Tenant context needed?
        │
        └─→ YES (always) → Automatic with 'web' middleware
                           Or use 'tenant' middleware if custom
```

### Decision Tree 6: How Should I Handle Pagination?

```
START: Endpoint returns collection?
  │
  ├─→ How many items typically?
  │     │
  │     ├─→ < 100 → Consider returning all
  │     │           (Check performance first)
  │     │
  │     └─→ > 100 → Always paginate
  │
  ├─→ What pagination style?
  │     │
  │     ├─→ Standard (page numbers) → Use Laravel's paginate()
  │     │                              return $this->paginatedResponse($results)
  │     │
  │     └─→ Cursor-based (infinite scroll) → Use cursorPaginate()
  │                                           (Future enhancement)
  │
  ├─→ What default limit?
  │     │
  │     ├─→ Lists/tables → 25 items
  │     ├─→ Cards/grids → 12 items
  │     ├─→ Dropdowns → 50 items
  │     └─→ Heavy data → 10 items
  │
  └─→ What maximum limit?
        │
        ├─→ Standard → 100 items (prevent abuse)
        ├─→ Heavy data → 50 items
        └─→ Lightweight → 200 items
```

---

## Roadmap

### Phase 1: Foundation (Current - 3 Months)

**Status**: In Progress

**Goals**: Establish core API infrastructure for internal SPA

**Tasks**:
- [x] Define API architecture standards (this document)
- [ ] Create `BaseApiController` with standardized response methods
- [ ] Implement first batch of REST resource endpoints:
  - [ ] Projects API (`/api/v1/projects`)
  - [ ] Customers API (`/api/v1/customers`)
  - [ ] ProjectTasks API (`/api/v1/project-tasks`)
  - [ ] Vendors API (`/api/v1/vendors`)
- [ ] Document all endpoints with PHPDoc
- [ ] Add comprehensive error handling
- [ ] Set up API route organization in `routes/api.php`

**Success Criteria**:
- 10+ domain models have REST API endpoints
- All endpoints follow standardized response format
- Frontend SPA successfully migrated to use API instead of mixed AJAX calls
- Zero web controllers handling JSON responses

### Phase 2: Enhancement (3-6 Months)

**Status**: Planned

**Goals**: Improve API capabilities and developer experience

**Tasks**:
- [ ] Implement advanced filtering on all endpoints
  - Support operators: `eq`, `neq`, `gt`, `lt`, `in`, `like`
  - Example: `?filters[status][eq]=active&filters[budget][gt]=10000`
- [ ] Add field selection (sparse fieldsets)
  - Example: `?fields=id,title,status` (return only specified fields)
- [ ] Add relationship loading control
  - Example: `?include=customer,projectTasks` (eager load relationships)
- [ ] Implement cursor-based pagination for infinite scroll
- [ ] Add bulk operations endpoints:
  - `POST /api/v1/bulk/delete` (bulk delete)
  - `POST /api/v1/bulk/update` (bulk update)
- [ ] Create export endpoints:
  - `GET /api/v1/export/projects?format=csv`
- [ ] Improve rate limiting with per-endpoint limits
- [ ] Add request/response logging for debugging

**Success Criteria**:
- Frontend can build complex queries using API
- Reduced number of API calls via eager loading
- Bulk operations significantly faster than individual requests
- Export functionality available for all major resources

### Phase 3: Documentation & Testing (6-9 Months)

**Status**: Planned

**Goals**: Production-ready API with comprehensive documentation

**Tasks**:
- [ ] Generate OpenAPI (Swagger) specification
  - Use annotations or automatic generation
  - Host Swagger UI for interactive docs
- [ ] Create API testing suite
  - Integration tests for all endpoints
  - Contract tests for response formats
  - Performance tests for pagination
- [ ] Set up API versioning infrastructure
  - Deprecation header system
  - Version negotiation
  - Changelog generation
- [ ] Add API metrics and monitoring
  - Response time tracking
  - Error rate monitoring
  - Endpoint usage analytics
- [ ] Create API usage guide for frontend developers
- [ ] Implement API health check endpoints
  - `GET /api/health` (overall health)
  - `GET /api/health/database` (database connectivity)

**Success Criteria**:
- Swagger docs available at `/api/docs`
- 90%+ API test coverage
- API performance metrics dashboard
- < 200ms average response time for simple queries

### Phase 4: Token Authentication (9-12 Months)

**Status**: Planned

**Goals**: Enable mobile app and external integrations

**Tasks**:
- [ ] Install and configure Laravel Sanctum
- [ ] Create token management endpoints:
  - `POST /api/auth/login` (generate token)
  - `POST /api/auth/logout` (revoke token)
  - `GET /api/auth/tokens` (list user tokens)
  - `DELETE /api/auth/tokens/{id}` (revoke specific token)
- [ ] Implement token scopes for granular permissions
- [ ] Update all API routes to support both session and token auth
  - `->middleware(['auth:sanctum,web'])`
- [ ] Create v2 API with token-only authentication
- [ ] Build token testing utilities
- [ ] Document token authentication flow
- [ ] Add token refresh mechanism

**Success Criteria**:
- Mobile app can authenticate via tokens
- Existing SPA continues working with sessions
- Token scopes properly restrict access
- Token expiration and refresh working
- Zero breaking changes to existing API consumers

### Phase 5: External Access (12-18 Months)

**Status**: Future

**Goals**: Open API to third-party developers

**Tasks**:
- [ ] Build developer portal
  - User registration
  - Application/token management
  - Usage dashboard
  - Documentation access
- [ ] Implement OAuth 2.0 for third-party apps (via Laravel Passport)
- [ ] Create API key management system
- [ ] Set up per-client rate limiting
- [ ] Add webhook system:
  - `POST /api/webhooks` (register webhook)
  - Event types: `project.created`, `customer.updated`, etc.
  - Retry logic for failed deliveries
- [ ] Create API SDKs:
  - JavaScript/TypeScript SDK
  - PHP SDK
  - Python SDK (if demand exists)
- [ ] Implement API usage billing/metering (if monetized)
- [ ] Set up dedicated API gateway (optional)

**Success Criteria**:
- External developers can register and get API access
- Third-party applications successfully integrate
- Webhook system reliably delivers events
- Rate limiting prevents abuse
- API usage tracked and billed (if applicable)

### Phase 6: Advanced Features (18+ Months)

**Status**: Future Consideration

**Goals**: Cutting-edge API capabilities

**Tasks**:
- [ ] Evaluate GraphQL implementation
  - Compare benefits vs REST for our use cases
  - Consider Apollo Server or Lighthouse
  - Prototype alongside REST (not replacement)
- [ ] Implement WebSocket support for real-time updates
  - Use Laravel Echo/Pusher
  - Real-time notifications
  - Live data updates
- [ ] Add API versioning via content negotiation
  - `Accept: application/vnd.suitex.v2+json`
- [ ] Implement HATEOAS (Hypermedia as the Engine of Application State)
  - Include related resource links in responses
- [ ] Create API analytics dashboard
  - Most-used endpoints
  - Error rate trends
  - Performance metrics
  - User/client breakdowns
- [ ] Add API caching layer (Redis/Varnish)
- [ ] Implement API gateway for microservices (if architecture evolves)

**Success Criteria**:
- GraphQL evaluation complete with decision documented
- Real-time features working for high-value use cases
- API analytics providing actionable insights
- Caching reduces database load by 40%+

---

## Appendix: Quick Reference

### Common Patterns Cheat Sheet

#### Create Resource Endpoint
1. Create controller: `src/App/Http/Controllers/Api/v1/ProjectController.php`
2. Extend `BaseApiController`
3. Implement REST methods: `index`, `show`, `store`, `update`, `destroy`
4. Add route: `Route::apiResource('projects', ProjectController::class)`
5. Test with Pest

#### Add Use-Case Endpoint
1. Create controller: `src/App/Http/Controllers/Api/v1/SpecialOperationController.php`
2. Extend `BaseApiController`
3. Implement custom methods
4. Add explicit routes: `Route::get()`, `Route::post()`, etc.
5. Document in controller PHPDoc

#### Handle Errors
```php
try {
    // Operation
    return $this->successResponse($data);
} catch (ValidationException $e) {
    return $this->validationErrorResponse($e->errors());
} catch (ModelNotFoundException $e) {
    return $this->notFoundResponse();
} catch (Exception $e) {
    $this->logError('Context message', $e);
    return $this->errorResponse('User message', 500);
}
```

#### Add Pagination
```php
$results = Model::query()->paginate($this->parsePaginationParams($request)['limit']);
return $this->paginatedResponse($results);
```

#### Check Permissions
```php
if (!$this->checkPermission('can_read', 'project')) {
    return $this->forbiddenResponse();
}
```

### HTTP Status Code Reference

| Code | When to Use |
|------|-------------|
| 200 | Successful read/update/delete |
| 201 | Successful create |
| 204 | Successful delete (no body) |
| 400 | Bad request format |
| 401 | Not authenticated |
| 403 | Not authorized |
| 404 | Resource not found |
| 409 | Optimistic concurrency conflict |
| 422 | Validation failed |
| 429 | Rate limit exceeded |
| 500 | Server error |

### Route Examples

```php
// Standard REST resource
Route::apiResource('projects', ProjectController::class);
// Generates: GET, POST, PUT/PATCH, DELETE

// Nested resource
Route::apiResource('projects.tasks', ProjectTaskController::class);
// Generates: GET /projects/{project}/tasks, etc.

// Custom action on resource
Route::post('projects/{id}/duplicate', [ProjectController::class, 'duplicate']);

// Use-case endpoints
Route::prefix('import')->group(function () {
    Route::get('status/{jobId}', [ImportController::class, 'status']);
    Route::get('preview', [ImportController::class, 'preview']);
});
```

---

## Document Maintenance

**Version**: 1.0
**Last Updated**: November 7, 2025
**Next Review**: February 7, 2026
**Owners**: Backend Architecture Team

**Change Log**:
- 2025-11-07: Initial version created

**Related Documents**:
- `/docs/AI/ai_rules.md` - Project code generation rules
- `/docs/architecture/broadcasting/gantt.md` - Real-time broadcasting architecture
- `/routes/api.php` - Current API route definitions

**Feedback**:
For questions, suggestions, or improvements to this document, contact the backend architecture team or open an issue in the project repository.

---

*This document is a living guide. As the API evolves, this document should be updated to reflect current best practices and architectural decisions.*

