{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/cubi-estate/json-schema/cubi-estate-agent-search-request-schema.json",
  "title": "AgentSearchRequest",
  "x-generated": "2026-11-04",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/cubi-estate-openapi.yml#/components/schemas/AgentSearchRequest",
  "properties": {
    "query": {
      "type": "string",
      "title": "Query",
      "description": "Natural-language property search request."
    },
    "user_message": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "User Message",
      "description": "The user's ORIGINAL, untranslated message text. Used only for server-side language detection — the reply summary and listing cards are rendered in the detected language. Send the user's words verbatim; keep `query` as the English-translated search string for the NL extractor. When omitted, the backend falls back to `lang`."
    },
    "lang": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Lang",
      "description": "ISO 639-1 language code; one of en/pt/es/fr/de/nl/ru. Fallback when `user_message` detection is unavailable/low-confidence.",
      "default": "en"
    },
    "limit": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 50.0,
          "minimum": 1.0
        },
        {
          "type": "null"
        }
      ],
      "title": "Limit",
      "description": "Max listings to render in the markdown body."
    },
    "fetch_limit": {
      "anyOf": [
        {
          "type": "integer",
          "maximum": 50.0,
          "minimum": 1.0
        },
        {
          "type": "null"
        }
      ],
      "title": "Fetch Limit",
      "description": "Max listings to fetch and include in the structured cache block. When set (any value), the response appends a `<!-- CUBI_PROPERTIES: <json-array> -->` block carrying the full property dicts (including all image URLs) for `fetch_limit` listings — so callers like the Telegram bot can render media groups and paginate 'show more' locally without re-calling the backend. Omit to get markdown-only output (default; OpenClaw path)."
    },
    "history": {
      "anyOf": [
        {
          "items": {
            "additionalProperties": true,
            "type": "object"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "History",
      "description": "Prior-turn history. Pass back the entries extracted from the previous response's `<!-- CUBI_HISTORY: ... -->` block to maintain context across turns."
    },
    "telegram_chat_id": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Telegram Chat Id",
      "description": "Telegram chat ID. When set, the backend itself delivers the first listing's text + photo gallery to that chat via Bot API (`sendMessage` + `sendMediaGroup`), in addition to returning markdown to the caller. Independent of which agent tool calls the endpoint — works for `web_fetch`, `exec`, MCP. Requires `TELEGRAM_BOT_TOKEN` env var on the backend; if unset, delivery is skipped with a warning and the markdown body is returned unchanged."
    },
    "whatsapp_chat_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Whatsapp Chat Id",
      "description": "WhatsApp wa_id (digits-only phone with country code, no leading '+', e.g. '351912345678'). When set, the backend delivers the first listing's text + image + interactive buttons via Meta WhatsApp Cloud API. Mutually exclusive with `telegram_chat_id`; one must be set when `X-Cubi-Channel` is 'whatsapp'. Requires `WHATSAPP_ACCESS_TOKEN` + `WHATSAPP_PHONE_NUMBER_ID` env vars; if unset, delivery is skipped with a warning and the markdown body is returned unchanged."
    },
    "apply_strategy": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "off",
            "log",
            "apply",
            "apply_relax"
          ]
        },
        {
          "type": "null"
        }
      ],
      "title": "Apply Strategy",
      "description": "Per-request override of `FILTER_PREFERENCES_MODE`. None (default) → use the env value. `off` / `log` / `apply` mirror the env modes for one request. `apply_relax` is opt-in here only: same as `apply` but on zero results, the softest must constraint is dropped and the filter retried, repeating until non-zero or all dropped (climate → drive_time → condition → views → recreation). Dropped axes are logged."
    }
  },
  "type": "object",
  "required": [
    "query"
  ]
}