{
  "openapi": "3.1.0",
  "info": {
    "title": "Propertio javni API",
    "version": "0.1.0",
    "description": "Javni API za sajtove agencija za nekretnine i partnere: oglasi, agencija, agenti, upiti, zahtevi za pokazivanje, AI asistent, feedovi i webhookovi.\n\nAutentikacija: zaglavlje `x-api-key: pk_…` (ključ agencije). Uputstva: https://developers.propertio.rs\n\nOvaj dokument je izveden iz punog OpenAPI izvoza backend-a i sadrži samo javni deo (putanje `/v1/…`) plus ručno opisane feedove i webhookove.",
    "contact": {
      "name": "Propertio",
      "url": "https://developers.propertio.rs",
      "email": "info@propertio.rs"
    }
  },
  "servers": [
    {
      "url": "https://app.propertio.rs/api",
      "description": "Produkcija"
    },
    {
      "url": "https://app.test.propertio.rs/api",
      "description": "Test okruženje"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Agencija",
      "description": "Profil agencije (ili filijale) i agenti."
    },
    {
      "name": "Oglasi",
      "description": "Pretraga i detalj aktivnih oglasa koji su objavljeni na sajt."
    },
    {
      "name": "Upiti i pokazivanja",
      "description": "Kontakt forma i zahtev za pokazivanje; stižu u CRM i kalendar agencije."
    },
    {
      "name": "Asistent i glas",
      "description": "AI asistent za posetioce sajta i izgovor odgovora (potrebna je opcija plana „ai“)."
    },
    {
      "name": "Feedovi",
      "description": "XML / JSON / CSV feedovi zaštićeni tokenom, za portale i sopstvene integracije. Link i token daje Propertio tim (ili vlasnik agencije u režimu za programere)."
    },
    {
      "name": "Webhookovi",
      "description": "Događaji koje Propertio šalje na vaš URL (veza „Website webhook“)."
    }
  ],
  "paths": {
    "/v1/agency": {
      "get": {
        "summary": "Agency profile",
        "tags": [
          "Agencija"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "phone": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "website": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "address": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "logoUrl": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "defaultCurrency": {
                      "type": "string"
                    },
                    "branch": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "slug": {
                          "type": "string"
                        },
                        "accent": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "id",
                        "name",
                        "slug",
                        "accent"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "name",
                    "slug",
                    "email",
                    "phone",
                    "website",
                    "address",
                    "logoUrl",
                    "defaultCurrency"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getV1Agency"
      }
    },
    "/v1/properties": {
      "get": {
        "summary": "Search active properties",
        "tags": [
          "Oglasi"
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "in": "query",
            "name": "page",
            "required": false
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "in": "query",
            "name": "pageSize",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "in": "query",
            "name": "q",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "apartment",
                "house",
                "land",
                "commercial",
                "garage",
                "office",
                "other"
              ]
            },
            "in": "query",
            "name": "type",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "sale",
                "rent"
              ]
            },
            "in": "query",
            "name": "dealType",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "city",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "municipality",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "priceMin",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "priceMax",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "areaMin",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "areaMax",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "roomsMin",
            "required": false
          },
          {
            "schema": {
              "type": "number"
            },
            "in": "query",
            "name": "roomsMax",
            "required": false
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "query",
            "name": "features",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$"
            },
            "in": "query",
            "name": "bbox",
            "required": false
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "newest",
                "oldest",
                "priceAsc",
                "priceDesc",
                "areaAsc",
                "areaDesc",
                "relevance"
              ],
              "default": "newest"
            },
            "in": "query",
            "name": "sort",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PublicProperty"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "items",
                    "page",
                    "pageSize",
                    "total"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getV1Properties"
      }
    },
    "/v1/properties/{idOrSlug}": {
      "get": {
        "summary": "Get one active property by id or slug",
        "tags": [
          "Oglasi"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 240
            },
            "in": "path",
            "name": "idOrSlug",
            "required": true
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicProperty"
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Oglas ne postoji, nije aktivan ili nije objavljen na sajt",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getV1PropertiesByIdOrSlug"
      }
    },
    "/v1/agents": {
      "get": {
        "summary": "Agents of the agency (active users with role agent/admin/owner)",
        "tags": [
          "Agencija"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "phone": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "email": {
                        "type": "string"
                      },
                      "activeListings": {
                        "type": "number"
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "phone",
                      "email",
                      "activeListings"
                    ],
                    "additionalProperties": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getV1Agents"
      }
    },
    "/v1/assistant": {
      "post": {
        "summary": "Website assistant for visitors: finds listings, answers about them, books viewing requests and inquiries (needs the 'ai' plan feature)",
        "tags": [
          "Asistent i glas"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "messages": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "role": {
                          "type": "string",
                          "enum": [
                            "user",
                            "assistant"
                          ]
                        },
                        "content": {
                          "type": "string",
                          "maxLength": 2000
                        }
                      },
                      "required": [
                        "role",
                        "content"
                      ],
                      "additionalProperties": false
                    },
                    "minItems": 1,
                    "maxItems": 30
                  },
                  "language": {
                    "type": "string",
                    "enum": [
                      "sr",
                      "en"
                    ],
                    "default": "sr"
                  },
                  "visitor": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "maxLength": 200
                      },
                      "phone": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "maxLength": 40
                      },
                      "email": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "maxLength": 200
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "required": [
                  "messages"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reply": {
                      "type": "string"
                    },
                    "speechToken": {
                      "type": "string"
                    },
                    "actions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "result": {}
                        },
                        "required": [
                          "name"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "listings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "reference": {
                            "type": "string"
                          },
                          "slug": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "reference",
                          "slug",
                          "title",
                          "price",
                          "currency"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "reply",
                    "speechToken",
                    "actions",
                    "listings"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan agencije ne uključuje opciju „ai“ (LICENCE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "AI nije uključen ili trenutno nije dostupan (AI_DISABLED / AI_UNAVAILABLE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postV1Assistant"
      }
    },
    "/v1/voice/speak": {
      "post": {
        "summary": "Speak a short text in the agency voice (mp3) for voice assistants on the agency website; needs the 'ai' plan feature",
        "tags": [
          "Asistent i glas"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 600
                  },
                  "language": {
                    "type": "string",
                    "enum": [
                      "sr",
                      "en"
                    ],
                    "default": "sr"
                  },
                  "token": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 64
                  }
                },
                "required": [
                  "text",
                  "token"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh"
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan agencije ne uključuje opciju „ai“ (LICENCE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Glas nije podešen na serveru (VOICE_DISABLED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postV1VoiceSpeak"
      }
    },
    "/v1/voice/confirmation": {
      "get": {
        "summary": "Spoken confirmation for the website ('an agent will contact you'), mp3, cached",
        "tags": [
          "Asistent i glas"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "sr",
                "en"
              ],
              "default": "sr"
            },
            "in": "query",
            "name": "language",
            "required": false
          }
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Uspeh"
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Glas nije podešen na serveru (VOICE_DISABLED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getV1VoiceConfirmation"
      }
    },
    "/v1/viewing-requests": {
      "post": {
        "summary": "A website visitor asks to view a listing at a preferred time; lands in the agency calendar as 'requested'",
        "tags": [
          "Upiti i pokazivanja"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "propertyId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "phone": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 40
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "email",
                    "maxLength": 200
                  },
                  "preferredAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 2000,
                    "default": ""
                  },
                  "website": {
                    "type": "string",
                    "maxLength": 0
                  },
                  "consent": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                },
                "required": [
                  "propertyId",
                  "name",
                  "phone",
                  "preferredAt",
                  "consent"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "201": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "requested"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "status"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Oglas ne postoji ili nije vidljiv sajtu",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postV1ViewingRequests"
      }
    },
    "/v1/inquiries": {
      "post": {
        "summary": "Submit a contact / property inquiry from the website",
        "tags": [
          "Upiti i pokazivanja"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "propertyId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "email",
                    "maxLength": 200
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 40
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 5000,
                    "default": ""
                  },
                  "website": {
                    "type": "string",
                    "maxLength": 0
                  },
                  "consent": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                },
                "required": [
                  "name",
                  "consent"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "201": {
            "description": "Uspeh",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Neispravan zahtev (error: VALIDATION ili BAD_REQUEST)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Nedostaje ili je neispravan `x-api-key` (UNAUTHORIZED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ključ nema potreban opseg, agencija je suspendovana ili Origin nije dozvoljen (FORBIDDEN)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "propertyId ne postoji ili nije vidljiv sajtu",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Previše zahteva. Pogledajte `retry-after` i `x-ratelimit-*` zaglavlja. Isti status vraćaju i mesečni limiti AI_QUOTA / VOICE_QUOTA.",
            "headers": {
              "retry-after": {
                "schema": {
                  "type": "integer"
                },
                "description": "Sekunde do sledećeg pokušaja"
              },
              "x-ratelimit-limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "x-ratelimit-reset": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postV1Inquiries"
      }
    },
    "/plugins/generic-feed/feed/{channelId}.json": {
      "get": {
        "operationId": "getGenericFeedJson",
        "tags": [
          "Feedovi"
        ],
        "summary": "Generički feed (JSON)",
        "description": "Svi aktivni oglasi objavljeni na ovaj kanal, do 5.000. `{ generatedAt, count, items: FlatListing[] }`.",
        "security": [],
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Id kanala (veze) u Propertiju.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Tajni token feeda (deo linka koji dobijate uz feed).",
            "schema": {
              "type": "string",
              "minLength": 16
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feed (keširan do 5 minuta)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "generatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FlatListing"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Pogrešan token ili kanal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "forbidden"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/plugins/generic-feed/feed/{channelId}.xml": {
      "get": {
        "operationId": "getGenericFeedXml",
        "tags": [
          "Feedovi"
        ],
        "summary": "Generički feed (XML)",
        "description": "`<listings><listing>…</listing></listings>`; `features/feature` i `images/image` su liste. Polja kao u FlatListing.",
        "security": [],
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Id kanala (veze) u Propertiju.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Tajni token feeda (deo linka koji dobijate uz feed).",
            "schema": {
              "type": "string",
              "minLength": 16
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feed (keširan do 5 minuta)",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Pogrešan token ili kanal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "forbidden"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/plugins/generic-feed/feed/{channelId}.csv": {
      "get": {
        "operationId": "getGenericFeedCsv",
        "tags": [
          "Feedovi"
        ],
        "summary": "Generički feed (CSV)",
        "description": "Zaglavlje: reference,title,type,deal,status,price,currency,areaSqm,rooms,floor,yearBuilt,city,municipality,street,streetNumber,lat,lng,features,images,description,updatedAt. Liste su spojene znakom `|`.",
        "security": [],
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Id kanala (veze) u Propertiju.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Tajni token feeda (deo linka koji dobijate uz feed).",
            "schema": {
              "type": "string",
              "minLength": 16
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feed (keširan do 5 minuta)",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Pogrešan token ili kanal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "forbidden"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/plugins/4zida/feed/{channelId}.xml": {
      "get": {
        "operationId": "get4zidaFeedByChannel",
        "tags": [
          "Feedovi"
        ],
        "summary": "4zida XML feed (po id-u kanala)",
        "description": "Feed u zvaničnom 4zida formatu (https://api.4zida.rs/v6/doc/xml). Oglasi koje bi 4zida odbila (nedostaje struktura, ulica, slike, nepoznato mesto, cena koja nije u EUR) izostavljeni su. Za portal koristite čitljiv link `/xml/4zida/{feedSlug}.xml`.",
        "security": [],
        "parameters": [
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "description": "Id kanala (veze) u Propertiju.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Tajni token feeda (deo linka koji dobijate uz feed).",
            "schema": {
              "type": "string",
              "minLength": 16
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feed (keširan do 5 minuta)",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Pogrešan token ili kanal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "forbidden"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Kanal ne postoji",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "unknown channel"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/xml/4zida/{feedSlug}.xml": {
      "servers": [
        {
          "url": "https://app.propertio.rs",
          "description": "Produkcija (bez /api)"
        },
        {
          "url": "https://app.test.propertio.rs",
          "description": "Test okruženje (bez /api)"
        }
      ],
      "get": {
        "operationId": "get4zidaFeedReadable",
        "tags": [
          "Feedovi"
        ],
        "summary": "4zida XML feed (čitljiv, trajni link)",
        "security": [],
        "description": "Isti feed kao `/plugins/4zida/feed/{channelId}.xml`, pod imenom koje se ne menja (`feedSlug` = slug agencije u trenutku povezivanja). Token određuje kanal. nginx ga prosleđuje na `/plugins/4zida/x/{feedSlug}.xml`.",
        "parameters": [
          {
            "name": "feedSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "description": "Tajni token feeda (deo linka koji dobijate uz feed).",
            "schema": {
              "type": "string",
              "minLength": 16
            }
          }
        ],
        "responses": {
          "200": {
            "description": "4zida XML",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Pogrešan token ili kanal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "examples": [
                        "forbidden"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "property.upserted": {
      "post": {
        "operationId": "webhook-property.upserted",
        "summary": "Oglas objavljen ili izmenjen",
        "description": "Šalje se za svaki oglas koji je objavljen ili se promenio (uključujući nove obrađene slike). Jedan oglas po zahtevu.",
        "tags": [
          "Webhookovi"
        ],
        "parameters": [
          {
            "name": "x-propertio-event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "property.upserted",
                "property.removed",
                "ping"
              ]
            }
          },
          {
            "name": "x-propertio-signature",
            "in": "header",
            "required": true,
            "description": "`sha256=` + HEX HMAC-SHA256 sirovog tela zahteva, ključ je `secret` veze.",
            "schema": {
              "type": "string",
              "pattern": "^sha256=[0-9a-f]{64}$"
            }
          },
          {
            "name": "x-propertio-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix vreme u milisekundama. NIJE deo potpisa; za zaštitu od ponavljanja koristite potpisano polje `sentAt` iz tela.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "user-agent",
            "in": "header",
            "schema": {
              "type": "string",
              "examples": [
                "Propertio-Webhook/1.0"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "event": "property.upserted",
                "sentAt": "2026-10-20T10:00:00.000Z",
                "channelId": "6f1c…",
                "agencyId": "0b7e…",
                "property": {
                  "id": "1d2e…",
                  "reference": "PR-1042",
                  "slug": "trosoban-stan-vracar-pr-1042",
                  "title": "Trosoban stan, Vračar",
                  "status": "active",
                  "price": 245000,
                  "currency": "EUR"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Primljeno. Bilo koji 2xx odgovor znači uspeh; sve ostalo (ili 20 s bez odgovora) je neuspeh."
          }
        }
      }
    },
    "property.removed": {
      "post": {
        "operationId": "webhook-property.removed",
        "summary": "Oglas skinut",
        "description": "Oglas više nije aktivan, obrisan je ili skinut sa ove veze. `externalId` je id oglasa u Propertiju.",
        "tags": [
          "Webhookovi"
        ],
        "parameters": [
          {
            "name": "x-propertio-event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "property.upserted",
                "property.removed",
                "ping"
              ]
            }
          },
          {
            "name": "x-propertio-signature",
            "in": "header",
            "required": true,
            "description": "`sha256=` + HEX HMAC-SHA256 sirovog tela zahteva, ključ je `secret` veze.",
            "schema": {
              "type": "string",
              "pattern": "^sha256=[0-9a-f]{64}$"
            }
          },
          {
            "name": "x-propertio-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix vreme u milisekundama. NIJE deo potpisa; za zaštitu od ponavljanja koristite potpisano polje `sentAt` iz tela.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "user-agent",
            "in": "header",
            "schema": {
              "type": "string",
              "examples": [
                "Propertio-Webhook/1.0"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "event": "property.removed",
                "sentAt": "2026-10-20T10:05:00.000Z",
                "channelId": "6f1c…",
                "agencyId": "0b7e…",
                "externalId": "1d2e…"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Primljeno. Bilo koji 2xx odgovor znači uspeh; sve ostalo (ili 20 s bez odgovora) je neuspeh."
          }
        }
      }
    },
    "ping": {
      "post": {
        "operationId": "webhook-ping",
        "summary": "Provera veze",
        "description": "Šalje se kada se u aplikaciji klikne „Proveri vezu“.",
        "tags": [
          "Webhookovi"
        ],
        "parameters": [
          {
            "name": "x-propertio-event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "property.upserted",
                "property.removed",
                "ping"
              ]
            }
          },
          {
            "name": "x-propertio-signature",
            "in": "header",
            "required": true,
            "description": "`sha256=` + HEX HMAC-SHA256 sirovog tela zahteva, ključ je `secret` veze.",
            "schema": {
              "type": "string",
              "pattern": "^sha256=[0-9a-f]{64}$"
            }
          },
          {
            "name": "x-propertio-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix vreme u milisekundama. NIJE deo potpisa; za zaštitu od ponavljanja koristite potpisano polje `sentAt` iz tela.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "user-agent",
            "in": "header",
            "schema": {
              "type": "string",
              "examples": [
                "Propertio-Webhook/1.0"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              },
              "example": {
                "event": "ping",
                "sentAt": "2026-10-20T09:59:00.000Z",
                "channelId": "6f1c…",
                "agencyId": "0b7e…"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Primljeno. Bilo koji 2xx odgovor znači uspeh; sve ostalo (ili 20 s bez odgovora) je neuspeh."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Ključ agencije oblika `pk_xxxxxxxx.<tajna>`. Pravi ga Propertio tim ili agencija u aplikaciji (Portali i sajt → Vaš sajt → Ključevi za sajt, uz režim za programere). Opsezi: `public:read`, `public:inquiry`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "statusCode",
          "error",
          "message"
        ],
        "properties": {
          "statusCode": {
            "type": "integer",
            "examples": [
              404
            ]
          },
          "error": {
            "type": "string",
            "description": "Mašinski kod greške: VALIDATION, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, LICENCE, AI_QUOTA, VOICE_QUOTA, AI_DISABLED, AI_UNAVAILABLE, VOICE_DISABLED, INTERNAL …",
            "examples": [
              "NOT_FOUND"
            ]
          },
          "message": {
            "type": "string",
            "examples": [
              "Property not found"
            ]
          },
          "details": {
            "description": "Dodatni podaci; kod VALIDATION lista problema po poljima."
          }
        }
      },
      "FlatListing": {
        "type": "object",
        "description": "Ravan zapis oglasa u generičkom feedu (plugins/generic-feed `FlatListing`). Slike su potpisani linkovi varijante `large` (važe 30 dana).",
        "required": [
          "id",
          "reference",
          "title",
          "type",
          "deal",
          "status",
          "price",
          "currency",
          "city",
          "images",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "apartment",
              "house",
              "land",
              "commercial",
              "garage",
              "office",
              "other"
            ]
          },
          "deal": {
            "type": "string",
            "enum": [
              "sale",
              "rent"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ]
          },
          "price": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR",
              "RSD",
              "USD"
            ]
          },
          "pricePerSqm": {
            "type": [
              "number",
              "null"
            ]
          },
          "areaSqm": {
            "type": "number"
          },
          "landAreaSqm": {
            "type": [
              "number",
              "null"
            ]
          },
          "rooms": {
            "type": [
              "number",
              "null"
            ]
          },
          "bedrooms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "bathrooms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "floor": {
            "type": [
              "integer",
              "null"
            ]
          },
          "totalFloors": {
            "type": [
              "integer",
              "null"
            ]
          },
          "yearBuilt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "heating": {
            "type": [
              "string",
              "null"
            ]
          },
          "furnished": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "yes",
              "no",
              "semi",
              null
            ]
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "country": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "municipality": {
            "type": [
              "string",
              "null"
            ]
          },
          "neighborhood": {
            "type": [
              "string",
              "null"
            ]
          },
          "street": {
            "type": [
              "string",
              "null"
            ],
            "description": "null kada je uključeno „sakrij adresu“"
          },
          "streetNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lng": {
            "type": [
              "number",
              "null"
            ]
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PublicProperty": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 3,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 10000,
            "default": ""
          },
          "type": {
            "type": "string",
            "enum": [
              "apartment",
              "house",
              "land",
              "commercial",
              "garage",
              "office",
              "other"
            ]
          },
          "dealType": {
            "type": "string",
            "enum": [
              "sale",
              "rent"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "reserved",
              "sold",
              "rented",
              "archived"
            ],
            "default": "draft"
          },
          "price": {
            "type": "number",
            "minimum": 0
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR",
              "RSD",
              "USD"
            ],
            "default": "EUR"
          },
          "pricePerSqm": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "areaSqm": {
            "type": "number",
            "minimum": 0
          },
          "landAreaSqm": {
            "type": [
              "number",
              "null"
            ],
            "exclusiveMinimum": true,
            "minimum": 0
          },
          "rooms": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "bedrooms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "bathrooms": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "floor": {
            "type": [
              "integer",
              "null"
            ]
          },
          "totalFloors": {
            "type": [
              "integer",
              "null"
            ]
          },
          "yearBuilt": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1500,
            "maximum": 2100
          },
          "heating": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "central",
              "gas",
              "electric",
              "floor",
              "heat_pump",
              "wood",
              "none",
              "other"
            ]
          },
          "furnished": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "yes",
              "no",
              "semi"
            ]
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 50
            },
            "maxItems": 100,
            "default": []
          },
          "location": {
            "type": "object",
            "properties": {
              "country": {
                "type": "string",
                "default": "RS"
              },
              "city": {
                "type": "string",
                "maxLength": 100
              },
              "municipality": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "neighborhood": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "street": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "streetNumber": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "postalCode": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "lat": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": -90,
                "maximum": 90
              },
              "lng": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": -180,
                "maximum": 180
              }
            },
            "required": [
              "city"
            ],
            "additionalProperties": false
          },
          "agentId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "branchId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "propertyId": {
                  "type": "string",
                  "format": "uuid"
                },
                "position": {
                  "type": "integer"
                },
                "alt": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "isCover": {
                  "type": "boolean"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "processing",
                    "ready",
                    "failed"
                  ]
                },
                "originalName": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "variants": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string"
                      },
                      "width": {
                        "type": "integer"
                      },
                      "height": {
                        "type": "integer"
                      },
                      "bytes": {
                        "type": "integer"
                      },
                      "format": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "name",
                      "url",
                      "width",
                      "height",
                      "bytes",
                      "format"
                    ],
                    "additionalProperties": false
                  }
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "required": [
                "id",
                "propertyId",
                "position",
                "alt",
                "isCover",
                "status",
                "originalName",
                "variants",
                "createdAt"
              ],
              "additionalProperties": false
            },
            "default": []
          },
          "imageCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "publishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "agent": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "phone": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "phone",
              "email"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "title",
          "type",
          "dealType",
          "price",
          "areaSqm",
          "location",
          "id",
          "reference",
          "slug",
          "createdAt",
          "updatedAt",
          "publishedAt",
          "agent"
        ],
        "additionalProperties": false
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "event",
          "sentAt",
          "channelId",
          "agencyId"
        ],
        "description": "Telo webhook zahteva (plugins/webhook `WebhookEvent`). `property` je oglas bez internih polja; slike (samo obrađene, bez varijante original) su potpisani linkovi koji važe 30 dana, i samo ako je u vezi uključeno „includeImages“.",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "property.upserted",
              "property.removed",
              "ping"
            ]
          },
          "sentAt": {
            "type": "string",
            "format": "date-time"
          },
          "channelId": {
            "type": "string",
            "format": "uuid"
          },
          "agencyId": {
            "type": "string",
            "format": "uuid"
          },
          "property": {
            "type": "object",
            "description": "Samo kod property.upserted. Polja kao PublicProperty, ali bez objekta `agent` (ima `agentId`) i sa poljem `attributes` (dodatna polja za portale); `images` izostaje ako je includeImages isključen. Sa opcijom includeInternal (samo za sopstvene sisteme) stižu i interna polja oglasa.",
            "additionalProperties": true
          },
          "externalId": {
            "type": "string",
            "description": "Samo kod property.removed: id oglasa."
          }
        }
      }
    }
  }
}
