{
  "openapi": "3.0.0",
  "info": {
    "title": "Zip Code Portal API",
    "version": "1.0.0",
    "description": "\n# Zip Code Portal API\n\nOpenAPI documentation for all Next.js App Router endpoints under `/api/**`.\n\n## Authentication\n\nMost endpoints require **PortalUser authentication**, supported via:\n\n- **Portal JWT**: `Authorization: JWT <token>` (or cookie `payload-token`)\n- **API Key**: `x-api-key`, `x-resi-key`, or `Authorization: Bearer <token>`\n\n## Notes\n\n- This spec is generated from route-level `@swagger` blocks.\n"
  },
  "servers": [
    {
      "url": "https://zip-code-portal-blhlswtsk-resi-labs-ai.vercel.app",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Portal",
      "description": "Portal project, chat, and API key endpoints"
    },
    {
      "name": "Billing",
      "description": "Stripe checkout, customer portal, subscription summaries, and webhooks"
    },
    {
      "name": "RealEstateV1",
      "description": "External /api/v1/real_estate endpoints"
    },
    {
      "name": "Geocode",
      "description": "Geocoding endpoints"
    },
    {
      "name": "Dashboard",
      "description": "ZIPCODE dashboard endpoints (models/stats/auth/etc.)"
    }
  ],
  "components": {
    "securitySchemes": {
      "PortalJWT": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Portal JWT auth. Use `Authorization: JWT <token>` (cookie `payload-token` is also supported for browser sessions)."
      },
      "PortalApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Portal API key auth. The server accepts `x-api-key`, `x-resi-key`, or `Authorization: Bearer <token>`."
      },
      "BittensorAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Hotkey",
        "description": "Bittensor hotkey (SS58 address) with accompanying `Nonce` and `Signature` headers."
      }
    },
    "schemas": {
      "Model": {
        "type": "object",
        "description": "Miner model metadata and aggregated performance.",
        "properties": {
          "uid": {
            "type": "integer",
            "description": "Miner UID on the subnet."
          },
          "hotkey": {
            "type": "string",
            "description": "Miner hotkey (SS58 address)."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "networkId": {
            "type": "integer",
            "description": "Network ID (1=mainnet, 2=testnet, 3=local)."
          },
          "score": {
            "type": "number",
            "nullable": true,
            "description": "Aggregate performance score (if available)."
          },
          "rank": {
            "type": "integer",
            "nullable": true,
            "description": "Rank among miners (if available)."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "uid",
          "hotkey",
          "networkId"
        ],
        "additionalProperties": true
      },
      "OkResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        },
        "required": [
          "ok",
          "error"
        ]
      }
    }
  },
  "paths": {
    "/api/billing/checkout": {
      "post": {
        "summary": "Create a Stripe Checkout session",
        "description": "Creates a subscription checkout session for the authenticated PortalUser.",
        "tags": [
          "Billing"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "plan"
                ],
                "properties": {
                  "plan": {
                    "type": "string",
                    "description": "Billing plan key (starter, growth, scale)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout session URL"
          },
          "400": {
            "description": "Invalid plan"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Stripe configuration error"
          }
        }
      }
    },
    "/api/billing/portal": {
      "post": {
        "summary": "Create a Stripe Billing Portal session",
        "tags": [
          "Billing"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Billing portal session URL"
          },
          "400": {
            "description": "No billing customer on account"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/billing/summary": {
      "get": {
        "summary": "Billing usage and plan summary for current user",
        "tags": [
          "Billing"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage and plan configuration"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/auth/validation-set": {
      "post": {
        "summary": "Get presigned URLs for validation set and raw data",
        "description": "Authenticate as a validator using Bittensor signature authentication\nand receive presigned S3 URLs for the validation set and raw data files.\nValidators can use the validation set directly or verify the processing\nby downloading and processing the raw state data files themselves.\n\n## Authentication\nValidators must sign a message containing: `{METHOD}{URL}{HEADERS_JSON}`\n\nHeaders JSON includes Hotkey and Nonce, sorted alphabetically:\n`{\"Hotkey\": \"5xxx...\", \"Nonce\": \"1733920000\"}`\n\n## Rate Limiting\n- Authenticated validators: 10 requests per minute\n- Failed attempts: IP blacklisted after 5 failures in 5 minutes\n",
        "tags": [
          "Authentication"
        ]
      },
      "get": {
        "summary": "Health check for validation set endpoint",
        "description": "Returns basic info about the endpoint. No authentication required.",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/dashboard/docs": {
      "get": {
        "summary": "OpenAPI spec (dashboard only)",
        "description": "Returns the OpenAPI 3.0 JSON spec for dashboard endpoints under `/api/dashboard/**`.",
        "tags": [
          "Dashboard"
        ],
        "responses": {
          "200": {
            "description": "OpenAPI 3.0 JSON"
          },
          "401": {
            "description": "Unauthorized (Basic Auth in production)"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/models/{uid}": {
      "get": {
        "summary": "Get model details by UID or hotkey",
        "tags": [
          "Models"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "uid",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model UID (numeric) or hotkey (SS58)"
          },
          {
            "in": "query",
            "name": "networkId",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Model detail response"
          },
          "400": {
            "description": "Invalid input"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/models/{uid}/validations/{date}": {
      "get": {
        "summary": "Get validation results for a model on a date",
        "tags": [
          "Models"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "uid",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model UID (numeric) or hotkey (SS58)"
          },
          {
            "in": "path",
            "name": "date",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Date in YYYY-MM-DD"
          },
          {
            "in": "query",
            "name": "networkId",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "in": "query",
            "name": "validatorId",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Validator filter (id or 'all')"
          }
        ],
        "responses": {
          "200": {
            "description": "Validation results"
          },
          "400": {
            "description": "Invalid date format"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/models/{uid}/validations": {
      "get": {
        "summary": "List available validation dates for a model",
        "tags": [
          "Models"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "uid",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model UID (numeric) or hotkey (SS58)"
          },
          {
            "in": "query",
            "name": "networkId",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Available validation dates"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/models": {
      "get": {
        "summary": "List all models",
        "description": "Get a paginated list of miner AI models with their performance data",
        "tags": [
          "Models"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "networkId",
            "schema": {
              "type": "integer",
              "default": 1
            },
            "description": "Network ID (1=mainnet, 2=testnet, 3=local)"
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "integer",
              "default": 1
            },
            "description": "Page number for pagination"
          },
          {
            "in": "query",
            "name": "pageSize",
            "schema": {
              "type": "integer",
              "default": 10
            },
            "description": "Number of models per page"
          },
          {
            "in": "query",
            "name": "hotkey",
            "schema": {
              "type": "string"
            },
            "description": "Optional hotkey prefix to filter models by (minimum 4 characters)"
          }
        ],
        "responses": {
          "200": {
            "description": "List of models",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "models": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Model"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/dashboard/stats/emissions": {
      "get": {
        "summary": "Get daily emissions",
        "description": "Get the current daily TAO emissions for subnet 46",
        "tags": [
          "Stats"
        ],
        "responses": {
          "200": {
            "description": "Emissions data"
          }
        }
      }
    },
    "/api/dashboard/stats": {
      "get": {
        "summary": "Get network statistics",
        "description": "Get overall network statistics including total models, top score, emissions, and daily prize pool",
        "tags": [
          "Stats"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "networkId",
            "schema": {
              "type": "integer"
            },
            "description": "Network ID (1=mainnet, 2=testnet). Defaults to 1."
          }
        ],
        "responses": {
          "200": {
            "description": "Network statistics"
          }
        }
      }
    },
    "/api/dashboard/validation-set/real_estate/download": {
      "get": {
        "summary": "Download the real estate validation set JSON",
        "description": "Downloads and returns the validation set for a requested date.\\n\nDate must be today or yesterday.\\n\nResponse may be sanitized for field name compatibility.\n",
        "tags": [
          "Dashboard"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "date",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Date in YYYY-MM-DD (defaults to today)"
          }
        ],
        "responses": {
          "200": {
            "description": "Validation set JSON"
          },
          "400": {
            "description": "Invalid date or out of range"
          },
          "404": {
            "description": "Validation set not found"
          },
          "500": {
            "description": "Server error"
          },
          "502": {
            "description": "Upstream download failed"
          }
        }
      }
    },
    "/api/docs": {
      "get": {
        "summary": "OpenAPI spec (combined)",
        "description": "Returns the combined OpenAPI 3.0 JSON spec for all `/api/**` routes.",
        "tags": [
          "Dashboard"
        ],
        "responses": {
          "200": {
            "description": "OpenAPI 3.0 JSON"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/geocode": {
      "get": {
        "summary": "Forward geocode a query string",
        "description": "Proxies Mapbox forward geocoding for UI autocomplete.\\n\nRate limited per IP.\\n\nRequires `MAPBOX_ACCESS_TOKEN` server env var.\n",
        "tags": [
          "Geocode"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Search query (address, place, etc.)"
          }
        ],
        "responses": {
          "200": {
            "description": "Geocode results"
          },
          "400": {
            "description": "Missing or invalid query"
          },
          "429": {
            "description": "Rate limited"
          },
          "500": {
            "description": "Server error"
          },
          "502": {
            "description": "Upstream Mapbox error"
          }
        }
      }
    },
    "/api/portal/acs/{zipCode}": {
      "get": {
        "summary": "ACS housing estimates for a ZIP code",
        "description": "Returns ACS 5-Year housing estimates for the given ZCTA (ZIP Code\nTabulation Area), including bedroom distribution, unit structure counts,\nmedian rooms, and year-built breakdown.\n",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "zipCode",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}$"
            },
            "description": "5-digit US ZIP code"
          }
        ],
        "responses": {
          "200": {
            "description": "ACS data found"
          },
          "204": {
            "description": "No ACS data available for this ZIP code"
          },
          "400": {
            "description": "Invalid ZIP code format"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/api-keys/{keyId}/reveal": {
      "post": {
        "summary": "Reveal an API key token (one-way retrieval)",
        "description": "Returns the decrypted API key token for the specified key, if owned by the authenticated user and project.",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "keyId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Decrypted token"
          },
          "400": {
            "description": "Missing route params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Decryption or server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/api-keys/{keyId}/revoke": {
      "post": {
        "summary": "Revoke an API key",
        "description": "Marks an API key as revoked for the authenticated user and project.",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "keyId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked key (safe fields only)"
          },
          "400": {
            "description": "Missing route params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets/{assetId}": {
      "delete": {
        "summary": "Hide an asset from a chat",
        "description": "Soft-removes an asset from the chat by setting `hiddenFromChat = true`.",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Missing route params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets/bulk-upload": {
      "post": {
        "summary": "Bulk import assets into a chat from an address-only CSV",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "options": {
                    "type": "string",
                    "description": "JSON string, e.g. {\\\"queueEvaluation\\\": true}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Import results"
          },
          "400": {
            "description": "Invalid input (too many rows, missing file)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets/features": {
      "get": {
        "summary": "Fetch computed feature vectors for assets in a chat",
        "description": "Computes and returns feature vectors derived from stored asset fields for evaluation.",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feature vectors"
          },
          "400": {
            "description": "Missing/invalid route params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Submit an evaluation request for a specific asset",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "assetId": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Evaluation submitted"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets/from-address": {
      "post": {
        "summary": "Create a single asset from a JSON record (address + summary fields)",
        "description": "JSON twin of the CSV bulk-upload \"summary\" format. Geocodes the address,\nlooks up ACS data for the resolved ZIP, merges JSON overrides over ACS\nestimates over defaults, then creates one asset under the project/chat.\nNumeric fields follow the same validation rules as the CSV summary\nformat; invalid scalar values are silently dropped (only `address` is\nrequired).\n",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "address"
                ],
                "properties": {
                  "address": {
                    "type": "string"
                  },
                  "stories": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "living_area": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "lot_size": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "year_built": {
                    "type": "integer"
                  },
                  "garages": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "parking_spaces": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "bedrooms": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                  },
                  "bathrooms": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                  },
                  "living_rooms": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                  },
                  "kitchens": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                  },
                  "home_type": {
                    "type": "string",
                    "enum": [
                      "single-family",
                      "multi-family",
                      "manufactured",
                      "unknown"
                    ]
                  },
                  "last_sale_price": {
                    "type": "number"
                  },
                  "last_sale_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "options": {
                    "type": "object",
                    "properties": {
                      "queueEvaluation": {
                        "type": "boolean",
                        "default": true
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Asset created"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "422": {
            "description": "Address could not be geocoded"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets": {
      "get": {
        "summary": "List assets for a chat",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Asset list"
          },
          "400": {
            "description": "Missing route params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Create a new asset for a chat (from normalized location)",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "streetAddress",
                  "city",
                  "state",
                  "zipCode",
                  "latitude",
                  "longitude"
                ],
                "properties": {
                  "streetAddress": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "state": {
                    "type": "string"
                  },
                  "zipCode": {
                    "type": "string"
                  },
                  "latitude": {
                    "type": "number"
                  },
                  "longitude": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created asset and default rooms"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/assets/stats": {
      "get": {
        "summary": "Aggregate appraisal stats across every (non-hidden) asset in a chat",
        "description": "The chat list endpoint is paginated, so the header tiles can't be derived\nfrom the currently-loaded page — that would make \"Total appraisals\" and\n\"Avg appraisal value\" climb as the user scrolls through pages. This\nendpoint runs a single aggregate query against the whole chat so the\ntiles can show the final values immediately.\n",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregate stats"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}": {
      "patch": {
        "summary": "Update a chat (rename)",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated chat"
          },
          "400": {
            "description": "Missing params or invalid body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden (not project owner)"
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "delete": {
        "summary": "Delete a chat",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat deleted"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/scrape-batch/{jobId}": {
      "get": {
        "summary": "Poll the status of a previously-submitted scrape batch",
        "description": "Relays to the single-property-scraper's `GET /scrape/batch/{job_id}`.\nReturns the current state, completed/failed counters, and per-address\nresults as they fill in. Status transitions from `running` to\n`completed` when `completed + failed == total`.\n",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "jobId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current job status"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "No access to the project / chat"
          },
          "404": {
            "description": "Unknown jobId",
            "project": null,
            "or chat": null
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats/{chatId}/scrape-batch": {
      "post": {
        "summary": "Submit a batch of addresses to the scraper service for pre-caching",
        "description": "Relays to the single-property-scraper's `/scrape/batch` endpoint. The\nscraper queues each address, resolves it via autocomplete + the\nBrightData/ScrapingBee fallback chain, and writes the result into the\nshared lookup cache. Subsequent per-row asset creates hit the warm\ncache and skip the cold scrape.\n",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "chatId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "addresses"
                ],
                "properties": {
                  "addresses": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 200
                  },
                  "detailsMode": {
                    "type": "string",
                    "enum": [
                      "basic",
                      "standard",
                      "comprehensive"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted"
          },
          "400": {
            "description": "Invalid request body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "No access to the project / chat"
          },
          "404": {
            "description": "Project or chat not found"
          },
          "502": {
            "description": "Scraper service unavailable"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}/chats": {
      "get": {
        "summary": "List chats for a project",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of chats"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden (not project owner)"
          }
        }
      },
      "post": {
        "summary": "Create a chat in a project",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created chat"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden (not project owner)"
          }
        }
      }
    },
    "/api/portal/projects/{projectId}": {
      "patch": {
        "summary": "Update a project (rename)",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated project"
          },
          "400": {
            "description": "Missing params or invalid body"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "delete": {
        "summary": "Soft-delete a project (hide)",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "Missing params"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/portal/projects": {
      "get": {
        "summary": "List projects for the authenticated portal user",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Project list"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      },
      "post": {
        "summary": "Create a project",
        "tags": [
          "Portal"
        ],
        "security": [
          {
            "PortalJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created project"
          },
          "400": {
            "description": "Missing name"
          },
          "401": {
            "description": "Unauthorized"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/bathrooms/{id}": {
      "put": {
        "summary": "Update a bathroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial bathroom update payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated bathroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "delete": {
        "summary": "Delete a bathroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted bathroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/bathrooms": {
      "get": {
        "summary": "List bathrooms for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bathrooms list"
          },
          "400": {
            "description": "Invalid assetId"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Create a bathroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "RealEstateAssetBathroom create payload"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created bathroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/bedrooms/{id}": {
      "put": {
        "summary": "Update a bedroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial bedroom update payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated bedroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "delete": {
        "summary": "Delete a bedroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted bedroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/bedrooms": {
      "get": {
        "summary": "List bedrooms for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bedrooms list"
          },
          "400": {
            "description": "Invalid assetId"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Create a bedroom for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "RealEstateAssetBedroom create payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created bedroom"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/kitchens/{id}": {
      "put": {
        "summary": "Update a kitchen for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial kitchen update payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated kitchen"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "delete": {
        "summary": "Delete a kitchen for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted kitchen"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/kitchens": {
      "get": {
        "summary": "List kitchens for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Kitchens list"
          },
          "400": {
            "description": "Invalid assetId"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Create a kitchen for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "RealEstateAssetKitchen create payload"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created kitchen"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/living-rooms/{id}": {
      "put": {
        "summary": "Update a living room for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial living room update payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated living room"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "delete": {
        "summary": "Delete a living room for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted living room"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found for this asset"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}/living-rooms": {
      "get": {
        "summary": "List living rooms for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Living rooms list"
          },
          "400": {
            "description": "Invalid assetId"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "post": {
        "summary": "Create a living room for an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "RealEstateAssetLivingRoom create payload"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created living room"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset/{assetId}": {
      "put": {
        "summary": "Update an asset",
        "description": "Updates an existing RealEstateAsset by ID. Supports optional skipEvaluation flag.",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "skipEvaluation",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "If set to 1 (or header x-skip-evaluation=1), skips evaluation side-effects."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial RealEstateAsset update payload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated asset"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      },
      "delete": {
        "summary": "Delete an asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "assetId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/asset": {
      "post": {
        "summary": "Create a new asset",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "RealEstateAsset create payload"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created asset"
          },
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Unauthorized"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/evaluate/features": {
      "post": {
        "summary": "Create an evaluation from feature payload",
        "description": "Creates a new ModelEvaluation in queued state and returns a UID.",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Feature payload (shape depends on current feature config)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued evaluation UID"
          },
          "400": {
            "description": "Invalid JSON or payload"
          },
          "401": {
            "description": "Unauthorized"
          },
          "402": {
            "description": "Billing/usage gate blocked"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    },
    "/api/v1/real_estate/evaluate/results/{id}": {
      "get": {
        "summary": "Fetch evaluation results by UID",
        "description": "Fetches a ModelEvaluation record by its `uid` field (not DB id).",
        "tags": [
          "RealEstateV1"
        ],
        "security": [
          {
            "PortalJWT": []
          },
          {
            "PortalApiKey": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Evaluation UID"
          }
        ],
        "responses": {
          "200": {
            "description": "Evaluation record (uid, state, features, result)"
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "Not found"
          },
          "500": {
            "description": "Server error"
          }
        }
      }
    }
  }
}
