openapi: 3.0.3
info:
  title: Ad-tlas Advertisers API
  description: |
    Comprehensive API for managing advertisers, agencies, brands, and their relationships
    in the Ad-tlas advertising management platform.
    
    ## Features
    - Complete CRUD operations for all entities
    - Advanced filtering and search capabilities
    - Bulk operations for efficiency
    - Export functionality (CSV/Excel)
    - Role-based access control
    - Comprehensive validation and error handling
    
    ## Authentication
    All endpoints require authentication using JWT tokens or API keys.
    
    ## Rate Limiting
    - Standard users: 1000 requests/hour
    - Premium users: 5000 requests/hour
    - Enterprise users: Unlimited
    
    ## Pagination
    List endpoints support pagination with configurable page sizes (default: 20, max: 100).
  version: 1.0.0
  contact:
    name: Ad-tlas Development Team
    email: dev@ad-tlas.com
  license:
    name: Proprietary
    
servers:
  - url: https://api.ad-tlas.com/v1
    description: Production server
  - url: https://staging-api.ad-tlas.com/v1
    description: Staging server
  - url: http://localhost:8000/api/v1
    description: Development server

security:
  - BearerAuth: []
  - ApiKeyAuth: []

paths:
  # Agency Endpoints
  /agencies/:
    get:
      tags: [Agencies]
      summary: List agencies
      description: |
        Retrieve a paginated list of agencies with optional filtering and search.
        
        ## Filtering Options
        - `is_active`: Filter by active status
        - `country`: Filter by country
        - `commission_rate_min/max`: Filter by commission rate range
        - `created_at_after/before`: Filter by creation date
        
        ## Search
        Search across name, code, email, and description fields.
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PageSizeParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: is_active
          in: query
          schema:
            type: boolean
          description: Filter by active status
        - name: country
          in: query
          schema:
            type: string
          description: Filter by country
        - name: commission_rate_min
          in: query
          schema:
            type: number
            format: decimal
          description: Minimum commission rate
        - name: commission_rate_max
          in: query
          schema:
            type: number
            format: decimal
          description: Maximum commission rate
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgencyListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    
    post:
      tags: [Agencies]
      summary: Create agency
      description: Create a new advertising agency
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgencyCreate'
      responses:
        '201':
          description: Agency created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agency'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'

  /agencies/{id}/:
    get:
      tags: [Agencies]
      summary: Get agency details
      description: Retrieve detailed information about a specific agency
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgencyDetail'
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      tags: [Agencies]
      summary: Update agency
      description: Update an existing agency
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgencyUpdate'
      responses:
        '200':
          description: Agency updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agency'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    
    delete:
      tags: [Agencies]
      summary: Delete agency
      description: |
        Delete an agency. This will also affect all related advertisers and campaigns.
        Use with caution as this operation cannot be undone.
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '204':
          description: Agency deleted successfully
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Cannot delete agency with active campaigns

  /agencies/{id}/advertisers/:
    get:
      tags: [Agencies]
      summary: Get agency advertisers
      description: Retrieve all advertisers managed by a specific agency
      parameters:
        - $ref: '#/components/parameters/IdParam'
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PageSizeParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvertiserListResponse'

  /agencies/{id}/stats/:
    get:
      tags: [Agencies]
      summary: Get agency statistics
      description: Retrieve comprehensive statistics for an agency
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgencyStats'

  # Advertiser Endpoints
  /advertisers/:
    get:
      tags: [Advertisers]
      summary: List advertisers
      description: |
        Retrieve a paginated list of advertisers with filtering and search capabilities.
        
        ## Filtering Options
        - `advertiser_type`: Filter by type (individual, agency_managed, corporate, etc.)
        - `agency`: Filter by managing agency
        - `is_active`: Filter by active status
        - `industry`: Filter by industry
        - `credit_limit_min/max`: Filter by credit limit range
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PageSizeParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: advertiser_type
          in: query
          schema:
            $ref: '#/components/schemas/AdvertiserType'
        - name: agency
          in: query
          schema:
            type: integer
          description: Filter by agency ID
        - name: is_active
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvertiserListResponse'
    
    post:
      tags: [Advertisers]
      summary: Create advertiser
      description: Create a new advertiser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvertiserCreate'
      responses:
        '201':
          description: Advertiser created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Advertiser'

  /advertisers/{id}/:
    get:
      tags: [Advertisers]
      summary: Get advertiser details
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdvertiserDetail'
    
    put:
      tags: [Advertisers]
      summary: Update advertiser
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdvertiserUpdate'
      responses:
        '200':
          description: Advertiser updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Advertiser'
    
    delete:
      tags: [Advertisers]
      summary: Delete advertiser
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '204':
          description: Advertiser deleted successfully

  /advertisers/{id}/brands/:
    get:
      tags: [Advertisers]
      summary: Get advertiser brands
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandListResponse'

  /advertisers/{id}/contacts/:
    get:
      tags: [Advertisers]
      summary: Get advertiser contacts
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactListResponse'

  # Brand Endpoints
  /brands/:
    get:
      tags: [Brands]
      summary: List brands
      parameters:
        - $ref: '#/components/parameters/PageParam'
        - $ref: '#/components/parameters/PageSizeParam'
        - $ref: '#/components/parameters/SearchParam'
        - name: advertiser
          in: query
          schema:
            type: integer
        - name: category
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandListResponse'
    
    post:
      tags: [Brands]
      summary: Create brand
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandCreate'
      responses:
        '201':
          description: Brand created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Brand'

  /brands/{id}/:
    get:
      tags: [Brands]
      summary: Get brand details
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandDetail'
    
    put:
      tags: [Brands]
      summary: Update brand
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BrandUpdate'
      responses:
        '200':
          description: Brand updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Brand'

  # Brand Category Endpoints
  /brand-categories/:
    get:
      tags: [Brand Categories]
      summary: List brand categories
      description: Retrieve hierarchical list of brand categories
      parameters:
        - name: parent
          in: query
          schema:
            type: integer
          description: Filter by parent category (null for root categories)
        - name: is_active
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CategoryListResponse'
    
    post:
      tags: [Brand Categories]
      summary: Create brand category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CategoryCreate'
      responses:
        '201':
          description: Category created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BrandCategory'

  /brand-categories/{id}/:
    get:
      tags: [Brand Categories]
      summary: Get category details
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CategoryDetail'

  /brand-categories/{id}/descendants/:
    get:
      tags: [Brand Categories]
      summary: Get category descendants
      description: Get all descendant categories recursively
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BrandCategory'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  parameters:
    IdParam:
      name: id
      in: path
      required: true
      schema:
        type: integer
      description: Unique identifier
    
    PageParam:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
      description: Page number
    
    PageSizeParam:
      name: page_size
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
      description: Number of items per page
    
    SearchParam:
      name: search
      in: query
      schema:
        type: string
      description: Search query

  responses:
    BadRequest:
      description: Bad request - validation errors
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  schemas:
    # Base schemas
    PaginatedResponse:
      type: object
      properties:
        count:
          type: integer
          description: Total number of items
        next:
          type: string
          nullable: true
          description: URL for next page
        previous:
          type: string
          nullable: true
          description: URL for previous page
        results:
          type: array
          description: Array of results
    
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Error message
        details:
          type: object
          description: Detailed error information
        code:
          type: string
          description: Error code

    # Enum schemas
    AdvertiserType:
      type: string
      enum: [individual, agency_managed, corporate, government, non_profit]
    
    ContactType:
      type: string
      enum: [primary, billing, technical, marketing, legal]
    
    PaymentMethod:
      type: string
      enum: [credit_card, bank_transfer, check, invoice, prepaid]
    
    BillingCycle:
      type: string
      enum: [monthly, quarterly, annually, campaign]

    # Agency schemas
    Agency:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        name:
          type: string
          maxLength: 200
        code:
          type: string
          maxLength: 20
          pattern: '^[A-Z0-9-_]+$'
        email:
          type: string
          format: email
        phone:
          type: string
          maxLength: 20
          nullable: true
        address:
          type: string
          nullable: true
        city:
          type: string
          maxLength: 100
          nullable: true
        country:
          type: string
          maxLength: 100
          nullable: true
        website:
          type: string
          format: uri
          nullable: true
        description:
          type: string
          nullable: true
        commission_rate:
          type: number
          format: decimal
          minimum: 0
          maximum: 100
          default: 15.00
        is_active:
          type: boolean
          default: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
      required: [name, code, email]
    
    AgencyCreate:
      allOf:
        - $ref: '#/components/schemas/Agency'
        - type: object
          properties:
            id:
              readOnly: true
            created_at:
              readOnly: true
            updated_at:
              readOnly: true
    
    AgencyUpdate:
      allOf:
        - $ref: '#/components/schemas/AgencyCreate'
        - type: object
          properties:
            code:
              readOnly: true
    
    AgencyDetail:
      allOf:
        - $ref: '#/components/schemas/Agency'
        - type: object
          properties:
            total_advertisers:
              type: integer
              readOnly: true
            total_brands:
              type: integer
              readOnly: true
            active_campaigns_count:
              type: integer
              readOnly: true
    
    AgencyListResponse:
      allOf:
        - $ref: '#/components/schemas/PaginatedResponse'
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/Agency'
    
    AgencyStats:
      type: object
      properties:
        total_advertisers:
          type: integer
        total_brands:
          type: integer
        active_campaigns:
          type: integer
        total_spend:
          type: number
          format: decimal
        commission_earned:
          type: number
          format: decimal
        top_advertisers:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
              spend:
                type: number
                format: decimal

    # Advertiser schemas
    Advertiser:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        name:
          type: string
          maxLength: 200
        code:
          type: string
          maxLength: 20
          pattern: '^[A-Z0-9-_]+$'
        advertiser_type:
          $ref: '#/components/schemas/AdvertiserType'
        agency:
          type: integer
          nullable: true
        email:
          type: string
          format: email
        phone:
          type: string
          maxLength: 20
          nullable: true
        address:
          type: string
          nullable: true
        city:
          type: string
          maxLength: 100
          nullable: true
        country:
          type: string
          maxLength: 100
          nullable: true
        website:
          type: string
          format: uri
          nullable: true
        industry:
          type: string
          maxLength: 100
          nullable: true
        description:
          type: string
          nullable: true
        credit_limit:
          type: number
          format: decimal
          minimum: 0
          default: 0.00
        is_active:
          type: boolean
          default: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
      required: [name, code, email]
    
    AdvertiserCreate:
      allOf:
        - $ref: '#/components/schemas/Advertiser'
        - type: object
          properties:
            id:
              readOnly: true
            created_at:
              readOnly: true
            updated_at:
              readOnly: true
    
    AdvertiserUpdate:
      allOf:
        - $ref: '#/components/schemas/AdvertiserCreate'
        - type: object
          properties:
            code:
              readOnly: true
    
    AdvertiserDetail:
      allOf:
        - $ref: '#/components/schemas/Advertiser'
        - type: object
          properties:
            agency_details:
              $ref: '#/components/schemas/Agency'
              nullable: true
            total_brands:
              type: integer
              readOnly: true
            active_campaigns_count:
              type: integer
              readOnly: true
            total_spend:
              type: number
              format: decimal
              readOnly: true
    
    AdvertiserListResponse:
      allOf:
        - $ref: '#/components/schemas/PaginatedResponse'
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/Advertiser'

    # Brand schemas
    Brand:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        name:
          type: string
          maxLength: 200
        code:
          type: string
          maxLength: 20
          pattern: '^[A-Z0-9-_]+$'
        advertiser:
          type: integer
        category:
          type: integer
          nullable: true
        description:
          type: string
          nullable: true
        website:
          type: string
          format: uri
          nullable: true
        logo:
          type: string
          format: uri
          nullable: true
        primary_color:
          type: string
          pattern: '^#[0-9A-Fa-f]{6}$'
          nullable: true
        secondary_color:
          type: string
          pattern: '^#[0-9A-Fa-f]{6}$'
          nullable: true
        is_active:
          type: boolean
          default: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
      required: [name, code, advertiser]
    
    BrandCreate:
      allOf:
        - $ref: '#/components/schemas/Brand'
        - type: object
          properties:
            id:
              readOnly: true
            created_at:
              readOnly: true
            updated_at:
              readOnly: true
    
    BrandUpdate:
      allOf:
        - $ref: '#/components/schemas/BrandCreate'
        - type: object
          properties:
            code:
              readOnly: true
            advertiser:
              readOnly: true
    
    BrandDetail:
      allOf:
        - $ref: '#/components/schemas/Brand'
        - type: object
          properties:
            advertiser_details:
              $ref: '#/components/schemas/Advertiser'
            category_details:
              $ref: '#/components/schemas/BrandCategory'
              nullable: true
            campaigns_count:
              type: integer
              readOnly: true
            active_campaigns_count:
              type: integer
              readOnly: true
    
    BrandListResponse:
      allOf:
        - $ref: '#/components/schemas/PaginatedResponse'
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/Brand'

    # Brand Category schemas
    BrandCategory:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        name:
          type: string
          maxLength: 100
        parent:
          type: integer
          nullable: true
        description:
          type: string
          nullable: true
        order:
          type: integer
          minimum: 0
          default: 0
        is_active:
          type: boolean
          default: true
        full_path:
          type: string
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
      required: [name]
    
    CategoryCreate:
      allOf:
        - $ref: '#/components/schemas/BrandCategory'
        - type: object
          properties:
            id:
              readOnly: true
            full_path:
              readOnly: true
            created_at:
              readOnly: true
            updated_at:
              readOnly: true
    
    CategoryDetail:
      allOf:
        - $ref: '#/components/schemas/BrandCategory'
        - type: object
          properties:
            subcategories:
              type: array
              items:
                $ref: '#/components/schemas/BrandCategory'
              readOnly: true
            brands_count:
              type: integer
              readOnly: true
    
    CategoryListResponse:
      allOf:
        - $ref: '#/components/schemas/PaginatedResponse'
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/BrandCategory'

    # Contact schemas
    AdvertiserContact:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        advertiser:
          type: integer
        contact_type:
          $ref: '#/components/schemas/ContactType'
        first_name:
          type: string
          maxLength: 100
        last_name:
          type: string
          maxLength: 100
        title:
          type: string
          maxLength: 100
          nullable: true
        email:
          type: string
          format: email
        phone:
          type: string
          maxLength: 20
          nullable: true
        mobile:
          type: string
          maxLength: 20
          nullable: true
        is_active:
          type: boolean
          default: true
        full_name:
          type: string
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
      required: [advertiser, first_name, last_name, email]
    
    ContactListResponse:
      allOf:
        - $ref: '#/components/schemas/PaginatedResponse'
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/AdvertiserContact'

tags:
  - name: Agencies
    description: Advertising agency management
  - name: Advertisers
    description: Advertiser and client management
  - name: Brands
    description: Brand and product management
  - name: Brand Categories
    description: Hierarchical brand categorization
  - name: Contacts
    description: Contact information management
  - name: Billing
    description: Financial and billing management