openapi: 3.0.3
info:
  title: Cobroker Agent API
  description: >
    REST API providing commercial real estate tools for AI agents.
    Create projects, manage properties, enrich with demographics,
    search places, and run AI research — all via HTTP.
  version: "1.0.0"
  contact:
    name: Cobroker
    url: https://cobroker.ai
    email: isaac@cobroker.ai

servers:
  - url: https://app.cobroker.ai/api/agent/openclaw
    description: Production

security:
  - agentAuth: []

components:
  securitySchemes:
    agentAuth:
      type: apiKey
      in: header
      name: X-Agent-Secret
      description: Agent secret key. Also requires X-Agent-User-Id header.

  schemas:
    Property:
      type: object
      properties:
        address:
          type: string
          description: "Full address with at least 3 comma-separated parts (street, city, state+zip)"
          example: "123 Main St, Dallas, TX 75201"
        latitude:
          type: number
          example: 32.78
        longitude:
          type: number
          example: -96.80
        fields:
          type: object
          additionalProperties:
            type: string
          example:
            Price: "$500,000"
            Size: "10,000 SF"
            Type: "Warehouse"

    ProjectSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        public:
          type: boolean
        propertyCount:
          type: integer
        createdAt:
          type: string
          format: date-time
        projectUrl:
          type: string
        publicUrl:
          type: string

    ProjectDetails:
      type: object
      properties:
        project:
          $ref: "#/components/schemas/ProjectSummary"
        columns:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
              name:
                type: string
        properties:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
              address:
                type: string
              latitude:
                type: number
              longitude:
                type: number
              fields:
                type: object
                additionalProperties:
                  type: string
        propertyCount:
          type: integer

paths:
  /projects:
    get:
      summary: List all projects
      operationId: listProjects
      responses:
        "200":
          description: List of projects
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  projects:
                    type: array
                    items:
                      $ref: "#/components/schemas/ProjectSummary"
                  count:
                    type: integer
    post:
      summary: Create a project with properties
      operationId: createProject
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  example: "Dallas Warehouses"
                description:
                  type: string
                  example: "Q1 industrial survey"
                source:
                  type: string
                  example: "openclaw"
                public:
                  type: boolean
                  default: true
                properties:
                  type: array
                  items:
                    $ref: "#/components/schemas/Property"
      responses:
        "201":
          description: Project created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  projectId:
                    type: string
                    format: uuid
                  projectUrl:
                    type: string
                  publicUrl:
                    type: string
                  propertyCount:
                    type: integer
                  columnCount:
                    type: integer
                  geocodedCount:
                    type: integer

  /projects/{projectId}:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get project details
      operationId: getProject
      responses:
        "200":
          description: Project details with properties
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProjectDetails"
    patch:
      summary: Update project
      operationId: updateProject
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                public:
                  type: boolean
      responses:
        "200":
          description: Project updated
    delete:
      summary: Delete project and all associated data
      operationId: deleteProject
      responses:
        "200":
          description: Project deleted

  /projects/{projectId}/properties:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Add properties to project
      operationId: addProperties
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [properties]
              properties:
                properties:
                  type: array
                  maxItems: 50
                  items:
                    $ref: "#/components/schemas/Property"
      responses:
        "201":
          description: Properties added
    patch:
      summary: Update properties
      operationId: updateProperties
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [updates]
              properties:
                updates:
                  type: array
                  items:
                    type: object
                    required: [id]
                    properties:
                      id:
                        type: string
                        format: uuid
                      address:
                        type: string
                      fields:
                        type: object
                        additionalProperties:
                          type: string
      responses:
        "200":
          description: Properties updated
    delete:
      summary: Delete properties
      operationId: deleteProperties
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [propertyIds]
              properties:
                propertyIds:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        "200":
          description: Properties deleted

  /projects/{projectId}/demographics:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: List all 58 available demographic types
      operationId: listDemographicTypes
      responses:
        "200":
          description: Demographic types grouped by category
    post:
      summary: Add ESRI demographic data to properties
      operationId: addDemographics
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [dataType, radius]
              properties:
                dataType:
                  type: string
                  description: "ESRI GeoEnrichment data type. 15+ categories: Core Demographics, Income, Employment, Housing, Age, Race/Ethnicity, Tapestry Psychographics, Consumer Spending, Market Potential Index, Business Data, Crime Indexes, Retail Marketplace, Commute, Education, Health Insurance."
                  enum: [population, median_age, households, population_density, household_size, daytime_population, income, per_capita_income, avg_household_income, disposable_income, income_under_25k, income_25k_50k, income_50k_75k, income_75k_100k, income_100k_150k, income_150k_250k, income_250k_plus, total_employment, retail_jobs, healthcare_jobs, office_jobs, food_service_jobs, manufacturing_jobs, construction_jobs, education_jobs, unemployment_rate, median_home_value, median_rent, owner_occupied, renter_occupied, vacant_housing, housing_after_2010, avg_home_value, median_year_built, age_0_4, age_5_17, age_18_24, age_25_34, age_35_44, age_45_54, age_55_64, age_65_74, age_75_plus, working_age, white_population, black_population, hispanic_population, asian_population, two_or_more_races, diversity_index, tapestry_segment, tapestry_top3, lifemode_group, restaurant_spending, retail_spending, entertainment_spending, grocery_spending, spending_potential_index, dining_propensity, shopping_propensity, fitness_propensity, business_count, employee_count, retail_trade_businesses, food_service_businesses, total_crime_index, property_crime_index, personal_crime_index, retail_demand, retail_supply_gap, food_service_demand, avg_commute_time, work_from_home_pct, public_transit_pct, bachelors_degree_pct, graduate_degree_pct, uninsured_rate]
                  example: "population"
                radius:
                  type: number
                  minimum: 0.1
                  maximum: 100
                  description: "Miles for radius mode, minutes for drive/walk"
                  example: 1
                mode:
                  type: string
                  enum: [radius, drive, walk]
                  default: "radius"
                columnName:
                  type: string
                  description: "Auto-generated if omitted"
      responses:
        "201":
          description: Demographics added

  /projects/{projectId}/enrichment:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Check enrichment status
      operationId: getEnrichmentStatus
      parameters:
        - name: columnId
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Enrichment status
    post:
      summary: Submit AI research enrichment (async)
      operationId: submitEnrichment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [prompt]
              properties:
                prompt:
                  type: string
                  description: "Question to research for each property"
                  example: "What is the zoning classification for this property?"
                columnName:
                  type: string
                  description: "Auto-generated from prompt if omitted"
                processor:
                  type: string
                  enum: [base, core, pro, ultra]
                  default: "core"
                  description: "base ~15-100s, core ~1-5min, pro ~3-9min, ultra ~5-25min"
      responses:
        "202":
          description: Enrichment submitted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  projectId:
                    type: string
                    format: uuid
                  columnId:
                    type: string
                    format: uuid
                  columnName:
                    type: string
                  status:
                    type: string
                    enum: [processing]
                  propertiesSubmitted:
                    type: integer

  /projects/{projectId}/traffic:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Get foot traffic and visitor analytics for properties
      operationId: getTrafficData
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [dataType]
              properties:
                dataType:
                  type: string
                  description: "Traffic data type: visits, visit_trends, visits_by_hour, visits_by_day, dwell_time, visit_frequency, trade_area, drive_time_trade_area, rankings, competitive_benchmark, nearby_activity, transaction_volume, avg_ticket_size, sales_trends, daily_sales, visitor_origins, cross_shopping, favorite_chains"
                  enum: [visits, visit_trends, visits_by_hour, visits_by_day, dwell_time, visit_frequency, trade_area, drive_time_trade_area, rankings, competitive_benchmark, nearby_activity, transaction_volume, avg_ticket_size, sales_trends, daily_sales, visitor_origins, cross_shopping, favorite_chains]
                  example: "visits"
                period:
                  type: string
                  description: "Time period for data"
                  enum: [last_month, last_3_months, last_6_months, last_12_months]
                  default: "last_12_months"
                granularity:
                  type: string
                  enum: [daily, weekly, monthly]
                  default: "monthly"
      responses:
        "201":
          description: Traffic data added to properties

  /projects/{projectId}/places/search:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          description: "Project ID or 'new' to auto-create"
    post:
      summary: Search Google Places and save results
      operationId: searchPlaces
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query:
                  type: string
                  example: "Starbucks in Dallas"
                preview:
                  type: boolean
                  default: false
                  description: "If true, returns places without saving"
                maxResults:
                  type: integer
                  default: 50
                  maximum: 400
                regionSearch:
                  type: boolean
                  default: false
                  description: "Search across 7 US regions for nationwide coverage"
                projectName:
                  type: string
                  description: "Name for auto-created project (only when projectId is 'new')"
                destination:
                  type: string
                  enum: [properties, layer]
                  default: "properties"
                layerName:
                  type: string
                  description: "Required when destination is 'layer'"
                markerColor:
                  type: string
                  default: "#4285F4"
      responses:
        "200":
          description: Preview results (when preview=true)
        "201":
          description: Places saved to project

  /projects/{projectId}/places/nearby:
    parameters:
      - name: projectId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Analyze nearby places for each property
      operationId: nearbyAnalysis
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [mode, radiusMiles]
              properties:
                mode:
                  type: string
                  enum: [nearest, count]
                query:
                  type: string
                  description: "Required for 'nearest' mode"
                  example: "grocery store"
                placeTypes:
                  type: array
                  items:
                    type: string
                  description: "Required for 'count' mode"
                  example: ["restaurant", "cafe"]
                radiusMiles:
                  type: number
                  minimum: 0.1
                  maximum: 50
                  example: 2
                columnName:
                  type: string
                  description: "Auto-generated if omitted"
      responses:
        "201":
          description: Analysis complete
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  columnName:
                    type: string
                  propertiesProcessed:
                    type: integer
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        propertyId:
                          type: string
                        address:
                          type: string
                        nearestPlace:
                          type: string
                        distanceMiles:
                          type: number
                        totalFound:
                          type: integer
