---
openapi: 3.0.1

info:
  title: Hum API
  version: 1.7.0
  description: |
    The Hum API provides a comprehensive solution for discovering and comparing Internet service providers (ISPs) at specific addresses. 
    By leveraging AI-powered agents, the API normalizes addresses, identifies available providers, and returns detailed information 
    about service plans, pricing, and technology options. This makes it an ideal solution for businesses and applications that need 
    to help users find and compare Internet service options in their area.

    The API provides endpoints for creating and managing sessions, as well as verifying the connection to the API. Each session represents a service address lookup and its provider results.

    ## API Versioning
    This API uses semantic versioning. The current version is v1.6.0. For breaking changes, the major version will be incremented.
    
    ## Rate Limiting
    API requests are protected by a global limit of 100 requests per second per client IP. Analytics endpoints have an additional limit of 60 requests per minute per client IP. When a limit is exceeded, the API returns HTTP 429 with a `Retry-After: 60` header. No other rate-limit headers are sent.

    ## Authentication
    All endpoints require Bearer token authentication. Tokens must be included in the Authorization header.

    ## Error Handling
    Callers should send `Accept: application/json`. Errors handled by API controllers use a common JSON envelope with `message`, `request_status`, and `request_id`; validation errors may also include `errors`. The API emits 400, 401, 403, 404, 406, 410, 418, 422, 429, 500, and 503 statuses. Use the HTTP status code, not `request_status`, for control flow.

    The `request_id` is a best-effort support reference persisted with the API log. Request logging deliberately cannot break the request, so a logging failure can leave no matching row. Include the request timestamp when reporting an error so support can bound the lookup by date.

    Exceptions raised before an API controller handles the request, including unmatched routes and some middleware failures, may use Rails' `PublicExceptions` shape instead of the common envelope. `Accept: application/json` guarantees JSON for those responses; a wildcard or missing `Accept` header may receive HTML.

    One controller-level exception deliberately has a different body: when address validation is temporarily unavailable during session creation, HTTP 503 returns `{ "address_validation_unavailable": true }` and a `Retry-After` header.
  termsOfService: https://www.letshum.com/terms-of-service
  contact:
    name: Hum API Support
    url: https://docs.letshum.com
    email: support@letshum.com
  license:
    name: Proprietary
    url: https://www.letshum.com/terms-of-service

paths:
  /sessions:
    summary: Session Management
    description: |
      The session endpoint provides comprehensive session management capabilities for the Hum API. 
      A session represents a single service address lookup and maintains the state of various Hum AI agents working on that address.
      
      ## Session Lifecycle
      1. Create a session by POSTing service address details
      2. Use the session token to retrieve the session or its Internet services
      3. Close the session with DELETE when finished
      
      ## Important Notes
      - Sessions remain open until they are closed with DELETE
      - All requests require a valid bearer token
      - The session token is part of the URL path for all requests after creation

    post:
      operationId: createSession
      security:
        - bearerAuth: []
      tags:
        - Sessions
      summary: Create New Session
      description: |
        Creates a new session for processing a service address. This is typically the first endpoint called when starting 
        a new address lookup flow.

        ## Request Flow
        1. Submit service address components
        2. Receive a session token
        3. Use session token in the URL path for subsequent requests

        ## Processing
        During session creation, Hum:
        - Validates and normalizes the address
        - Geocodes the location
        - Identifies available service providers
        - Gathers plan and pricing information

        ## Response Handling
        - 201: Session created successfully with available provider results in `data`
        - 422: Invalid address components, check error details
        - 429: Rate limit exceeded; retry after the delay in `Retry-After`
        - 503: Address validation is temporarily unavailable; retry after the delay in `Retry-After`

        ## Important Notes
        - The session token is part of the URL path for all requests after creation
        - The session token is required for all subsequent requests
        - Sessions remain open until they are closed with DELETE
        - Rate limits apply to all requests

        ## Example Usage
        Provide address using one of: **(street1 and zip)**, **(street1, city, and state)**, or **(street1, city, and zip)**. State is optional when zip is present.
        ```json
        POST /sessions
        {
          "street1": "29090 Tiffany Drive E",
          "zip": "48034"
        }
        ```
        Or with city and state, or street+city+zip (no state):
        ```json
        POST /sessions
        {
          "street1": "29090 Tiffany Drive E",
          "city": "Southfield",
          "state": "MI",
          "zip": "48034"
        }
        ```
      requestBody:
        required: true
        description: |
          Service address for the session. Provide one of: **street1 and zip**; **street1, city, and state**; or **street1, city, and zip**. State is optional when zip is present. street2, latitude, longitude, and campaign_id are optional.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/session_params'
            examples:
              street_and_zip:
                summary: Street and ZIP only
                description: Minimum address using street and ZIP code (no city or state required)
                value:
                  street1: "29090 Tiffany Drive E"
                  zip: "48034"
              street_city_zip:
                summary: Street, city, and ZIP (no state)
                description: Address with street, city, and zip; state optional when zip is present
                value:
                  street1: "275 N Berkshire Rd"
                  city: "Bloomfield Hills"
                  zip: "48302"
              basic_address:
                summary: Full address (street, city, state, zip)
                description: Standard address with city and state
                value:
                  street1: "29090 Tiffany Drive E"
                  city: "Southfield"
                  state: "MI"
                  zip: "48034"
              address_with_unit:
                summary: Address with unit/apartment
                description: Address that includes a unit or apartment number
                value:
                  street1: "123 Main Street"
                  street2: "Apt 4B"
                  city: "Denver"
                  state: "CO"
                  zip: "80202"
              with_campaign_tracking:
                summary: Address with campaign tracking
                description: Address lookup with campaign tracking for analytics
                value:
                  street1: "851 Ashwood Dr"
                  city: "Cookeville"
                  state: "TN"
                  zip: "38501"
                  campaign_id: "bucket_of_winning"
              with_coordinates:
                summary: Address with latitude and longitude
                description: Address lookup with precise coordinates for enhanced geocoding
                value:
                  street1: "29090 Tiffany Drive E"
                  city: "Southfield"
                  state: "MI"
                  zip: "48034"
                  latitude: 42.50189
                  longitude: -83.29528
      responses:
        "201":
          description: Successful session creation.
          headers:
            session_token:
              $ref: '#/components/headers/session_token'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    $ref: '#/components/schemas/message'
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  qualify_status:
                    $ref: '#/components/schemas/qualify_status'
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/provider_offering'
                  meta:
                    $ref: '#/components/schemas/meta'
                required:
                  - message
                  - request_status
                  - data
                  - meta
              examples:
                successful_creation:
                  summary: Successful session creation
                  description: Session created with multiple providers found
                  value:
                    message: "Session created successfully."
                    request_status: "ok"
                    data:
                      - provider_id: "130317"
                        provider_name: "Xfinity"
                        provider_icon: null
                        provider_logo: "https://cdn.example.com/providers/xfinity-logo.png"
                        telephone: "+18332981431"
                        url: "https://affiliate.example.com/xfinity?session=SESSION_TOKEN"
                        min_plan_price:
                          currency: "USD"
                          amount_cents: 4000
                        button_label: "xfinity.com"
                        provider_promo: {}
                        url_promo: {}
                        offerings:
                          - technology: "Cable"
                            max_download_speed: 1200
                            max_upload_speed: 35
                        product_catalog:
                          - category: "internet"
                            category_name: "Internet Service"
                            category_description: "High-speed internet access plans"
                            products:
                              - id: "a5d1cba2-10f8-4469-8296-9b1b02f83516"
                                sku: "130317-INT-CBL-02"
                                name: "300 Mbps"
                                category: "internet"
                                category_name: "Internet Service"
                                technology: "cable"
                                position: 2
                                select_type: "radio"
                                description: "Great for everyday working, streaming, and learning. Price guaranteed for 60 months. No contract required."
                                download_speed: "300"
                                upload_speed: "100"
                                data_limit: "Unlimited"
                                channel_count: 0
                                streaming_apps: []
                                is_required_to_checkout: true
                                is_contract_required: false
                                is_modem_router_included: true
                                is_bundle_qualifier: false
                                bundle_discounts: {}
                                is_local_checkout: true
                                required_with_plans: []
                                included_with_plans: []
                                only_available_with_plans: []
                                initial_term_discount_months: 60
                                second_term_discount_months: null
                                hum_rank: 69
                                max_quantity: 1
                                product_promo: {}
                                info: "5-year price lock guarantee"
                                pricing:
                                  extra_data_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  professional_installation_fee:
                                    amount_cents: 5000
                                    currency: "USD"
                                  self_installation_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  activation_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  initial_term_discount:
                                    amount_cents: 1500
                                    currency: "USD"
                                  second_term_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  third_term_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  autopay_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  paperless_billing_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  combined_autopay_paperless_discount:
                                    amount_cents: 1000
                                    currency: "USD"
                                  net_monthly_price:
                                    amount_cents: 5500
                                    currency: "USD"
                                  gross_monthly_fee:
                                    amount_cents: 8000
                                    currency: "USD"
                          - category: "internet_add_on"
                            category_name: "Internet Add-On"
                            category_description: "Additional internet services"
                            products:
                              - id: "d5ff2d5a-2145-4c5e-95c5-05ed78fc473a"
                                sku: "130317-INT-AO-01"
                                name: "Xfinity WiFi Gateway (modem + router)"
                                category: "internet_add_on"
                                category_name: "Internet Add-On"
                                technology: "not_applicable"
                                position: 1
                                select_type: "radio"
                                description: "The most reliable WiFi with our best equipment. Free Advanced Security. Parental controls."
                                download_speed: null
                                upload_speed: null
                                data_limit: "0 GB"
                                channel_count: 0
                                streaming_apps: []
                                is_required_to_checkout: false
                                is_contract_required: false
                                is_modem_router_included: false
                                is_bundle_qualifier: false
                                bundle_discounts: {}
                                is_local_checkout: true
                                required_with_plans: []
                                included_with_plans:
                                  - "130317-INT-CBL-01"
                                  - "130317-INT-CBL-02"
                                  - "130317-INT-CBL-03"
                                only_available_with_plans:
                                  - "130317-INT-CBL-01"
                                  - "130317-INT-CBL-02"
                                  - "130317-INT-CBL-03"
                                initial_term_discount_months: 0
                                second_term_discount_months: null
                                hum_rank: null
                                max_quantity: 1
                                product_promo: {}
                                info: ""
                                pricing:
                                  extra_data_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  professional_installation_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  self_installation_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  activation_fee:
                                    amount_cents: 0
                                    currency: "USD"
                                  initial_term_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  second_term_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  third_term_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  autopay_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  paperless_billing_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  combined_autopay_paperless_discount:
                                    amount_cents: 0
                                    currency: "USD"
                                  net_monthly_price:
                                    amount_cents: 0
                                    currency: "USD"
                                  gross_monthly_fee:
                                    amount_cents: 0
                                    currency: "USD"
                    meta:
                      session_token: "SESSION_TOKEN_PLACEHOLDER"
                      session_status: "open"
                      session_params:
                        street1: "29090 Tiffany Dr E"
                        street2: "Apt 4B"
                        city: "Southfield"
                        state: "MI"
                        zip: "48034"
                        latitude: "42.50189"
                        longitude: "-83.29528"
                        campaign_id: null
                      service_address: "29090 Tiffany Dr E Apt 4B, Southfield, MI 48034-4540"
                      mdu: true
                      agent_status:
                        geocoding: "matched"
                        internet: "matched"
                        checkout: "pending"
                      created_at: "2026-07-13T12:26:15.618-04:00"
                      updated_at: "2026-07-13T12:26:15.618-04:00"
                      responded_at: "2026-07-13T16:26:16.278Z"
                      hum_data_set: "26011015"
        "400":
          $ref: '#/components/responses/400_bad_request'
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "422":
          $ref: '#/components/responses/422_unprocessable'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'
        "503":
          $ref: '#/components/responses/503_address_validation_unavailable'

  /sessions/{token}:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/session_token'
        description: The session token identifying the specific session

    get:
      operationId: getSession
      security:
        - bearerAuth: []
      tags:
        - Sessions
      summary: Get Session Status
      description: |
        Retrieves an open session. Provider results are usually returned by `POST /sessions`, but when `qualify_status` is `pending` or `retry_later`, poll `GET /sessions/{token}/services/internet` until the status resolves.

        ## Status Codes
        - 200: Session is open
        - 400: Session token is invalid
        - 410: Session is closed

        ## Response Data
        - `data.session_token`: The session token only
        - `meta.session_params`: Normalized address values
        - `meta.service_address`: Formatted service address
        - `meta.agent_status`: Agent status values

        ## Important Notes
        - The session token is part of the URL path for all requests after creation
        - Sessions remain open until they are closed with DELETE

        ## Example Response
        ```json
        {
          "message": "Session agents have successfully matched the service address. Please proceed.",
          "request_status": "ok",
          "data": {
            "session_token": "SESSION_TOKEN_PLACEHOLDER"
          },
          "meta": {
            "session_token": "SESSION_TOKEN_PLACEHOLDER",
            "session_status": "open",
            "session_params": {
              "street1": "29090 Tiffany Dr E",
              "street2": "Apt 4B",
              "city": "Southfield",
              "state": "MI",
              "zip": "48034",
              "latitude": "42.50189",
              "longitude": "-83.29528",
              "campaign_id": null
            },
            "service_address": "29090 Tiffany Dr E Apt 4B, Southfield, MI 48034-4540",
            "mdu": true,
            "agent_status": {
              "geocoding": "matched",
              "internet": "matched",
              "checkout": "pending"
            },
            "created_at": "2026-07-13T12:26:15.618-04:00",
            "updated_at": "2026-07-13T12:26:15.618-04:00",
            "responded_at": "2026-07-13T16:26:16.900Z",
            "hum_data_set": "26011015"
          }
        }
        ```
      responses:
        "200":
          description: Session data retrieved successfully
          headers:
            session_token:
              $ref: '#/components/headers/session_token'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    $ref: '#/components/schemas/message'
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  qualify_status:
                    $ref: '#/components/schemas/qualify_status'
                  data:
                    $ref: '#/components/schemas/session_token_data'
                  meta:
                    $ref: '#/components/schemas/meta'
                required:
                  - message
                  - request_status
                  - data
                  - meta
        "400":
          $ref: '#/components/responses/400_bad_request'
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "410":
          $ref: '#/components/responses/410_gone'
        "422":
          $ref: '#/components/responses/422_unprocessable'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

    delete:
      operationId: closeSession
      security:
        - bearerAuth: []
      tags:
        - Sessions
      summary: Close Session
      description: |
        Closes an active session. Sessions remain open until this endpoint is called.

        ## Closing Behavior
        - The response returns the session envelope with `meta.session_status` set to `closed`
        - Subsequent operations on the closed session return HTTP 410

        ## When to Close
        - After completing service lookup
        - When switching to a different address
        - After receiving final results
        - When abandoning a search

        ## Important Notes
        - A closed session cannot be reopened
        - Create a new session to perform another lookup
      responses:
        "200":
          description: Session closed successfully
          headers:
            session_token:
              $ref: '#/components/headers/session_token'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Session closed successfully.
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  data:
                    $ref: '#/components/schemas/session_token_data'
                  meta:
                    $ref: '#/components/schemas/meta'
                required:
                  - message
                  - request_status
                  - data
                  - meta
              example:
                message: "Session closed successfully."
                request_status: "ok"
                data:
                  session_token: "SESSION_TOKEN_PLACEHOLDER"
                meta:
                  session_token: "SESSION_TOKEN_PLACEHOLDER"
                  session_status: "closed"
                  session_params:
                    street1: "29090 Tiffany Dr E"
                    street2: null
                    city: "Southfield"
                    state: "MI"
                    zip: "48034"
                    latitude: "42.50189"
                    longitude: "-83.29528"
                    campaign_id: null
                  service_address: "29090 Tiffany Dr E, Southfield, MI 48034-4540"
                  mdu: false
                  agent_status:
                    geocoding: "matched"
                    internet: "matched"
                    checkout: "pending"
                  created_at: "2026-07-13T12:26:15.618-04:00"
                  updated_at: "2026-07-13T12:27:33.031-04:00"
                  responded_at: "2026-07-13T16:27:33.185Z"
                  hum_data_set: "26011015"
        "400":
          $ref: '#/components/responses/400_bad_request'
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "410":
          $ref: '#/components/responses/410_gone'
        "422":
          $ref: '#/components/responses/422_unprocessable'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

  /sessions/{token}/services/internet:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/session_token'
        description: The session token identifying the specific session
    get:
      operationId: getInternetServices
      security:
        - bearerAuth: []
      tags:
        - Service Availability
      summary: Get Internet Service Availability
      description: |
        Retrieves available Internet service providers and their offerings for the service address associated with 
        the current session.

        ## Data Provided
        - Internet Service Providers (ISPs)
        - Available plans and pricing
        - Technology types (Fiber, Cable, DSL, etc.)
        - Maximum speeds
        - Provider contact information
        - Checkout URLs

        ## Data Freshness
        Provider and product data comes from the current Hum data set identified by `meta.hum_data_set`.

        ## Example Response
        ```json
        {
          "message": "Service coverage for 29090 TIFFANY DR E, SOUTHFIELD, MI 48034",
          "request_status": "ok",
          "data": [
            {
              "provider_id": "130317",
              "provider_name": "Xfinity",
              "telephone": "+18332981431",
              "provider_icon": null,
              "provider_logo": "https://cdn.example.com/providers/xfinity-logo.png",
              "url": "https://affiliate.example.com/xfinity?session=SESSION_TOKEN",
              "button_label": "xfinity.com",
              "min_plan_price": {
                "amount_cents": 4000,
                "currency": "USD"
              },
              "provider_promo": {},
              "url_promo": {},
              "offerings": [
                {
                  "technology": "Cable",
                  "max_download_speed": 1200,
                  "max_upload_speed": 35
                }
              ],
              "product_catalog": []
            }
          ],
          "meta": {
            "session_token": "SESSION_TOKEN_PLACEHOLDER",
            "session_status": "open",
            "session_params": {
              "street1": "29090 Tiffany Dr E",
              "street2": null,
              "city": "Southfield",
              "state": "MI",
              "zip": "48034",
              "latitude": "42.50189",
              "longitude": "-83.29528",
              "campaign_id": null
            },
            "service_address": "29090 Tiffany Dr E, Southfield, MI 48034-4540",
            "mdu": false,
            "agent_status": {
              "geocoding": "matched",
              "internet": "matched",
              "checkout": "pending"
            },
            "created_at": "2026-07-13T12:26:15.618-04:00",
            "updated_at": "2026-07-13T12:26:15.618-04:00",
            "responded_at": "2026-07-13T16:26:17.471Z",
            "hum_data_set": "26011015"
          }
        }
        ```

        Note: Some providers may return an empty `product_catalog` array.
      responses:
        "200":
          description: Internet service providers, available plans, and logos/urls for a given service address.
          headers:
            session_token:
              $ref: '#/components/headers/session_token'
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    $ref: '#/components/schemas/message'
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  qualify_status:
                    $ref: '#/components/schemas/qualify_status'
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/provider_offering'
                  meta:
                    $ref: '#/components/schemas/meta'
                required:
                  - message
                  - request_status
                  - data
                  - meta
        "400":
          $ref: '#/components/responses/400_bad_request'
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "410":
          $ref: '#/components/responses/410_gone'
        "422":
          $ref: '#/components/responses/422_unprocessable'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

  /ping:
    get:
      operationId: healthCheck
      security:
        - bearerAuth: []
      tags:
        - Health Check
      summary: API Health Check
      description: |
        Simple health check endpoint to verify API availability and authentication status. Use this endpoint 
        for monitoring and to validate API tokens.

        ## Use Cases
        - Monitoring API health
        - Validating API tokens
        - Testing CORS configuration
        - Checking rate limits

        ## Response Times
        - Expected: < 100ms
        - Warning: > 500ms
        - Critical: > 1000ms

        ## Monitoring Guidelines
        - Poll every 60 seconds
        - Implement circuit breaker pattern
        - Track response times
        - Monitor error rates

        ## Example Response
        ```json
        {
          "message": "🎤 We're humming along!",
          "timestamp": "2026-07-13T16:26:17.874Z"
        }
        ```
      responses:
        "200":
          description: API is available, and token is valid
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: "🎤 We're humming along!"
                    description: A message confirming the API is available.
                  timestamp:
                    $ref: '#/components/schemas/timestamp'
                required:
                  - message
                  - timestamp
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "429":
          $ref: '#/components/responses/429_rate_limit'

  /service_providers:
    get:
      operationId: getServiceProviders
      security:
        - bearerAuth: []
      tags:
        - Informational
      summary: Get FCC Service Providers
      description: |
        Retrieves a comprehensive list of FCC service providers and their supplemental brands. 
        This endpoint provides access to the complete database of Internet service providers 
        that are tracked by the FCC and used by Hum for service availability lookups.

        ## Use Cases
        - Building provider selection interfaces
        - Validating provider names and IDs
        - Understanding provider coverage and scale
        - Integration with external systems

        ## Data Structure
        The response includes both primary FCC providers and their supplemental brands:
        - **Primary Providers**: Main FCC-registered service providers
        - **Supplemental Brands**: Subsidiary brands and service lines under parent providers
        - **Total Residential Units**: Coverage metrics for each provider

        ## Provider Types
        - `fcc_provider`: Primary FCC-registered service provider
        - `supplemental_brand`: Supplemental brand that inherits coverage from parent provider
        - Supplemental brands are grouped immediately after their parent provider

        ## Response Format
        Providers are ordered by total residential units (largest first) with supplemental 
        brands immediately following their parent provider in the list.

        ## Example Response
        ```json
        {
          "providers": [
            {
              "id": "130403",
              "name": "T-Mobile",
              "fcc_name": "T-Mobile USA, Inc.",
              "total_residential_units": 97951103,
              "type": "fcc_provider"
            },
            { 
                "id": "130077",
                "name": "AT&T",
                "fcc_name": "AT&T Inc.",
                "total_residential_units": 80864481,
                "type": "fcc_provider"
            },
            {
                "id": "130317",
                "name": "Xfinity",
                "fcc_name": "Comcast Corporation",
                "total_residential_units": 57287455,
                "type": "fcc_provider"
            },
            {
                "id": "130317-1",
                "name": "Now XFinity",
                "fcc_name": "Comcast Corporation",
                "total_residential_units": 57287455,
                "type": "supplemental_brand"
            },
            {
                "id": "131425",
                "name": "Verizon",
                "fcc_name": "Verizon Communications Inc.",
                "total_residential_units": 50554772,
                "type": "fcc_provider"
            }
          ]
        }
        ```

        ## Important Notes
        - All requests require a valid bearer token
        - Response includes both active and inactive providers
        - Supplemental brands reference their parent provider's FCC name
        - Total residential units represent the parent provider's coverage
      responses:
        "200":
          description: Service providers retrieved successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    description: Array of service providers and their supplemental brands
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Unique provider identifier (FCC provider ID or synthetic ID for brands)
                          example: "12345"
                        name:
                          type: string
                          description: Display name of the provider or brand
                          example: "Comcast Cable Communications"
                        fcc_name:
                          type: string
                          description: Official FCC registered name of the parent provider
                          example: "Comcast Cable Communications, LLC"
                        total_residential_units:
                          type: integer
                          description: Total number of residential units served by the provider
                          example: 50000000
                        type:
                          type: string
                          enum: [fcc_provider, supplemental_brand]
                          description: Type of provider - 'fcc_provider' for primary FCC providers, 'supplemental_brand' for supplemental brands
                          example: "fcc_provider"
                      required:
                        - id
                        - name
                        - fcc_name
                        - total_residential_units
                required:
                  - providers
              examples:
                major_providers:
                  summary: Major Internet Service Providers
                  description: Example response showing major ISPs and their brands
                  value:
                    providers:
                      - id: "12345"
                        name: "Comcast Cable Communications"
                        fcc_name: "Comcast Cable Communications, LLC"
                        total_residential_units: 50000000
                        type: "fcc_provider"
                      - id: "12345-Xfinity"
                        name: "Xfinity"
                        fcc_name: "Comcast Cable Communications, LLC"
                        total_residential_units: 50000000
                        type: "supplemental_brand"
                      - id: "67890"
                        name: "Charter Communications"
                        fcc_name: "Charter Communications, Inc."
                        total_residential_units: 30000000
                        type: "fcc_provider"
                      - id: "67890-Spectrum"
                        name: "Spectrum"
                        fcc_name: "Charter Communications, Inc."
                        total_residential_units: 30000000
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "415":
          $ref: '#/components/responses/415_unsupported_media_type'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

  /analytics/sessions:
    summary: Partner Analytics - Sessions
    description: |
      Paginated list of sessions with order and cart progression data,
      scoped to the authenticated token's client.

      ## Data Scoping
      - Results are scoped to all tokens belonging to the authenticated client
      - Only sessions with human activity are returned
      - Optional `token_ids[]` filter must reference tokens owned by the authenticated client

      ## Move-in / Move-out Segmentation
      Every session carries a `context` of `move_in` or `move_out`, set by the token that
      created it rather than by any request parameter. Partners running both flows are
      issued a separate token per context.

      There is no `context` query parameter. To segment, either request a single token's
      traffic with `token_ids[]`, or group client-side on the `context` field.

      Move-out sessions carry the resident's destination address, not the partner's
      building. Do not treat those addresses as partner properties.

      ## Rate Limiting
      Analytics endpoints are limited to 60 requests per minute per IP.

    get:
      operationId: listAnalyticsSessions
      security:
        - bearerAuth: []
      tags:
        - Analytics
      summary: List Sessions
      description: |
        Returns a paginated list of sessions with address, cart progression, order, and commission data.
        Scoped to all tokens belonging to the authenticated client.

        ## Filtering
        - `start_date` / `end_date`: ISO 8601 date strings to bound the query window
        - `updated_since`: Return only sessions whose session or order data changed on or after this time; results are ordered by most recent change first
        - `token_ids[]`: Restrict to specific API tokens (must belong to your account)
        - `with_clicks`: When `true`, only returns sessions with click activity

        ## Pagination
        Results are paginated at 50 sessions per page. Use the `page` parameter and
        the `meta.pages` field to navigate.

        ## Syncing order status
        To keep a local copy of order status current, poll with `updated_since` set to the time of
        your last successful sync and page through all results. A session is returned whenever the
        session or its order changed, including installs, cancellations, and commission updates on
        sessions created long ago. Treat each returned row as authoritative and overwrite your stored
        copy. Do not use `start_date` as a sync cursor: it filters on session creation time, so status
        changes on previously synced sessions are never returned. Unrecognized parameters are ignored.

      parameters:
        - name: page
          in: query
          description: Page number (default 1, 50 results per page)
          required: false
          schema:
            type: integer
            minimum: 1
            default: 1
            example: 1
        - name: start_date
          in: query
          description: Filter sessions created on or after this date (ISO 8601)
          required: false
          schema:
            type: string
            format: date-time
            example: "2026-01-01T00:00:00Z"
        - name: end_date
          in: query
          description: Filter sessions created on or before this date (ISO 8601)
          required: false
          schema:
            type: string
            format: date-time
            example: "2026-03-31T23:59:59Z"
        - name: updated_since
          in: query
          description: |
            Return only sessions whose session or order data changed on or after this time
            (ISO 8601). When provided, results are ordered by most recent change first.
            Invalid datetime values are ignored.
          required: false
          schema:
            type: string
            format: date-time
            example: "2026-03-10T00:00:00Z"
        - name: token_ids[]
          in: query
          description: |
            Filter to sessions from specific API tokens. All provided token IDs
            must belong to the authenticated client or a 403 is returned.
          required: false
          schema:
            type: array
            items:
              type: string
              format: uuid
        - name: with_clicks
          in: query
          description: When true, only return sessions that have click activity
          required: false
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: Paginated list of analytics sessions
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    $ref: '#/components/schemas/message'
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/analytics_session_summary'
                  meta:
                    $ref: '#/components/schemas/analytics_pagination_meta'
                required:
                  - message
                  - request_status
                  - data
                  - meta
              examples:
                with_results:
                  summary: Sessions with order data
                  value:
                    message: "Analytics sessions"
                    request_status: "ok"
                    data:
                      - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                        created_at: "2026-03-01T12:00:00Z"
                        context: "move_in"
                        campaign_id: "spring_promo_2026"
                        address:
                          street1: "123 Main St"
                          street2: "Apt 4B"
                          city: "Detroit"
                          state: "MI"
                          zip: "48201"
                        furthest_step: 6
                        order_number: "HUM-A1B2C3D4"
                        status: "complete"
                        ordered_at: "2026-03-01T12:30:00Z"
                        installed: true
                        installed_at: "2026-03-05T14:00:00Z"
                        commission_cents: 5000
                      - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                        created_at: "2026-03-01T11:00:00Z"
                        context: "move_out"
                        campaign_id: null
                        address:
                          street1: "456 Oak Ave"
                          street2: null
                          city: "Ann Arbor"
                          state: "MI"
                          zip: "48104"
                        furthest_step: 3
                        order_number: null
                        status: null
                        ordered_at: null
                        installed: null
                        installed_at: null
                        commission_cents: null
                    meta:
                      responded_at: "2026-03-12T15:00:00Z"
                      page: 1
                      pages: 5
                      count: 237
                empty_results:
                  summary: No sessions found
                  value:
                    message: "Analytics sessions"
                    request_status: "ok"
                    data: []
                    meta:
                      responded_at: "2026-03-12T15:00:00Z"
                      page: 1
                      pages: 0
                      count: 0
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "403":
          $ref: '#/components/responses/403_forbidden'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

  /analytics/sessions/{id}:
    summary: Partner Analytics - Session Detail
    description: |
      Single session with full cart timeline, order data, and commission info.

    get:
      operationId: getAnalyticsSession
      security:
        - bearerAuth: []
      tags:
        - Analytics
      summary: Get Session Detail
      description: |
        Returns a single session with address, order, commission, and full cart progression timeline.
        The session must belong to the authenticated client.
        The path accepts either the session UUID returned by the analytics index endpoint or
        the session token returned by session create.

      parameters:
        - name: id
          in: path
          description: Session UUID returned by analytics index or session token returned by session create
          required: true
          schema:
            type: string
            example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
      responses:
        "200":
          description: Session detail with cart progression
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    $ref: '#/components/schemas/message'
                  request_status:
                    $ref: '#/components/schemas/request_status'
                  data:
                    $ref: '#/components/schemas/analytics_session_detail'
                  meta:
                    type: object
                    properties:
                      responded_at:
                        $ref: '#/components/schemas/responded_at'
                required:
                  - message
                  - request_status
                  - data
                  - meta
              examples:
                with_order:
                  summary: Session with order and cart progression
                  value:
                    message: "Analytics session"
                    request_status: "ok"
                    data:
                      id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                      created_at: "2026-03-01T12:00:00Z"
                      context: "move_in"
                      campaign_id: "spring_promo_2026"
                      address:
                        street1: "123 Main St"
                        street2: "Apt 4B"
                        city: "Detroit"
                        state: "MI"
                        zip: "48201"
                      furthest_step: 6
                      order:
                        order_number: "HUM-A1B2C3D4"
                        status: "complete"
                        ordered_at: "2026-03-01T12:30:00Z"
                        installed: true
                        installed_at: "2026-03-05T14:00:00Z"
                        commission_cents: 5000
                      cart_progression:
                        - step: 1
                          step_name: "plan_selection"
                          timestamp: "2026-03-01T12:05:00Z"
                        - step: 2
                          step_name: "internet_addons"
                          timestamp: "2026-03-01T12:07:00Z"
                        - step: 6
                          step_name: "customer_info"
                          timestamp: "2026-03-01T12:15:00Z"
                    meta:
                      responded_at: "2026-03-12T15:00:00Z"
                without_order:
                  summary: Session without an order
                  value:
                    message: "Analytics session"
                    request_status: "ok"
                    data:
                      id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                      created_at: "2026-03-01T11:00:00Z"
                      context: "move_out"
                      campaign_id: null
                      address:
                        street1: "456 Oak Ave"
                        street2: null
                        city: "Ann Arbor"
                        state: "MI"
                        zip: "48104"
                      furthest_step: 3
                      order: null
                      cart_progression:
                        - step: 1
                          step_name: "plan_selection"
                          timestamp: "2026-03-01T11:05:00Z"
                        - step: 2
                          step_name: "internet_addons"
                          timestamp: "2026-03-01T11:07:00Z"
                        - step: 3
                          step_name: "tv_selection"
                          timestamp: "2026-03-01T11:10:00Z"
                    meta:
                      responded_at: "2026-03-12T15:00:00Z"
        "401":
          $ref: '#/components/responses/401_unauthorized'
        "404":
          $ref: '#/components/responses/404_not_found'
        "429":
          $ref: '#/components/responses/429_rate_limit'
        "500":
          $ref: '#/components/responses/500_internal_error'

components:
  #-------------------------------
  # Reusable schemas (data models)
  #-------------------------------
  schemas:

    #-#-#-#-#-#-#-#
    # Common Types
    #-#-#-#-#-#-#-#
    timestamp:
      type: string
      format: date-time
      example: 2024-09-20T23:13:31.179Z
      description: A timestamp in ISO 8601 format.

    created_at: # Can be referenced as '#/components/schemas/created_at'
      allOf:
        - $ref: '#/components/schemas/timestamp'
        - description: The timestamp when the session was created.

    updated_at: # Can be referenced as '#/components/schemas/updated_at'
      allOf:
        - $ref: '#/components/schemas/timestamp'
        - description: The timestamp when the session was last updated.

    responded_at: # Can be referenced as '#/components/schemas/responded_at'
      allOf:
        - $ref: '#/components/schemas/timestamp'
        - description: The timestamp when the response was generated.

    session_token: # Can be referenced as '#/components/schemas/session_token'
      type: string
      example: "XqCmeTVgYXrbWrZFZEymkD"
      description: The session token provided by the Hum API. Used to connect the response to the session in the client system.

    session_status: # Can be referenced as '#/components/schemas/session_status'
      type: string
      enum: [open, closed]
      example: open
      description: The status of the session.

    message: # Can be referenced as '#/components/schemas/message'
      type: string
      example: What happened in the most recent request.
      description: A message returned by the API.  Includes a human-readable message about the status of the request.

    request_status: # Can be referenced as '#/components/schemas/request_status'
      type: string
      enum: [ok, warning, error]
      example: ok
      description: |
        An informational summary returned in API response bodies: `ok` for successful responses, `warning` for standard request errors, and `error` for endpoint-specific failures. Integrations must use the HTTP status code, not `request_status`, to determine whether a request succeeded.

    request_id: # Can be referenced as '#/components/schemas/request_id'
      type: string
      example: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"
      description: |
        Best-effort support reference for the request. Include the approximate request timestamp when reporting an error. A request logging failure can leave no matching API log row.

    error_envelope: # Can be referenced as '#/components/schemas/error_envelope'
      type: object
      properties:
        message:
          $ref: '#/components/schemas/message'
        request_status:
          type: string
          enum: [warning, error]
          description: Informational severity only; use the HTTP status code for control flow.
        request_id:
          $ref: '#/components/schemas/request_id'
        errors:
          $ref: '#/components/schemas/errors'
      required:
        - message
        - request_status
        - request_id

    qualify_status: # Can be referenced as '#/components/schemas/qualify_status'
      type: string
      enum: [available, no_service, pending, failed, retry_later]
      example: available
      description: |
        The status of Internet service qualification for the address distinguishes a completed lookup from one that is not ready yet.

        **The answer is ready:**

        - `available`: Providers were found and are returned in `data`.
        - `no_service`: No providers serve this address. `data` is `[]`. You can act on this now, but Hum re-checks periodically, so re-validate it if you store it long term.

        **The answer is not ready, keep polling:**

        - `pending`: The lookup has not finished. `data` is `[]`. Retry by polling `GET /sessions/{token}/services/internet`.
        - `retry_later`: A transient upstream problem. `data` is `[]`. Retry by polling `GET /sessions/{token}/services/internet`.

        **Something went wrong:**

        - `failed`: The lookup errored for this address. `data` is `[]`.

        A `pending` lookup finishes as one of three values: `available` when providers are found, `no_service` when the lookup completes and finds none, or `failed` when it errors. `retry_later` arises separately and does not follow from `pending`. These three values should end the polling loop.

    latitude: # Can be referenced as '#/components/schemas/latitude'
      type: number
      format: float
      example: 42.501721339274
      description: The latitude of the service address.

    longitude: # Can be referenced as '#/components/schemas/longitude'
      type: number
      format: float
      example: -83.286263465881
      description: The longitude of the service address.

    telephone: # Can be referenced as '#/components/schemas/telephone'
      type: string
      nullable: true
      example: "+18332981433"
      description: The telephone number associated with the record.

    street1: # Can be referenced as '#/components/schemas/street1'
      type: string
      minLength: 1
      maxLength: 100
      example: 29090 Tiffany Drive E
      description: The street address of the service location.

    street2: # Can be referenced as '#/components/schemas/street2'
      type: string
      minLength: 1
      maxLength: 50
      example: A2
      description: The unit, apartment, or suite number of the service location.

    city: # Can be referenced as '#/components/schemas/city'
      type: string
      minLength: 1
      maxLength: 50
      example: Southfield
      description: The city of the service location. Required when using the (street1, city, state) combination; optional when providing street1 and zip.

    state: # Can be referenced as '#/components/schemas/state'
      type: string
      pattern: '^[A-Z]{2}$'
      enum: [AL, AK, AZ, AR, CA, CO, CT, DE, FL, GA, HI, ID, IL, IN, IA, KS, KY, LA, ME, MD, MA, MI, MN, MS, MO, MT, NE, NV, NH, NJ, NM, NY, NC, ND, OH, OK, OR, PA, PR, RI, SC, SD, TN, TX, UT, VT, VA, WA, WV, WI, WY]
      example: MI
      description: The two-letter state abbreviation. Required when using the (street1, city, state) combination; optional when providing street1 and zip.

    zip: # Can be referenced as '#/components/schemas/zip'
      type: string
      pattern: '^\d{5}(-\d{4})?$'
      example: 48034
      description: The ZIP code in 12345 or 12345-6789 format. Required when using the (street1, zip) combination; optional when providing street1, city, and state.

    campaign_id: # Can be referenced as '#/components/schemas/campaign_id'
      type: string
      minLength: 1
      maxLength: 100
      example: 1iYyMxsnTy87tFOvukTi7V   
      description: A unique identifier for the campaign. This is used to track the source of the session in your internal system.

    hum_data_set:
      type: string
      pattern: '^\d+$'
      example: "25041808"
      description: The version of the Hum data set used to generate the response. This version may change as the data set is updated.

    money:
      type: object
      properties:
        amount_cents:
          type: integer
          example: 4999
          description: The price amount in whole cents. For example, $49.99 would be 4999.
        currency:
          type: string
          example: USD
          description: The currency used for the price. USD, for example, would be US Dollars. CDN would be Canadian Dollars.
      required:
        - amount_cents
        - currency

    internet_provider_name: # Can be referenced as '#/components/schemas/internet_service_provider_name'
      type: string
      example: Rocket Fiber
      description: The name of the Internet service provider.

    internet_technology: # Can be referenced as '#/components/schemas/internet_technology'
      type: string
      enum: [Fiber, Cable, DSL, Satellite, Wireless, Other]
      example: Fiber
      description: The technology used to deliver the service.

    provider_logo: # Can be referenced as '#/components/schemas/provider_logo'
      type: string
      format: uri
      nullable: true
      example: https://harmony.letshum.com/images/providers/rocketfiber.png
      description: The logo of the service provider.

    #-#-#-#-#-#-#-#
    # Analytics Types
    #-#-#-#-#-#-#-#

    analytics_address:
      type: object
      description: Service address for the session.
      properties:
        street1:
          type: string
          nullable: true
          example: "123 Main St"
        street2:
          type: string
          nullable: true
          example: "Apt 4B"
        city:
          type: string
          nullable: true
          example: "Detroit"
        state:
          type: string
          nullable: true
          example: "MI"
        zip:
          type: string
          nullable: true
          example: "48201"

    analytics_order:
      type: object
      nullable: true
      description: Order data associated with the session. Null if no order was placed.
      properties:
        order_number:
          type: string
          example: "HUM-A1B2C3D4"
          description: Unique order identifier.
        status:
          type: string
          enum: [draft, submitted, processing, confirmed, complete, cancelled]
          example: "complete"
          description: Order status. Cancelled orders are final.
        ordered_at:
          type: string
          format: date-time
          example: "2026-03-01T12:30:00Z"
          description: Timestamp when the order was submitted.
        installed:
          type: boolean
          example: true
          description: Whether the service has been installed. Always false for cancelled orders.
        installed_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-03-05T14:00:00Z"
          description: Timestamp when service was installed. Null if not yet installed. Null for cancelled orders.
        commission_cents:
          type: integer
          nullable: true
          example: 5000
          description: Locked commission amount in cents. Null if no commission recorded. Null for cancelled orders.

    analytics_cart_event:
      type: object
      description: A single cart progression event.
      properties:
        step:
          type: integer
          minimum: 1
          maximum: 8
          example: 1
          description: |
            Numeric step in the checkout flow:
            1 = plan_selection, 2 = internet_addons, 3 = tv_selection,
            4 = tv_addons, 5 = schedule_activation, 6 = customer_info,
            7 = order_summary, 8 = order_confirmation
        step_name:
          type: string
          enum: [plan_selection, internet_addons, tv_selection, tv_addons, schedule_activation, customer_info, order_summary, order_confirmation]
          example: "plan_selection"
          description: Human-readable name of the checkout step.
        timestamp:
          type: string
          format: date-time
          example: "2026-03-01T12:05:00Z"
          description: When this cart event occurred.

    analytics_session_summary:
      type: object
      description: Session summary returned in the list endpoint.
      properties:
        id:
          type: string
          format: uuid
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        created_at:
          type: string
          format: date-time
          example: "2026-03-01T12:00:00Z"
        context:
          type: string
          enum: [move_in, move_out]
          example: "move_in"
          description: >-
            Traffic context for the session, set from the API token that created it.
            `move_in` is a resident setting up service at a home they are moving into.
            `move_out` is a resident leaving a partner's property. Note that the address
            on a move-out session is the resident's destination home, not the partner's
            building. Sessions created before this field was introduced read `move_in`.
        campaign_id:
          type: string
          nullable: true
          example: "spring_promo_2026"
          description: Campaign tracking identifier, if provided when the session was created.
        address:
          $ref: '#/components/schemas/analytics_address'
        furthest_step:
          type: integer
          nullable: true
          minimum: 1
          maximum: 8
          example: 6
          description: Highest checkout step reached during this session (1-8). Null if no cart events.
        order_number:
          type: string
          nullable: true
          example: "HUM-A1B2C3D4"
          description: Order number, if an order was placed.
        status:
          type: string
          nullable: true
          enum: [draft, submitted, processing, confirmed, complete, cancelled]
          example: "complete"
          description: Order status. Null if no order was placed. Cancelled orders are final.
        ordered_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-03-01T12:30:00Z"
          description: When the order was submitted.
        installed:
          type: boolean
          nullable: true
          example: true
          description: Whether the service has been installed. Always false for cancelled orders.
        installed_at:
          type: string
          format: date-time
          nullable: true
          example: "2026-03-05T14:00:00Z"
          description: When the service was installed. Null for cancelled orders.
        commission_cents:
          type: integer
          nullable: true
          example: 5000
          description: Locked commission in cents. Null if no commission recorded and for cancelled orders.

    analytics_session_detail:
      type: object
      description: Full session detail with nested order and cart progression.
      properties:
        id:
          type: string
          format: uuid
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        created_at:
          type: string
          format: date-time
          example: "2026-03-01T12:00:00Z"
        context:
          type: string
          enum: [move_in, move_out]
          example: "move_in"
          description: >-
            Traffic context for the session, set from the API token that created it.
            `move_in` is a resident setting up service at a home they are moving into.
            `move_out` is a resident leaving a partner's property. Note that the address
            on a move-out session is the resident's destination home, not the partner's
            building. Sessions created before this field was introduced read `move_in`.
        campaign_id:
          type: string
          nullable: true
          example: "spring_promo_2026"
        address:
          $ref: '#/components/schemas/analytics_address'
        furthest_step:
          type: integer
          nullable: true
          minimum: 1
          maximum: 8
          example: 6
          description: Highest checkout step reached (1-8). Null if no cart events.
        order:
          $ref: '#/components/schemas/analytics_order'
        cart_progression:
          type: array
          description: Ordered list of cart events for this session, sorted by timestamp ascending.
          items:
            $ref: '#/components/schemas/analytics_cart_event'

    analytics_pagination_meta:
      type: object
      description: Pagination metadata for list responses.
      properties:
        responded_at:
          $ref: '#/components/schemas/responded_at'
        page:
          type: integer
          example: 1
          description: Current page number.
        pages:
          type: integer
          example: 5
          description: Total number of pages.
        count:
          type: integer
          example: 237
          description: Total number of sessions matching the query.

    #-#-#-#-#-#-#-#
    # Composite Schemas
    #-#-#-#-#-#-#-#

    session_token_data:
      type: object
      properties:
        session_token:
          $ref: '#/components/schemas/session_token'
      required:
        - session_token
      description: Session response data. The formatted service address and normalized address components are returned in `meta`.

    session_params: # Can be referenced as '#/components/schemas/session_params'
      type: object
      description: |
        Service address. Provide one of: **street1** and **zip**; **street1**, **city**, and **state**; or **street1**, **city**, and **zip**. State is optional when zip is present.
      properties:
        street1:
          $ref: "#/components/schemas/street1"
        street2:
          $ref: "#/components/schemas/street2"
        city:
          $ref: "#/components/schemas/city"
        state:
          $ref: "#/components/schemas/state"
        zip:
          $ref: "#/components/schemas/zip"
        latitude:
          $ref: "#/components/schemas/latitude"
        longitude:
          $ref: "#/components/schemas/longitude"
        campaign_id:
          $ref: "#/components/schemas/campaign_id"
      required:
        - street1

    normalized_session_params:
      type: object
      description: Address and campaign values after normalization. Coordinate values are serialized as strings in response metadata.
      properties:
        street1:
          type: string
          example: "29090 Tiffany Dr E"
        street2:
          type: string
          nullable: true
          example: null
        city:
          type: string
          example: "Southfield"
        state:
          type: string
          example: "MI"
        zip:
          type: string
          example: "48034"
        latitude:
          type: string
          nullable: true
          example: "42.50189"
        longitude:
          type: string
          nullable: true
          example: "-83.29528"
        campaign_id:
          type: string
          nullable: true
          example: null
      required:
        - street1
        - street2
        - city
        - state
        - zip
        - latitude
        - longitude
        - campaign_id

    meta: # Can be referenced as '#/components/schemas/meta'
      type: object
      properties:
        session_token:
          $ref: "#/components/schemas/session_token"
        session_status:
          $ref: "#/components/schemas/session_status"
        agent_status:
          $ref: "#/components/schemas/agent_status"
        session_params:
          $ref: "#/components/schemas/normalized_session_params"
        service_address:
          $ref: "#/components/schemas/service_address"
        mdu:
          type: boolean
          example: false
          description: Whether the normalized service address is a multi-dwelling unit.
        created_at:
          $ref: "#/components/schemas/created_at"
        updated_at:
          $ref: "#/components/schemas/updated_at"
        responded_at:
          $ref: "#/components/schemas/responded_at"
        hum_data_set:
          $ref: "#/components/schemas/hum_data_set"
      required:
        - session_token
        - session_status
        - session_params
        - service_address
        - mdu
        - agent_status
        - created_at
        - updated_at
        - responded_at
        - hum_data_set
      description: Session metadata, including normalized input values, formatted service address, session status, and the Hum data set used for the response.

    agent_status: # Can be referenced as '#/components/schemas/agent_status'
      type: object
      properties:
        geocoding:
          type: string
          enum: [pending, matched, multiple, failed]
          example: matched
          description: "The status of the geocoding agent.  Pending: The agent has not yet processed the address. Matched: The agent has found a single match for the address. Multiple: The agent has found multiple matches for the address. Failed: The agent was unable to match the address."
        internet:
          type: string
          enum: [pending, matched, failed]
          example: matched
          description: "The status of the Internet service availability agent. Pending: The agent has not yet processed the address. Matched: The agent has found Internet service providers for the address. Failed: The agent was unable to find Internet service providers for the address."
        checkout: 
          type: string
          enum: [pending]
          example: pending
          description: "The status of the checkout agent. Pending: The agent has not yet begun processing a checkout for service. "

    service_address: # Can be referenced as '#/components/schemas/service_address'
      type: string
      example: "1420 Washington Blvd, Detroit, MI 48201"
      description: The complete service address as a single string.

    errors: # Can be referenced as '#/components/schemas/errors'
      type: object
      description: Validation errors for session creation (e.g. missing/invalid address combination, street1, state, or zip).
      properties:
        base:
          type: array
          items:
            type: string
            example: "Provide either (street1 and zip), (street1, city, and state), or (street1, city, and zip)"
        street1:
          type: array
          items:
            type: string
            example: can't be blank
        city:
          type: array
          items:
            type: string
            example: can't be blank
        state:
          type: array
          items:
            type: string
            example: must be a valid state or US territory/commonwealth
        zip:
          type: array
          items:
            type: string
            example: must be in the form 12345 or 12345-1234


    provider_offering: # Can be referenced as '#/components/schemas/provider_offering'
      type: object
      properties:
        provider_id:
          type: string
          example: "130317"
          description: FCC provider identifier
        provider_name:
          $ref: '#/components/schemas/internet_provider_name'
        telephone:
          $ref: '#/components/schemas/telephone'
        provider_icon:
          type: string
          format: uri
          nullable: true
          description: URL for the provider icon image
        provider_logo:
          $ref: '#/components/schemas/provider_logo'
        url:
          type: string
          format: uri
          example: https://provider.example.com/register?utm_source=Hum
          description: URL for accessing the provider website through Hum's tracking system
        button_label:
          type: string
          example: "Get Started"
          description: The text to display on the provider's call-to-action button
        min_plan_price:
          $ref: '#/components/schemas/money'
        provider_promo:
          type: object
          nullable: true
          description: Provider-level promotional information
          properties:
            promo_text:
              type: string
              nullable: true
              example: "$50 off your first month!"
              description: Promotional text from the provider
            promo_logo:
              type: string
              nullable: true
              example: "sale-badge"
              description: Promotional logo identifier
            promo_logo_url:
              type: string
              format: uri
              nullable: true
              example: "https://cdn.letshum.com/promo_icons/banknotes.svg"
              description: URL for the promotional icon image
        url_promo:
          type: object
          nullable: true
          description: URL/buyflow-specific promotional information
          properties:
            promo_text:
              type: string
              nullable: true
              example: "Limited time fiber offer!"
              description: Promotional text specific to this URL/technology
            promo_logo:
              type: string
              nullable: true
              example: "fiber-special"
              description: Promotional logo identifier
            promo_logo_url:
              type: string
              format: uri
              nullable: true
              example: "https://cdn.letshum.com/promo_icons/fiber-deal.svg"
              description: URL for the promotional icon image
        offerings:
          type: array
          items:
            type: object
            properties:
              technology:
                $ref: '#/components/schemas/internet_technology'
              max_download_speed: 
                type: integer
                minimum: 0
                example: 1000
                description: The advertised maximum download speed in Mbps.
              max_upload_speed:
                type: integer
                minimum: 0
                example: 1000
                description: The advertised maximum upload speed in Mbps.
            required:
              - technology
              - max_download_speed
              - max_upload_speed
        product_catalog:
          type: array
          items:
            type: object
            properties:
              category:
                type: string
                enum: [internet, internet_add_on, mobile, television, television_add_on, telephone]
                description: Category identifier
                example: "internet"
              category_name:
                type: string
                description: Human-readable category name
                example: "Internet Service"
              category_description:
                type: string
                description: Description of the category
                example: "High-speed internet access plans"
              products:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      description: Unique product identifier
                      example: "8083bfe8-a53f-49eb-b0e8-a77d3e9e7577"
                    sku:
                      type: string
                      description: Product SKU
                      example: "130235-INT-CBL-01"
                    name:
                      type: string
                      description: Product name
                      example: "Internet Advantage"
                    category:
                      type: string
                      description: Product category
                      example: "internet"
                    category_name:
                      type: string
                      description: Human-readable category name
                      example: "Internet Service"
                    technology:
                      type: string
                      enum: [cable, fiber, dsl, satellite, wireless, not_applicable]
                      description: Technology used for this product
                      example: "cable"
                    position:
                      type: integer
                      minimum: 1
                      description: Display position within category
                      example: 1
                    select_type:
                      type: string
                      enum: [radio, checkbox]
                      description: How this product should be displayed in UI
                      example: "radio"
                    description:
                      type: string
                      description: Product description
                      example: "Boosted Internet for users looking to stream and share content quickly."
                    download_speed:
                      type: string
                      nullable: true
                      description: "Download speed in Mbps. Single value (e.g., '400') or hyphen-delimited range (e.g., '50-100')"
                      example: "50-100"
                    upload_speed:
                      type: string
                      nullable: true
                      description: "Upload speed in Mbps. Single value (e.g., '169') or hyphen-delimited range (e.g., '10-25')"
                      example: "10-25"
                    data_limit:
                      type: string
                      description: Data limit display - either GB amount (e.g., "50 GB") or "Unlimited"
                      example: "Unlimited"
                    channel_count:
                      type: integer
                      minimum: 0
                      description: Number of TV channels (for TV products)
                      example: 0
                    streaming_apps:
                      type: array
                      items:
                        type: string
                      description: Included streaming apps
                      example: ["Disney+ Basic", "ESPN+"]
                    is_required_to_checkout:
                      type: boolean
                      description: Whether this product is required for checkout
                      example: true
                    is_contract_required:
                      type: boolean
                      description: Whether a contract is required for this product
                      example: false
                    is_modem_router_included:
                      type: boolean
                      description: Whether modem/router is included with this product
                      example: false
                    is_bundle_qualifier:
                      type: boolean
                      nullable: true
                      description: Whether this product qualifies for bundle discounts
                      example: null
                    bundle_discounts:
                      type: object
                      description: Bundle discount information
                      example: {}
                    is_local_checkout:
                      type: boolean
                      description: Whether checkout is available locally
                      example: true
                    required_with_plans:
                      type: array
                      items:
                        type: string
                      description: Plans that this product is required with
                      example: []
                    included_with_plans:
                      type: array
                      items:
                        type: string
                      description: Plans that include this product
                      example: []
                    only_available_with_plans:
                      type: array
                      items:
                        type: string
                      description: Plans that this product is only available with
                      example: []
                    initial_term_discount_months:
                      type: integer
                      minimum: 0
                      description: Number of months for initial term discount
                      example: 12
                    second_term_discount_months:
                      type: integer
                      minimum: 0
                      nullable: true
                      description: Number of months for second term discount
                      example: null
                    hum_rank:
                      type: integer
                      minimum: 0
                      maximum: 100
                      nullable: true
                      description: Hum rank score for this product (0-100, higher is better)
                      example: 85
                    max_quantity:
                      type: integer
                      minimum: 1
                      description: Maximum quantity allowed for this product
                      example: 1
                    product_promo:
                      type: object
                      description: Product-level promotional information
                      properties:
                        promo_text:
                          type: string
                          nullable: true
                          description: Promotional text for this product
                          example: "$50 off your first month!"
                        promo_logo_url:
                          type: string
                          format: uri
                          nullable: true
                          description: URL for the promotional icon image
                          example: "https://cdn.letshum.com/promo_icons/sale-badge.svg"
                      example: {}
                    info:
                      type: string
                      description: Additional product information
                      example: "5-year price lock guarantee"
                    pricing:
                      type: object
                      properties:
                        extra_data_fee:
                          $ref: '#/components/schemas/money'
                        professional_installation_fee:
                          $ref: '#/components/schemas/money'
                        self_installation_fee:
                          $ref: '#/components/schemas/money'
                        activation_fee:
                          $ref: '#/components/schemas/money'
                        initial_term_discount:
                          $ref: '#/components/schemas/money'
                        second_term_discount:
                          $ref: '#/components/schemas/money'
                        third_term_discount:
                          $ref: '#/components/schemas/money'
                        autopay_discount:
                          $ref: '#/components/schemas/money'
                        paperless_billing_discount:
                          $ref: '#/components/schemas/money'
                        combined_autopay_paperless_discount:
                          $ref: '#/components/schemas/money'
                        net_monthly_price:
                          $ref: '#/components/schemas/money'
                        gross_monthly_fee:
                          $ref: '#/components/schemas/money'
                      required:
                        - extra_data_fee
                        - professional_installation_fee
                        - self_installation_fee
                        - activation_fee
                        - initial_term_discount
                        - second_term_discount
                        - third_term_discount
                        - autopay_discount
                        - paperless_billing_discount
                        - combined_autopay_paperless_discount
                        - net_monthly_price
                        - gross_monthly_fee
                      description: Detailed pricing breakdown for this product
                  required:
                    - id
                    - sku
                    - name
                    - category
                    - category_name
                    - technology
                    - position
                    - description
                    - select_type
                    - download_speed
                    - upload_speed
                    - data_limit
                    - channel_count
                    - streaming_apps
                    - is_required_to_checkout
                    - is_contract_required
                    - is_modem_router_included
                    - is_bundle_qualifier
                    - bundle_discounts
                    - is_local_checkout
                    - required_with_plans
                    - included_with_plans
                    - only_available_with_plans
                    - initial_term_discount_months
                    - second_term_discount_months
                    - hum_rank
                    - max_quantity
                    - product_promo
                    - info
                    - pricing
            required:
              - category
              - category_name
              - category_description
              - products
          description: Array of product categories, each containing detailed product offerings from this provider
      required:
        - provider_id
        - provider_name
        - provider_icon
        - provider_logo
        - telephone
        - url
        - button_label
        - min_plan_price
        - provider_promo
        - url_promo
        - offerings
        - product_catalog

  #-------------------------------
  # Reusable parameters
  #   path, query, header and cookie parameters
  #-------------------------------
  # parameters:

  #-------------------------------
  # Reusable security schemes
  #-------------------------------
  securitySchemes: # Can be referenced as '#/components/securitySchemes/bearerAuth'
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Bearer token authentication using API tokens.
        Include the token in the Authorization header as: `Authorization: Bearer <token>`

        Obtain tokens from your Hum representative or contact support@letshum.com. There is no self-serve API key dashboard.

  #-------------------------------
  # Reusable responses
  #-------------------------------
  responses:
    400_bad_request: # Can be referenced as '#/components/responses/400_bad_request'
      description: Bad request. This includes an invalid session token. Use the HTTP status code, not `request_status`, to detect the error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          examples:
            invalid_parameters:
              summary: Invalid session parameters
              value:
                message: "Invalid parameters sent to session."
                request_status: "warning"
                request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"
            invalid_session_token:
              summary: Invalid session token
              value:
                message: "Invalid session token."
                request_status: "warning"
                request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    401_unauthorized: # Can be referenced as '#/components/responses/401_unauthorized'
      description: Unauthorized. Use the HTTP status code, not `request_status`, to detect the error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Unauthorized access"
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    403_forbidden: # Can be referenced as '#/components/responses/403_forbidden'
      description: Forbidden. The requested resource does not belong to the authenticated account, or a security policy blocked the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Forbidden: token_ids contain tokens not belonging to your account"
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    404_not_found: # Can be referenced as '#/components/responses/404_not_found'
      description: Resource not found or does not belong to the authenticated account.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Session not found."
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    415_unsupported_media_type: # Can be referenced as '#/components/responses/415_unsupported_media_type'
      description: The request was rejected because its content type is unsupported.
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: Request rejected
              message:
                type: string
                example: The request was rejected due to security concerns
              status:
                type: integer
                enum: [415]
                example: 415
            required:
              - error
              - message
              - status

    410_gone: # Can be referenced as '#/components/responses/410_gone'
      description: The session has been closed. Use the HTTP status code, not `request_status`, to detect the error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Session is closed. Please open a new session."
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    422_unprocessable: # Can be referenced as '#/components/responses/422_unprocessable'
      description: Session validation failed. Use the HTTP status code, not `request_status`, to detect the error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Session could not be created."
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"
            errors:
              base:
                - "Provide either (street1 and zip), (street1, city, and state), or (street1, city, and zip)"

    429_rate_limit: # Can be referenced as '#/components/responses/429_rate_limit'
      description: Rate Limit Exceeded
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Rate limit exceeded. Please try again later."
            request_status: "warning"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"
  
    500_internal_error: # Can be referenced as '#/components/responses/500_internal_error'
      description: Unexpected internal error handled by an API controller.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error_envelope'
          example:
            message: "Internal server error"
            request_status: "error"
            request_id: "3f1c3c07-785d-4c59-97dd-25cb38f670d7"

    503_address_validation_unavailable: # Can be referenced as '#/components/responses/503_address_validation_unavailable'
      description: Address validation is temporarily unavailable. This deliberate exception to the common error envelope preserves its body and `Retry-After` header.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            type: object
            properties:
              address_validation_unavailable:
                type: boolean
                enum: [true]
                example: true
            required:
              - address_validation_unavailable
  
  #-------------------------------
  # Reusable Headers
  #-------------------------------
  headers:
    session_token: # Can be referenced as '#/components/headers/session_token'
      description: The session token provided by the Hum API. Used to connect the response to the session in the client system.
      required: true
      schema:
        type: string
      example: "XqCmeTVgYXrbWrZFZEymkD"

    Retry-After:
      description: Number of seconds to wait before retrying the request.
      schema:
        type: integer
      example: 60
      required: true

    Cache-Control:
      description: Cache control directives
      schema:
        type: string
      example: private, max-age=600

  #-------------------------------
  # Reusable Examples
  #-------------------------------
  # examples:

  #-------------------------------
  # Reusable Links
  #-------------------------------
  # links:

  #-------------------------------
  # Reusable Callbacks
  #-------------------------------
  # callbacks:

servers:
# - url: http://api.letshum.local:3000
#   description: Local development environment
- url: https://api-sandbox.letshum.com
  description: Sandbox environment
- url: https://api.letshum.com
  description: Production environment
