openapi: 3.1.0
info:
  title: Neuron API
  description: API pública de campañas de llamadas con AI. Todos los ejemplos usan
    la URL base de Producción.
  version: 1.0.0
servers:
- url: https://api.neuronstudio.ai
paths:
  /api/auth/login:
    post:
      tags:
      - Authentication
      summary: User Login
      description: "Authenticate user with email and password.\n\n    ## Security\
        \ Features\n    - Rate limiting: 5 attempts per 5 minutes per IP\n    - Bcrypt\
        \ password hashing (14 rounds)\n    - httpOnly cookies for XSS protection\n\
        \    - Failed attempts tracking\n\n    ## Response\n    Returns JWT access\
        \ token (for WebSocket) and user info.\n    Tokens also set in httpOnly cookies\
        \ for API security.\n\n    ## Rate Limiting\n    After 5 failed attempts,\
        \ the IP is blocked for 5 minutes.\n    Successful login resets the counter."
      operationId: login_api_auth_login_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: Missing email or password
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Too many login attempts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/start:
    post:
      tags:
      - Campaigns
      summary: Start a new call campaign
      description: "Start a new outbound call campaign with uploaded CSV leads.\n\n\
        \    ## CSV Format Requirements\n    The CSV must contain these columns:\n\
        \    - `to_number`: Phone number in E.164 format (e.g., +525512345678)\n \
        \   - `lead_id`: Unique identifier for the lead\n    - `client_name`: Customer\
        \ name\n    - `debt`: Outstanding debt amount\n\n    Provide the CSV as plain-text\
        \ content in the `csv_content` field (raw CSV text —\n    not base64-encoded,\
        \ not a file path). To upload a .csv file directly, use\n    POST /api/campaign/start-upload\
        \ (multipart/form-data).\n\n    ## Limits\n    - Maximum leads per campaign:\
        \ Check your organization's limit\n    - Maximum concurrent campaigns: 1 per\
        \ organization\n    - Maximum active calls: Shared across all organizations\n\
        \n    ## Agent Selection\n    You can specify which AI agent to use for this\
        \ campaign using the `agent_id` parameter.\n\n    **How to get available agents:**\n\
        \    1. Call `GET /api/agents` to get a list of agents assigned to your organization\n\
        \    2. Copy the `agent_id` from the desired agent\n    3. Include it in the\
        \ `agent_id` field when starting the campaign\n\n    **Example:**\n    ```json\n\
        \    {\n      \"name\": \"January Campaign\",\n      \"csv_content\": \"to_number,lead_id,client_name,debt\\\
        n+525512345678,L001,Juan,5000\",\n      \"agent_id\": \"agent_abc123def456\"\
        \n    }\n    ```\n\n    If you don't specify an `agent_id`, the system will\
        \ use your organization's default agent (if configured).\n\n    ## Response\n\
        \    Returns campaign details including ID for tracking status via WebSocket\
        \ or polling.\n    Campaign starts immediately and begins dialing leads."
      operationId: start_campaign_api_campaign_start_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignStartRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignStartResponse'
        '400':
          description: Invalid request or validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Agent not authorized for organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Campaign limit reached
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/start-upload:
    post:
      tags:
      - Campaigns
      summary: Start a new call campaign by uploading a .csv file
      description: "Start a new outbound call campaign by uploading a `.csv` file\
        \ directly as\n    `multipart/form-data`.\n\n    This is the file-upload counterpart\
        \ of POST /api/campaign/start. Instead of\n    inlining the CSV as plain text\
        \ in a JSON body, the CSV is sent as an uploaded\n    file. All campaign validation,\
        \ agent selection and dialing behaviour is\n    identical to the JSON route.\n\
        \n    ## Form fields\n    - `file`: the `.csv` file (required, max 10MB).\n\
        \    - `name`: campaign name (required).\n    - `agent_id`: AI agent ID from\
        \ GET /api/agents (optional — falls back to the\n      organization default\
        \ agent if configured).\n    - `campaign_type`: `collections` (default), `sdr`\
        \ or `cx`.\n    - `customer_email`: email for notifications (optional).\n\
        \    - `routing_flow_id`: optional routing flow ID; CSV columns are validated\n\
        \      against the flow's required fields.\n    - `ignore_history_override`:\
        \ admin-only flag (default false).\n\n    ## CSV Format Requirements\n   \
        \ The CSV must contain these columns:\n    - `to_number`: Phone number in\
        \ E.164 format (e.g., +525512345678)\n    - `lead_id`: Unique identifier for\
        \ the lead\n    - `client_name`: Customer name\n    - `debt`: Outstanding\
        \ debt amount (collections campaigns)\n\n    ## Response\n    Returns campaign\
        \ details including ID for tracking status via WebSocket or polling.\n   \
        \ Campaign starts immediately and begins dialing leads."
      operationId: start_campaign_upload_api_campaign_start_upload_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Body_start_campaign_upload_api_campaign_start_upload_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignStartResponse'
        '400':
          description: Invalid request or validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Agent not authorized for organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Campaign limit reached
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/stop:
    post:
      tags:
      - Campaigns
      summary: Stop a running campaign
      description: "Stop and finalize a campaign permanently.\n\n    ## Behavior\n\
        \    - Marks the campaign as 'stopped' in the database\n    - Clears the remaining\
        \ queue (no more calls will be placed)\n    - Active calls continue until\
        \ completion\n    - Cannot be resumed after stopping\n\n    ## Campaign Selection\n\
        \    If `campaign_id` is not provided, stops the organization's currently\
        \ active campaign.\n\n    ## Difference from Pause\n    - **Stop**: Permanent\
        \ - campaign cannot be resumed\n    - **Pause**: Temporary - campaign can\
        \ be resumed later with same queue\n\n    Use pause if you want to temporarily\
        \ halt calling and resume later."
      operationId: stop_campaign_endpoint_api_campaign_stop_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignOperationRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: No campaign to stop
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Not authorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/pause:
    post:
      tags:
      - Campaigns
      summary: Pause a running campaign
      description: "Temporarily pause a running campaign.\n\n    ## Behavior\n   \
        \ - Marks the campaign as 'paused' in the database\n    - Stops placing new\
        \ calls from the queue\n    - Active calls continue until completion\n   \
        \ - Queue is preserved and can be resumed later\n\n    ## Campaign Selection\n\
        \    If `campaign_id` is not provided, pauses the organization's currently\
        \ active campaign.\n\n    ## Difference from Stop\n    - **Pause**: Temporary\
        \ - can be resumed with same queue intact\n    - **Stop**: Permanent - campaign\
        \ cannot be resumed, queue is cleared\n\n    Use pause when you need to temporarily\
        \ halt calling (e.g., during business hours adjustment)."
      operationId: pause_campaign_endpoint_api_campaign_pause_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignOperationRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: No campaign to pause
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Not authorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/resume:
    post:
      tags:
      - Campaigns
      summary: Resume a paused campaign
      description: "Resume a previously paused campaign.\n\n    ## Behavior\n    -\
        \ Changes campaign state from 'paused' to 'running'\n    - Resumes placing\
        \ calls from the preserved queue\n    - Continues from where the campaign\
        \ was paused\n    - All leads remain in their original order\n\n    ## Campaign\
        \ Selection\n    If `campaign_id` is not provided, resumes the organization's\
        \ currently paused campaign.\n\n    ## Requirements\n    - Campaign must be\
        \ in 'paused' state (cannot resume a 'stopped' or 'completed' campaign)\n\
        \    - Queue must have remaining leads to call\n\n    Use this to continue\
        \ a campaign after a temporary pause."
      operationId: resume_campaign_endpoint_api_campaign_resume_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignOperationRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          description: No paused campaign to resume
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Not authorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/status:
    get:
      tags:
      - Campaigns
      summary: Get real-time campaign status
      description: "Get the current status of the organization's active campaign with\
        \ real-time metrics.\n\n    ## Authorization\n    - User must be authenticated\n\
        \    - Returns status for user's organization only\n\n    ## Response Data\n\
        \    Returns real-time metrics including:\n    - **Campaign state**: idle,\
        \ running, paused, stopped, completed\n    - **Active calls**: Number of calls\
        \ currently in progress\n    - **Queue size**: Number of leads waiting to\
        \ be called\n    - **Campaign details**: Name, dates, lead counts, agent info\n\
        \    - **Global status**: System-wide metrics (total active calls, capacity)\n\
        \n    ## Use Cases\n    - Dashboard real-time display\n    - Monitoring campaign\
        \ progress\n    - Capacity planning\n    - Alerting on campaign completion\
        \ or errors\n\n    ## Note on Real-Time Updates\n    For continuous real-time\
        \ updates, consider using the WebSocket endpoint `/ws/campaign/status`\n \
        \   instead of polling this endpoint. WebSocket provides push-based updates\
        \ with lower latency\n    and reduced server load."
      operationId: get_campaign_status_api_campaign_status_get
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignStatusResponse'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaigns:
    get:
      tags:
      - Campaigns
      summary: List all campaigns
      description: "Get a list of all campaigns for your organization.\n\n    ## Query\
        \ Parameters\n    - **type** (optional): Filter by campaign type ('collections'\
        \ or 'sdr'). Omit to get all campaigns.\n\n    ## Response\n    Returns an\
        \ array of campaign objects, each containing:\n    - Campaign ID and name\n\
        \    - Status (running, paused, stopped, completed)\n    - Creation date and\
        \ start/end dates\n    - Lead counts (total, completed, remaining)\n    -\
        \ Agent ID assigned to the campaign\n    - Campaign type (collections or sdr)\n\
        \n    ## Pagination\n    Returns the 50 most recent campaigns.\n    Contact\
        \ support if you need access to older campaigns or custom pagination."
      operationId: list_campaigns_api_campaigns_get
      security:
      - HTTPBearer: []
      parameters:
      - name: type
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: 'Filter by campaign type: ''collections'' or ''sdr'''
          title: Type
        description: 'Filter by campaign type: ''collections'' or ''sdr'''
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignListResponse'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/schedule:
    post:
      tags:
      - Campaigns
      summary: Schedule a campaign to run at a future time
      description: Schedule a campaign to start automatically at a future date/time.
      operationId: schedule_campaign_api_campaign_schedule_post
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CampaignScheduleRequest'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignScheduleResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/scheduled:
    get:
      tags:
      - Campaigns
      summary: List upcoming scheduled campaigns for this organization
      description: Return all scheduled (not yet fired) campaigns for the authenticated
        user's org.
      operationId: list_scheduled_campaigns_api_campaign_scheduled_get
      security:
      - HTTPBearer: []
      parameters:
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/campaign/scheduled/{campaign_id}:
    delete:
      tags:
      - Campaigns
      summary: Cancel a scheduled campaign
      description: Cancel a scheduled campaign. Only the owning organization can cancel.
      operationId: cancel_scheduled_campaign_api_campaign_scheduled__campaign_id__delete
      security:
      - HTTPBearer: []
      parameters:
      - name: campaign_id
        in: path
        required: true
        schema:
          type: integer
          description: Campaign ID to cancel
          title: Campaign Id
        description: Campaign ID to cancel
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/analytics/stats:
    get:
      tags:
      - Analytics
      summary: Get analytics statistics
      description: "Get comprehensive analytics statistics for the user's organization.\n\
        \n    ## Authorization\n    - User must be authenticated\n    - Returns statistics\
        \ for user's organization only\n\n    ## Query Parameters\n    All filters\
        \ are optional and combine with AND semantics.\n    - **campaign_type**: Filter\
        \ by campaign type (collections, sdr, interviewer, cx)\n    - **campaign_ids**:\
        \ Comma-separated campaign IDs (e.g., \"1,2,3\")\n    - **call_outcome**:\
        \ Filter by specific outcome\n    - **sentiment**: Filter by sentiment (positive,\
        \ negative, neutral)\n    - **payment_filter**: \"committed\" or \"none\"\n\
        \    - **from_date**: Start date ISO 8601 (e.g., \"2026-04-24T00:00:00Z\"\
        ). Inclusive.\n    - **to_date**: End date ISO 8601. Exclusive upper bound.\n\
        \n    ## Statistics Included\n    Returns aggregated metrics including:\n\
        \    - **Total / completed / analyzed calls**: Volume KPIs\n    - **Payment\
        \ commitments**: count, total committed, total original debt\n    - **Qualification\
        \ / pass / resolution rates**: per-campaign-type KPIs (always present, 0.0\
        \ if N/A)\n    - **Duration**: avg and total in seconds\n    - **Breakdown\
        \ dicts**: sentiment, outcome, cx_category\n    - **Contact rate**: fraction\
        \ of calls that reached a live contact\n\n    ## Use Cases\n    - Dashboard\
        \ KPI display\n    - Performance reporting\n    - Campaign effectiveness analysis"
      operationId: get_analytics_stats_api_analytics_stats_get
      security:
      - HTTPBearer: []
      parameters:
      - name: campaign_type
        in: query
        required: false
        schema:
          type: string
          title: Campaign Type
      - name: campaign_ids
        in: query
        required: false
        schema:
          type: string
          title: Campaign Ids
      - name: call_outcome
        in: query
        required: false
        schema:
          type: string
          title: Call Outcome
      - name: sentiment
        in: query
        required: false
        schema:
          type: string
          title: Sentiment
      - name: payment_filter
        in: query
        required: false
        schema:
          type: string
          title: Payment Filter
      - name: from_date
        in: query
        required: false
        schema:
          type: string
          title: From Date
      - name: to_date
        in: query
        required: false
        schema:
          type: string
          title: To Date
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnalyticsStatsResponse'
        '400':
          description: Invalid query parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/analytics/calls:
    get:
      tags:
      - Analytics
      summary: Get paginated list of calls
      description: "Get a paginated list of calls with optional filtering.\n\n   \
        \ Uses keyset (cursor-based) pagination — stable under inserts and\n    index-friendly.\
        \ No offset; pass the prior response's `next_cursor` to\n    fetch the next\
        \ page.\n\n    ## Authorization\n    - User must be authenticated\n    - Returns\
        \ only calls from user's organization\n\n    ## Query Parameters\n    - **limit**:\
        \ Max calls per page (default 100, max 100)\n    - **cursor**: Opaque cursor\
        \ from previous response's `next_cursor`\n    - **campaign_ids**: Filter by\
        \ campaigns (comma-separated, e.g., \"1,2,3\")\n    - **call_outcome**: Filter\
        \ by outcome (e.g., \"promised_payment\")\n    - **sentiment**: Filter by\
        \ sentiment (\"positive\", \"negative\", \"neutral\")\n    - **payment_filter**:\
        \ \"committed\" or \"none\"\n    - **from_date**: Start date ISO 8601, inclusive\n\
        \    - **to_date**: End date ISO 8601, exclusive\n\n    ## Response Structure\n\
        \    - **calls**: Array of call objects (transcript + raw_data excluded; use\n\
        \      `/api/analytics/call/{call_id}` for details).\n    - **pagination**:\
        \ `{limit, count, has_more, next_cursor}` — `next_cursor`\n      is `null`\
        \ when there are no more pages.\n\n    ## Pagination Example\n    ```\n  \
        \  # First page\n    GET /api/analytics/calls?limit=100\n\n    # Next page\n\
        \    GET /api/analytics/calls?limit=100&cursor=<next_cursor>\n\n    # With\
        \ date range\n    GET /api/analytics/calls?from_date=2026-04-01T00:00:00Z&to_date=2026-05-01T00:00:00Z\n\
        \    ```\n\n    ## Use Cases\n    - Call history display\n    - Analytics\
        \ dashboard\n    - Filtered reports"
      operationId: get_analytics_calls_api_analytics_calls_get
      security:
      - HTTPBearer: []
      parameters:
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          default: 100
          title: Limit
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          title: Cursor
      - name: campaign_ids
        in: query
        required: false
        schema:
          type: string
          title: Campaign Ids
      - name: call_outcome
        in: query
        required: false
        schema:
          type: string
          title: Call Outcome
      - name: sentiment
        in: query
        required: false
        schema:
          type: string
          title: Sentiment
      - name: payment_filter
        in: query
        required: false
        schema:
          type: string
          title: Payment Filter
      - name: campaign_type
        in: query
        required: false
        schema:
          type: string
          title: Campaign Type
      - name: from_date
        in: query
        required: false
        schema:
          type: string
          title: From Date
      - name: to_date
        in: query
        required: false
        schema:
          type: string
          title: To Date
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallsListResponse'
        '400':
          description: Invalid query parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/analytics/call/{call_id}:
    get:
      tags:
      - Analytics
      summary: Get detailed call information
      description: "Get comprehensive information for a single call including transcript\
        \ and full AI analysis.\n\n    ## Authorization\n    - User must be authenticated\n\
        \    - Call must belong to user's organization\n\n    ## Response Data\n \
        \   Returns complete call details including:\n\n    ### Call Metadata\n  \
        \  - Call ID and campaign information\n    - Lead details (ID, name, debt\
        \ amount)\n    - Phone numbers (to/from)\n    - Timestamps (start, end, duration)\n\
        \    - Call status and disconnect reason\n\n    ### Transcript\n    - Full\
        \ conversation transcript\n    - Recording URL (if available)\n\n    ### AI\
        \ Analysis\n    - **Call outcome**: Categorized result (promised_payment,\
        \ no_answer, refused, etc.)\n    - **Sentiment**: Overall sentiment (positive,\
        \ negative, neutral)\n    - **Payment promise**: Boolean flag and details\
        \ if commitment made\n    - **Payment amount**: Committed payment amount\n\
        \    - **Payment date**: Promised payment date\n    - **Summary**: AI-generated\
        \ conversation summary\n    - **Customer objections**: Extracted objections\
        \ or concerns\n    - **Key phrases**: Important quotes from the conversation\n\
        \    - **Follow-up needed**: Whether follow-up action is required\n    - **Confidence\
        \ scores**: AI confidence levels for outcome, payment, sentiment\n\n    ##\
        \ Use Cases\n    - Detailed call review\n    - Quality assurance\n    - Training\
        \ and coaching\n    - Dispute resolution\n    - Payment tracking"
      operationId: get_call_details_api_analytics_call__call_id__get
      security:
      - HTTPBearer: []
      parameters:
      - name: call_id
        in: path
        required: true
        schema:
          type: string
          title: Call Id
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallDetailsResponse'
        '403':
          description: Not authorized to view this call
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Call not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /health:
    get:
      summary: Health Check
      description: "Health check endpoint.\n\nReturns:\n    JSON with status and active\
        \ call count"
      operationId: health_check_health_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /health/ready:
    get:
      summary: Health Ready
      description: 'Readiness probe.


        Returns 200 only if the service is ready to accept traffic.

        Use this for load balancer health checks.'
      operationId: health_ready_health_ready_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /health/live:
    get:
      summary: Health Live
      description: 'Liveness probe.


        Returns 200 if the process is alive (not deadlocked).

        Simple and fast - no external dependencies checked.'
      operationId: health_live_health_live_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
  /api/agents:
    get:
      tags:
      - Agents
      summary: List available AI agents
      description: "Get a list of AI agents available for your organization.\n\n \
        \   ## Authorization\n    - User must be authenticated\n    - Returns only\
        \ agents assigned to your organization\n\n    ## Query Parameters\n    - **type**:\
        \ Filter by agent type - 'collections' or 'sdr' (optional)\n\n    ## Response\n\
        \    Returns an array of agent objects, each containing:\n    - **agent_id**:\
        \ Unique identifier to use when starting campaigns\n    - **agent_name**:\
        \ Human-readable display name\n    - **agent_type**: Type of agent - 'collections'\
        \ or 'sdr'\n\n    ## Usage\n    Use the `agent_id` from this endpoint when\
        \ creating a campaign via `/api/campaign/start`.\n\n    ## Example\n    ```json\n\
        \    {\n      \"success\": true,\n      \"agents\": [\n        {\n       \
        \   \"agent_id\": \"agent_abc123def456\",\n          \"agent_name\": \"Debt\
        \ Collection Agent - Spanish\",\n          \"agent_type\": \"collections\"\
        \n        },\n        {\n          \"agent_id\": \"agent_xyz789ghi012\",\n\
        \          \"agent_name\": \"Sales Development Agent - English\",\n      \
        \    \"agent_type\": \"sdr\"\n        }\n      ]\n    }\n    ```\n\n    If\
        \ no agents are assigned to your organization, contact support."
      operationId: list_agents_api_agents_get
      security:
      - HTTPBearer: []
      parameters:
      - name: type
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Type
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/csv-template:
    get:
      summary: Download Csv Template
      description: Download a CSV template for campaign leads.
      operationId: download_csv_template_api_csv_template_get
      security:
      - HTTPBearer: []
      parameters:
      - name: type
        in: query
        required: false
        schema:
          type: string
          default: collections
          title: Type
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/analytics/export:
    get:
      tags:
      - Analytics
      summary: Export calls to Excel
      description: |-
        Export the organization's call detail as an Excel (.xlsx) workbook.

        The response body is the binary file, not JSON. The suggested filename
        travels in the `Content-Disposition` header.

        ## Authorization
        - API Key (`X-API-Key`) or session token
        - Returns only calls from the caller's organization

        ## Workbook structure
        - Sheet 1: call detail, preserving the client's own CSV columns
        - Sheet 2 ("Promesas de Pago"): only rows with a payment commitment.
          Omitted entirely when there are none.

        ## Limits
        - 60 exports per hour, per organization (429 on exceed)
        - 250,000 rows per workbook; larger selections are truncated
        - Large workbooks can take tens of seconds to build
      operationId: export_analytics_excel_api_analytics_export_get
      security:
      - HTTPBearer: []
      parameters:
      - name: campaign_ids
        in: query
        required: false
        description: Comma-separated campaign IDs, e.g. "1,2,3"
        schema:
          type: string
          title: Campaign Ids
      - name: campaign_type
        in: query
        required: false
        schema:
          type: string
          title: Campaign Type
      - name: call_outcome
        in: query
        required: false
        schema:
          type: string
          title: Call Outcome
      - name: sentiment
        in: query
        required: false
        schema:
          type: string
          title: Sentiment
      - name: payment_filter
        in: query
        required: false
        description: '"committed" or "none"'
        schema:
          type: string
          title: Payment Filter
      - name: from_date
        in: query
        required: false
        schema:
          type: string
          title: From Date
      - name: to_date
        in: query
        required: false
        schema:
          type: string
          title: To Date
      - name: exclude_inbound
        in: query
        required: false
        description: Opt-in. Drops inbound calls from the workbook.
        schema:
          type: boolean
          default: false
          title: Exclude Inbound
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: The .xlsx workbook
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
        '400':
          description: Invalid campaign_ids format
          content:
            application/json:
              schema: {}
        '401':
          description: Not authenticated
          content:
            application/json:
              schema: {}
        '404':
          description: No data to export
          content:
            application/json:
              schema: {}
        '429':
          description: Export cap exceeded (60/hour per organization)
          content:
            application/json:
              schema: {}
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema: {}
  /api/campaigns/{campaign_id}/skip-log:
    get:
      tags:
      - Campaigns
      summary: Skip log for a campaign
      description: |-
        Return the contacts that were skipped when this campaign expanded its
        dial queue, plus a grouped count per skip reason.

        Answers "I uploaded 5,000 contacts and see 4,200 calls — what happened
        to the other 800?".

        ## Authorization
        - API Key (`X-API-Key`) or session token
        - Any authenticated user in the campaign's organization

        ## Response
        - **rows**: per-contact detail, newest first, capped by `limit`
        - **counts**: aggregate count per reason — NOT capped by `limit`
        - **total_skipped**: sum of `counts`

        ## Reason values
        `promise_future`, `promise_recent`, `callback_pending`,
        `cooldown_answered`, `refused`, `unreachable`, `retry_backoff`
      operationId: get_campaign_skip_log_api_campaigns__campaign_id__skip_log_get
      security:
      - HTTPBearer: []
      parameters:
      - name: campaign_id
        in: path
        required: true
        schema:
          type: integer
          title: Campaign Id
      - name: limit
        in: query
        required: false
        description: Max detail rows returned. Does not affect counts.
        schema:
          type: integer
          default: 500
          title: Limit
      - name: X-API-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '401':
          description: Not authenticated
          content:
            application/json:
              schema: {}
        '403':
          description: Not authorized for this campaign
          content:
            application/json:
              schema: {}
        '404':
          description: Campaign not found in your organization
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    AnalyticsStatsResponse:
      properties:
        success:
          type: boolean
          title: Success
        stats:
          additionalProperties: true
          type: object
          title: Stats
      type: object
      required:
      - success
      - stats
      title: AnalyticsStatsResponse
      description: Analytics statistics response.
      example:
        stats:
          avg_duration: 36.5
          negative_sentiment: 150
          neutral_sentiment: 350
          payment_commitments: 45
          positive_sentiment: 750
          total_calls: 1250
          total_committed_amount: 567000.0
          total_duration: 45600
        success: true
    Body_start_campaign_upload_api_campaign_start_upload_post:
      properties:
        file:
          type: string
          format: binary
          title: File
        name:
          type: string
          title: Name
        agent_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Agent Id
        campaign_type:
          type: string
          title: Campaign Type
          default: collections
        customer_email:
          anyOf:
          - type: string
          - type: 'null'
          title: Customer Email
        routing_flow_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Routing Flow Id
        ignore_history_override:
          type: boolean
          title: Ignore History Override
          default: false
      type: object
      required:
      - file
      - name
      title: Body_start_campaign_upload_api_campaign_start_upload_post
    CallDetailsResponse:
      properties:
        success:
          type: boolean
          title: Success
        call:
          additionalProperties: true
          type: object
          title: Call
      type: object
      required:
      - success
      - call
      title: CallDetailsResponse
      description: Single call details response.
      example:
        call:
          analysis:
            payment_promised: true
            sentiment: positive
          call_id: call_abc123
          recording_url: https://recordings.example.com/call_abc123.mp3
          transcript: Full conversation transcript...
        success: true
    CallsListResponse:
      properties:
        success:
          type: boolean
          title: Success
        calls:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Calls
        pagination:
          $ref: '#/components/schemas/PaginationInfo'
      type: object
      required:
      - success
      - calls
      - pagination
      title: CallsListResponse
      description: Paginated list of calls response.
      example:
        calls:
        - call_id: call_abc123
          call_outcome: promised_payment
          campaign_name: January Campaign
          client_name: Juan Pérez
          sentiment: positive
        pagination:
          count: 100
          has_more: true
          limit: 100
        success: true
    CampaignListResponse:
      properties:
        success:
          type: boolean
          title: Success
        campaigns:
          items:
            $ref: '#/components/schemas/CampaignResponse'
          type: array
          title: Campaigns
      type: object
      required:
      - success
      - campaigns
      title: CampaignListResponse
      description: List of campaigns response.
      example:
        campaigns:
        - active_calls: 0
          completed_calls: 150
          id: 1
          name: January Campaign
          status: completed
          total_leads: 150
        success: true
    CampaignOperationRequest:
      properties:
        campaign_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Campaign Id
          description: Campaign ID (optional - uses organization's active campaign
            if not provided)
      type: object
      title: CampaignOperationRequest
      description: Generic campaign operation request (pause/resume/stop/reset).
      example:
        campaign_id: 1
    CampaignResponse:
      properties:
        id:
          type: integer
          title: Id
        name:
          type: string
          title: Name
        status:
          type: string
          title: Status
          description: 'Campaign status: idle, running, paused, stopped, completed'
        total_leads:
          type: integer
          title: Total Leads
        completed_calls:
          type: integer
          title: Completed Calls
        active_calls:
          type: integer
          title: Active Calls
        agent_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Agent Id
        organization_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Organization Id
        created_at:
          type: string
          title: Created At
        started_at:
          anyOf:
          - type: string
          - type: 'null'
          title: Started At
        stopped_at:
          anyOf:
          - type: string
          - type: 'null'
          title: Stopped At
      type: object
      required:
      - id
      - name
      - status
      - total_leads
      - completed_calls
      - active_calls
      - created_at
      title: CampaignResponse
      description: Campaign response.
      example:
        active_calls: 3
        agent_id: agent_abc123def456
        completed_calls: 45
        created_at: '2024-02-10T08:00:00'
        id: 1
        name: January Debt Collection
        organization_id: 1
        started_at: '2024-02-10T08:05:00'
        status: running
        total_leads: 150
    CampaignScheduleRequest:
      properties:
        name:
          type: string
          maxLength: 255
          minLength: 1
          title: Name
        csv_content:
          type: string
          maxLength: 10000000
          title: Csv Content
          description: CSV file content as string (required)
        agent_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Agent Id
        customer_email:
          anyOf:
          - type: string
          - type: 'null'
          title: Customer Email
        campaign_type:
          type: string
          title: Campaign Type
          description: 'Campaign type: ''collections'', ''sdr'', or ''cx'''
          default: collections
        scheduled_at:
          type: string
          format: date-time
          title: Scheduled At
          description: ISO 8601 datetime when the campaign should start
        routing_flow_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Routing Flow Id
          description: Optional routing flow ID. When provided, the flow selects the
            agent per-call at fire time — agent_id is not required.
      type: object
      required:
      - name
      - csv_content
      - scheduled_at
      title: CampaignScheduleRequest
      description: Schedule a campaign to run at a future time.
    CampaignScheduleResponse:
      properties:
        success:
          type: boolean
          title: Success
        campaign_id:
          type: integer
          title: Campaign Id
        scheduled_at:
          type: string
          title: Scheduled At
        total_leads:
          type: integer
          title: Total Leads
      type: object
      required:
      - success
      - campaign_id
      - scheduled_at
      - total_leads
      title: CampaignScheduleResponse
      description: Response after scheduling a campaign.
    CampaignStartRequest:
      properties:
        name:
          type: string
          maxLength: 255
          minLength: 1
          title: Name
          description: Campaign name
        csv_path:
          anyOf:
          - type: string
          - type: 'null'
          title: Csv Path
          description: Path to local CSV file (for internal use)
        csv_content:
          anyOf:
          - type: string
            maxLength: 10000000
          - type: 'null'
          title: Csv Content
          description: CSV file content as string (uploaded file, max 10MB)
        agent_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Agent Id
          description: AI agent ID from /api/agents endpoint (required if no default
            agent configured)
        customer_email:
          anyOf:
          - type: string
          - type: 'null'
          title: Customer Email
          description: Email for notifications
        campaign_type:
          type: string
          title: Campaign Type
          description: 'Campaign type: ''collections'' for debt collection, ''sdr''
            for sales development, or ''cx'' for customer experience'
          default: collections
        routing_flow_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Routing Flow Id
          description: Optional routing flow ID to associate with this campaign. CSV
            columns will be validated against the flow's required fields.
        ignore_history_override:
          type: boolean
          title: Ignore History Override
          description: Admin-only. When True, skip the campaign history filter (cooldown,
            refused, callback_pending, etc.) and dial all leads.
          default: false
      type: object
      required:
      - name
      title: CampaignStartRequest
      description: Start campaign request.
      example:
        agent_id: agent_abc123def456
        campaign_type: collections
        csv_content: 'to_number,lead_id,client_name,debt

          +525512345678,lead_001,Juan Pérez,15000.00'
        customer_email: manager@example.com
        name: January Debt Collection
    CampaignStartResponse:
      properties:
        success:
          type: boolean
          title: Success
        campaign_id:
          type: integer
          title: Campaign Id
        total_leads:
          type: integer
          title: Total Leads
        agent_id:
          type: string
          title: Agent Id
        customer_email:
          anyOf:
          - type: string
          - type: 'null'
          title: Customer Email
        organization_id:
          type: integer
          title: Organization Id
        message:
          type: string
          title: Message
      type: object
      required:
      - success
      - campaign_id
      - total_leads
      - agent_id
      - organization_id
      - message
      title: CampaignStartResponse
      description: Campaign start response.
      example:
        agent_id: agent_abc123def456
        campaign_id: 1
        message: Campaign 'January Debt Collection' started
        organization_id: 1
        success: true
        total_leads: 150
    CampaignStatusResponse:
      properties:
        success:
          type: boolean
          title: Success
        status:
          additionalProperties: true
          type: object
          title: Status
        campaign:
          anyOf:
          - additionalProperties: true
            type: object
          - type: 'null'
          title: Campaign
        global_status:
          additionalProperties: true
          type: object
          title: Global Status
      type: object
      required:
      - success
      - status
      - global_status
      title: CampaignStatusResponse
      description: Campaign status response.
      example:
        campaign:
          completed_calls: 102
          id: 1
          name: January Campaign
          total_leads: 150
        global_status:
          active_campaigns: 2
          total_active_calls: 5
        status:
          active_calls: 3
          campaign_id: 1
          queue_size: 45
          state: running
        success: true
    ErrorResponse:
      properties:
        success:
          type: boolean
          title: Success
          description: Always false for errors
          default: false
        error:
          type: string
          title: Error
          description: Error message
        detail:
          anyOf:
          - type: string
          - type: 'null'
          title: Detail
          description: Additional error details
        error_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Error Id
          description: Unique error ID for tracking
      type: object
      required:
      - error
      title: ErrorResponse
      description: Error response.
      example:
        detail: Campaign with ID 123 does not exist
        error: Resource not found
        success: false
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    LoginRequest:
      properties:
        email:
          type: string
          format: email
          title: Email
          description: User's email address
        password:
          type: string
          minLength: 8
          title: Password
          description: User's password (minimum 8 characters)
      type: object
      required:
      - email
      - password
      title: LoginRequest
      description: Login request body.
      example:
        email: user@example.com
        password: SecurePassword123!
    LoginResponse:
      properties:
        success:
          type: boolean
          title: Success
          description: Whether the login was successful
        access_token:
          type: string
          title: Access Token
          description: JWT access token (24h expiry)
        user:
          $ref: '#/components/schemas/UserResponse'
          description: Authenticated user details
      type: object
      required:
      - success
      - access_token
      - user
      title: LoginResponse
      description: Login response with tokens and user info.
      example:
        access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
        success: true
        user:
          created_at: '2024-01-15T10:30:00'
          email: user@example.com
          id: 1
          is_active: true
          name: John Doe
          organization_id: 1
          organization_name: Acme Corp
          role: user
    PaginationInfo:
      properties:
        limit:
          type: integer
          title: Limit
          description: Maximum items per page
        count:
          type: integer
          title: Count
          description: Number of items in current page
        has_more:
          type: boolean
          title: Has More
          description: Whether more items are available
        next_cursor:
          anyOf:
          - type: string
          - type: 'null'
          title: Next Cursor
          description: Opaque cursor for next page (null if no more pages)
      type: object
      required:
      - limit
      - count
      - has_more
      title: PaginationInfo
      description: Pagination metadata (keyset-based).
      example:
        count: 100
        has_more: true
        limit: 100
        next_cursor: eyJ0cyI6IjIwMjYtMDEtMDFUMDA6MDA6MDBaIiwiaWQiOiJjYWxsX2FiYzEyMyJ9
    SuccessResponse:
      properties:
        success:
          type: boolean
          title: Success
          description: Whether the operation was successful
        message:
          anyOf:
          - type: string
          - type: 'null'
          title: Message
          description: Optional success message
      type: object
      required:
      - success
      title: SuccessResponse
      description: Generic success response.
      example:
        message: Operation completed successfully
        success: true
    UserResponse:
      properties:
        id:
          type: integer
          title: Id
        email:
          type: string
          title: Email
        name:
          type: string
          title: Name
        organization_id:
          type: integer
          title: Organization Id
        organization_name:
          anyOf:
          - type: string
          - type: 'null'
          title: Organization Name
        organization_slug:
          anyOf:
          - type: string
          - type: 'null'
          title: Organization Slug
        role:
          type: string
          title: Role
        is_active:
          type: boolean
          title: Is Active
        last_login:
          anyOf:
          - type: string
          - type: 'null'
          title: Last Login
        created_at:
          type: string
          title: Created At
      type: object
      required:
      - id
      - email
      - name
      - organization_id
      - role
      - is_active
      - created_at
      title: UserResponse
      description: User response (excludes password).
      example:
        created_at: '2024-01-15T10:30:00'
        email: user@example.com
        id: 1
        is_active: true
        last_login: '2024-02-10T08:30:00'
        name: John Doe
        organization_id: 1
        organization_name: Acme Corp
        organization_slug: acme-corp
        role: user
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer
