{"openapi":"3.1.0","info":{"title":"Immoprobe API","version":"1","description":"Immoprobe is a system of record for German residential investment properties: a deterministic calculator (cashflow, yields, AfA, projections), geodata from public sources (Bodenrichtwert, flood and noise zones) and the user's saved objects with notes. It runs no language model of its own; the user's agent does the reasoning through this API or the MCP server at /mcp, which exposes the operations marked `x-mcp`."},"servers":[{"url":"https://api.immoprobe.de"},{"url":"https://api.stage.immoprobe.de"}],"tags":[{"name":"calc","description":"Stateless calculations. No account needed."},{"name":"geo","description":"The baked data layers: tiles for the map, zones at a point."},{"name":"objects","description":"The user's saved properties."},{"name":"notes","description":"Notes attached to an object — the place for an agent's verdict."},{"name":"lists","description":"Named lists an object can belong to."},{"name":"account","description":"Who am I, quotas, tokens."}],"components":{"securitySchemes":{"bearerToken":{"type":"http","scheme":"bearer","description":"A personal access token or an OAuth 2.1 access token issued to a connected app. Scopes: objects:read, objects:write, geo:read."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"session","description":"The web app's session, set by the sign-in code or link; never used by agents."}},"schemas":{"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"time":{"type":"string","format":"date-time"}},"required":["status","time"]},"Problem":{"type":"object","properties":{"code":{"type":"string","enum":["validation_failed","unauthorized","forbidden","not_found","conflict","quota_exceeded","rate_limited","payload_too_large","internal"]},"message":{"type":"string","description":"What went wrong and what to do next, written for the reader — a person or an agent"},"details":{"type":"object","additionalProperties":{},"description":"Field errors for validation_failed; limit/used for quotas; retryAfter (seconds) for rate_limited"}},"required":["code","message"]},"Calculation":{"type":"object","properties":{"input":{"$ref":"#/components/schemas/PropertyInput"},"result":{"$ref":"#/components/schemas/CalculationResult"},"projection":{"type":"array","items":{"$ref":"#/components/schemas/ProjectionRow"},"description":"Milestone years (1–5, 10, 15, 20, 25, 30) of a 30-year projection with the input's growth rates, plus the first year a sale is past the Spekulationsfrist (§ 23 EStG) when it is not one of them: year ten bought ready, a year earlier per full year of Bauphase, since the ten years run from the purchase contract; capitalGainsTax is 0 from that row on"},"equityIrr":{"type":["number","null"],"description":"Internal rate of return on the equity over the first 10 years, in percent p.a.: every year's after-tax cashflow, then a sale at the end of year 10 at the indexed value, after the remaining loan and § 23. Rests on the growth-rate assumptions; selling costs are not modelled. Null when there is no equity or no rate fits"},"assumed":{"type":"array","items":{"type":"string"},"description":"Fields that were not supplied and took a default the calculation reads — assumptions, to be shown as such. A default it never reads is not listed: the broker's rate without a broker, the KfW conditions without a tranche, the growth rates at zero"},"verdict":{"$ref":"#/components/schemas/Verdict"},"summary":{"type":"array","items":{"$ref":"#/components/schemas/SummaryLine"},"description":"The calculation in a few lines of facts, in the requested language: object (with its address when known), side costs, financing, rent, yields, first-year cashflow, depreciation and tax effect, dates; the last line names the assumptions, and a value that is one carries a *. Nothing in it judges — the verdict does that. Quote it rather than recomputing."},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/Warning"},"description":"Where the input is not something the market or the law offers — a KfW term or Anlaufjahre outside the KfW grid, a KfW amount above the loan, the § 7b route on a building over 5,200 €/m² (the allowance is left out). The maths ran as entered otherwise; show them to the user"},"shareUrl":{"type":"string","format":"uri","description":"Opens this calculation in the web calculator with charts and the year table"}},"required":["input","result","projection","equityIrr","assumed","verdict","summary","warnings","shareUrl"]},"PropertyInput":{"type":"object","properties":{"purchasePrice":{"type":"number","minimum":0,"description":"Kaufpreis, EUR"},"livingArea":{"type":"number","minimum":1,"description":"Wohnfläche, m²"},"yearBuilt":{"type":"number","default":1998,"description":"Baujahr; decides the AfA rate"},"stateCode":{"type":"string","enum":["BW","BY","BE","BB","HB","HH","HE","MV","NI","NW","RP","SL","SN","ST","SH","TH"],"description":"Bundesland, two-letter code (BE, NW, BY …); sets the Grunderwerbsteuer"},"address":{"type":"string","description":"Free-text address, a label only"},"notaryPercent":{"type":"number","minimum":0,"default":1.5,"description":"Notar, % of the purchase price"},"landRegistryPercent":{"type":"number","minimum":0,"default":0.5,"description":"Grundbuch, % of the purchase price"},"brokerPercent":{"type":"number","minimum":0,"default":3.57,"description":"Käuferprovision, % of the purchase price; counts only with hasBroker"},"hasBroker":{"type":"boolean","default":false,"description":"Whether a Provision is due; \"provisionsfrei\" is false"},"movablesInPrice":{"type":"number","minimum":0,"default":0,"description":"Inventar (Einbauküche, furniture) inside the purchase price, EUR; outside the Grunderwerbsteuer base"},"movablesExtra":{"type":"number","minimum":0,"default":0,"description":"Inventar bought on top of the purchase price (Ablöse), EUR"},"movablesUsefulLife":{"type":"number","minimum":1,"default":10,"description":"Useful life of the Inventar, years: 10 for a kitchen, 13 for furniture"},"renovationCost":{"type":"number","minimum":0,"default":0,"description":"Renovierung right after the purchase, EUR incl. VAT; the 15 % rule decides AfA or deduction in year one"},"coldRent":{"type":"number","minimum":0,"description":"Kaltmiete per month, EUR"},"parkingRent":{"type":"number","minimum":0,"default":0,"description":"Stellplatz or garage rent, EUR per month, apart from coldRent"},"serviceCharge":{"type":"number","minimum":0,"default":250,"description":"Hausgeld, the whole monthly charge, EUR"},"serviceChargeRecoverable":{"type":"number","minimum":0,"default":150,"description":"Umlagefähiger Teil of the Hausgeld the tenant reimburses, per month, EUR"},"otherCosts":{"type":"number","minimum":0,"default":0,"description":"Running costs outside the Hausgeld (Sondereigentumsverwaltung, insurance, upkeep), EUR per month"},"serviceChargeReserve":{"type":"number","minimum":0,"default":0,"description":"Erhaltungsrücklage part of the Hausgeld, EUR per month; not deducted from tax until the WEG spends it"},"equity":{"type":"number","minimum":0,"description":"Eigenkapital, EUR; when given, ltv is ignored"},"ltv":{"type":"number","minimum":0,"default":80,"description":"Beleihungsauslauf: loan on the object in % of the purchase price, as a bank quotes it; side costs come from equity. Used when equity is left out"},"interestRate":{"type":"number","minimum":0,"default":4,"description":"Sollzins, % per year"},"repaymentRate":{"type":"number","minimum":0,"default":2,"description":"Anfängliche Tilgung, % of the loan per year"},"fixedRateYears":{"type":"number","minimum":1,"maximum":40,"default":10,"description":"Sollzinsbindung of the bank loan, years (1–40); afterwards the loan continues at interestRate plus followUpRateDelta"},"followUpRateDelta":{"type":"number","default":0,"description":"Rate change after the Sollzinsbindung, percentage points; 0 continues the Sollzins. A scenario, never a listing figure"},"specialRepayment":{"type":"number","minimum":0,"default":0,"description":"Sondertilgung on the bank loan, EUR per year, during the Sollzinsbindung; a plan, never a listing figure"},"kfwAmount":{"type":"number","minimum":0,"description":"KfW-Darlehen, EUR: a tranche of the loan, never on top of it; 0 or left out means none. Send only what the user names"},"kfwInterestRate":{"type":"number","minimum":0,"default":2,"description":"Sollzins of the KfW tranche, % per year, for its ten-year Zinsbindung"},"kfwTermYears":{"type":"number","minimum":1,"default":35,"description":"Laufzeit of the KfW tranche, years (4–35)"},"kfwGraceYears":{"type":"number","minimum":0,"default":1,"description":"Tilgungsfreie Anlaufjahre of the KfW tranche: 1–2 on a term to 10 years, up to 3 to 25, up to 5 to 35"},"kfwRepaymentMode":{"type":"string","enum":["annuity","bullet"],"description":"annuity (default) or bullet, an endfälliges Darlehen of up to 10 years"},"handover":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Übergabe/Fertigstellung month of a new build bought before completion, YYYY-MM (a quarter as its last month); the months from the contract to it are the Bauphase. Left out, the property is ready"},"contractMonth":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Month of the Kaufvertrag, YYYY-MM, once it is signed; left out, this month. Anchors the Bauphase and the building age"},"commitmentFreeMonths":{"type":"number","minimum":0,"maximum":36,"default":12,"description":"Bereitstellungszinsfreie Zeit of the bank loan, months (0–36); counts only with a Bauphase"},"commitmentFeeRate":{"type":"number","minimum":0,"maximum":1,"default":0.25,"description":"Bereitstellungszins of the bank loan, % per month on the undrawn part once the bereitstellungszinsfreie Zeit is over (0–1; 3 % p. a. is 0.25); counts only with a Bauphase"},"landShare":{"type":"number","minimum":0,"maximum":100,"description":"Bodenwertanteil, % of the price that is land (not depreciable). Send it when the Kaufvertrag or Bauträger states the split; left out, derived from landValuePerSqm and the plot share, else 20 assumed"},"landValuePerSqm":{"type":"number","minimum":0,"description":"Bodenrichtwert, EUR per m² of plot, as lookup_address answers it"},"plotArea":{"type":"number","minimum":0,"default":0,"description":"Grundstücksfläche: the whole plot, m²"},"coOwnershipShare":{"type":"number","minimum":0,"default":0,"description":"Miteigentumsanteil numerator: 56 of \"56/10.000\"; 1 for a house bought whole"},"coOwnershipTotal":{"type":"number","minimum":0,"default":0,"description":"Miteigentumsanteil denominator: 10000 of \"56/10.000\"; 1 for a house bought whole"},"floorAreaRatio":{"type":"number","minimum":0,"description":"GFZ of the Bodenrichtwert zone from lookup_address; estimates the plot share when plotArea and Miteigentumsanteil are unknown"},"taxRate":{"type":"number","minimum":0,"maximum":100,"default":42,"description":"Grenzsteuersatz incl. Soli, %"},"depreciationVariant":{"type":"string","enum":["linear2","linear25","linear3","declining5","declining7b","monument","remainingLife"],"description":"AfA route: linear2, linear25, linear3, declining5, declining7b, monument, remainingLife; left out, the Baujahr decides"},"renovationShare":{"type":"number","minimum":0,"maximum":100,"default":60,"description":"Sanierungsanteil under § 7i, % of the building value; monument route only"},"remainingUsefulLife":{"type":"number","minimum":1,"default":30,"description":"Restnutzungsdauer from an appraisal, years; remainingLife route only"},"rentGrowth":{"type":"number","default":0,"description":"Mietsteigerung, % per year"},"costGrowth":{"type":"number","default":0,"description":"Kostensteigerung, % per year"},"valueGrowth":{"type":"number","default":0,"description":"Wertsteigerung, % per year"},"vacancyMonths":{"type":"number","minimum":0,"maximum":12,"default":0.5,"description":"Mietausfallwagnis, vacancy months per year"}},"required":["purchasePrice","livingArea","stateCode","coldRent"],"description":"The input after defaults were applied — what was actually calculated"},"CalculationResult":{"type":"object","properties":{"landTransferTaxPercent":{"type":"number"},"landTransferTax":{"type":"number"},"notaryFee":{"type":"number"},"landRegistryFee":{"type":"number"},"brokerFee":{"type":"number"},"closingCosts":{"type":"number","description":"Kaufnebenkosten in EUR: Grunderwerbsteuer, Notar, Grundbuch, Makler"},"closingCostsPercent":{"type":"number","description":"Kaufnebenkosten in percent of the purchase price"},"totalInvestment":{"type":"number","description":"Gesamtinvestition: purchase price plus side costs plus movables bought on top plus the renovation"},"renovationCost":{"type":"number","description":"Renovation right after the purchase, EUR incl. VAT; funded like the side costs, equity first"},"renovationLimit":{"type":"number","description":"The 15 % limit of § 6 Abs. 1 Nr. 1a EStG in EUR, net of VAT: 15 % of the building's acquisition cost; renovation within three years of the purchase above it is written off with the building"},"renovationCapitalized":{"type":"boolean","description":"True when the renovation's net amount exceeds renovationLimit: anschaffungsnahe Herstellungskosten, in the AfA base and in the acquisition cost of a sale"},"renovationDeduction":{"type":"number","description":"The renovation deducted as Erhaltungsaufwand in the first projected year, EUR; 0 when capitalised. Kept out of the first-year figures above, which stay recurring"},"equityEffective":{"type":"number","description":"Eigenkapital that actually funds the purchase, in EUR"},"loanAmount":{"type":"number","description":"Darlehen in EUR"},"loanToCost":{"type":"number","description":"Loan as a share of the total investment incl. side costs, in percent"},"ltv":{"type":"number","description":"Beleihungsauslauf: the loan on the object as a share of the purchase price, in percent — bank and KfW tranche together, less what the loan puts into the Inventar bought on top (movablesExtra); a loan that also funds the side costs reads above 100, a 110 % financing"},"bankLoanAmount":{"type":"number","description":"The part of the loan the bank lends at the bank conditions, in EUR"},"kfwLoanAmount":{"type":"number","description":"The KfW tranche of the loan in EUR, capped at the loan; 0 without one"},"rentPerYear":{"type":"number","description":"Kaltmiete and parking rent for the year"},"parkingRentPerYear":{"type":"number","description":"The parking rent inside rentPerYear"},"vacancyLossPerYear":{"type":"number"},"rentEffectivePerYear":{"type":"number"},"serviceChargePerYear":{"type":"number"},"serviceChargeRecoverablePerYear":{"type":"number"},"operatingCostsPerYear":{"type":"number","description":"The part of the Hausgeld the landlord carries — the one that costs money"},"otherCostsPerYear":{"type":"number","description":"Running costs outside the Hausgeld the landlord carries: Sondereigentumsverwaltung, insurance, upkeep; deducted when paid"},"reservePerYear":{"type":"number","description":"The Erhaltungsrücklage inside operatingCostsPerYear: paid, but not deducted from the taxable result"},"netRentPerYear":{"type":"number","description":"Nettomietertrag: rent after vacancy, operating costs and other costs"},"interestPerYear":{"type":"number"},"repaymentPerYear":{"type":"number"},"annuityPerYear":{"type":"number","description":"Annuität: interest plus repayment in the first year, bank and KfW tranche together"},"bankAnnuityPerYear":{"type":"number","description":"The bank tranche of the annuity: (Sollzins + Tilgung) % of the bank loan"},"kfwAnnuityPerYear":{"type":"number","description":"What the KfW tranche costs in the first year: only its interest while it is tilgungsfrei, otherwise the annuity that repays it within its term"},"kfwInterestPerYear":{"type":"number","description":"Interest on the KfW tranche in the first year"},"kfwRepaymentPerYear":{"type":"number","description":"Repayment of the KfW tranche in the first year; 0 while it is tilgungsfrei"},"cashflowPerMonth":{"type":"number"},"cashflowPerYear":{"type":"number"},"constructionMonths":{"type":"number","description":"Months from the purchase contract — contractMonth, else today — to the handover; 0 for a property that is ready"},"fixedRateYearsLeft":{"type":"number","description":"Years of the Sollzinsbindung left at the handover: fixedRateYears less the Bauphase, which ran on the same clock since the loan was signed with the contract — whole years, the nearest; the projection runs at the Anschlusszins from the year after"},"kfwTermYearsLeft":{"type":"number","description":"Years of the KfW Laufzeit left at the handover: kfwTermYears less the Bauphase, the Zusage having come with the contract — whole years, the nearest; the Anlaufjahre and the ten-year Zinsbindung shrink the same way"},"constructionDueAtContract":{"type":"number","description":"What the Bauträger bills with the first instalment, in EUR: the Kaufpreisraten of § 3 MaBV the build has reached by the contract, read off the months left against a typical build of 24 months from the Erdarbeiten to the handover"},"constructionStage":{"type":["string","null"],"enum":["earthworks","shell","envelope","installation","interior","handover",null],"description":"How far the build has come at the contract — earthworks, shell (Rohbau), envelope (roof and windows), installation (Rohinstallation), interior (Ausbau), handover — or null while the Erdarbeiten have not begun; an assumption from the months left"},"constructionInterest":{"type":"number","description":"Bauzeitzinsen: interest on the bank and KfW tranches as they are drawn during the build, in EUR"},"constructionCommitmentFees":{"type":"number","description":"Bereitstellungszinsen of the bank and Bereitstellungsprovision of KfW on the undrawn tranches, in EUR"},"constructionKfwCommitmentFee":{"type":"number","description":"The KfW part of the commitment fees: 0.15 % per month on the undrawn KfW tranche from the 13th month"},"constructionCosts":{"type":"number","description":"The Bauphase altogether, before tax, in EUR — the first cashflow of the investment, negative"},"constructionCostsAfterTax":{"type":"number","description":"The same after the marginal tax rate: interest and fees before letting are vorweggenommene Werbungskosten"},"landShareEffective":{"type":"number","description":"Bodenwertanteil the calculation ran with, in percent"},"priceSplit":{"type":"object","properties":{"method":{"type":"string","enum":["given","income","cost","default"],"description":"given — the land share was sent; income / cost — the Ertragswert- or Sachwertverfahren of the BMF Arbeitshilfe on the land value; default — nothing to go on, LAND_SHARE_DEFAULT"},"plotShareSource":{"type":["string","null"],"enum":["given","zone",null],"description":"given — plotArea × coOwnershipShare / coOwnershipTotal; zone — estimated from floorAreaRatio and the living area, an assumption"},"landValue":{"type":["number","null"],"description":"Bodenwert of the owner's share the split rests on, EUR"},"buildingValue":{"type":["number","null"],"description":"Gebäudewert the split rests on, EUR"}},"required":["method","plotShareSource","landValue","buildingValue"],"description":"How the land share came about — the Kaufpreisaufteilung the tax office would run"},"buildingShare":{"type":"number"},"depreciationBase":{"type":"number"},"buildingValue":{"type":"number"},"movablesInPrice":{"type":"number","description":"Movable assets named in the purchase contract, capped at the price"},"movablesExtra":{"type":"number","description":"Movable assets bought on top of the purchase price"},"movablesTotal":{"type":"number"},"movablesUsefulLife":{"type":"number"},"depreciationVariant":{"type":"string","enum":["linear2","linear25","linear3","declining5","declining7b","monument","remainingLife"]},"depreciationVariantDerived":{"type":"boolean","description":"True when the variant was derived from the Baujahr rather than chosen"},"depreciationSwitchYear":{"type":["number","null"],"description":"On the degressive routes (declining5, declining7b): the year after handover from which the deduction is straight-line — the residual over the remaining typified life (§ 7 Abs. 5a Satz 8), taken as soon as it exceeds 5 % of the residual, which is year 15; null on every other route"},"buildingDepreciationSeries":{"type":"array","items":{"type":"number"},"description":"Building depreciation per year after handover, index 0 being the first year"},"buildingDepreciationPerYear":{"type":"number"},"movablesDepreciationSeries":{"type":"array","items":{"type":"number"},"description":"Inventar depreciation per year after handover, index 0 being the first year"},"movablesDepreciationPerYear":{"type":"number"},"depreciationPerYear":{"type":"number","description":"AfA: building and Inventar depreciation in the first year after handover"},"depreciationTaxSavingFirstYear":{"type":"number","description":"What the first year's depreciation is worth at the marginal tax rate"},"taxableResultPerYear":{"type":"number"},"taxEffectPerYear":{"type":"number"},"cashflowPerYearAfterTax":{"type":"number"},"cashflowPerMonthAfterTax":{"type":"number"},"grossYield":{"type":"number","description":"Bruttomietrendite: yearly cold rent over the purchase price, in percent"},"netYield":{"type":"number","description":"Nettomietrendite: net rent over the total investment, in percent"},"returnOnEquity":{"type":"number","description":"(Cashflow before tax + repayment) / equity, in percent"},"returnOnEquityAfterTax":{"type":"number","description":"(Cashflow after tax + repayment) / equity, in percent"},"priceToRentFactor":{"type":"number","description":"Kaufpreisfaktor: purchase price divided by the yearly cold rent"}},"required":["landTransferTaxPercent","landTransferTax","notaryFee","landRegistryFee","brokerFee","closingCosts","closingCostsPercent","totalInvestment","renovationCost","renovationLimit","renovationCapitalized","renovationDeduction","equityEffective","loanAmount","loanToCost","ltv","bankLoanAmount","kfwLoanAmount","rentPerYear","parkingRentPerYear","vacancyLossPerYear","rentEffectivePerYear","serviceChargePerYear","serviceChargeRecoverablePerYear","operatingCostsPerYear","otherCostsPerYear","reservePerYear","netRentPerYear","interestPerYear","repaymentPerYear","annuityPerYear","bankAnnuityPerYear","kfwAnnuityPerYear","kfwInterestPerYear","kfwRepaymentPerYear","cashflowPerMonth","cashflowPerYear","constructionMonths","fixedRateYearsLeft","kfwTermYearsLeft","constructionDueAtContract","constructionStage","constructionInterest","constructionCommitmentFees","constructionKfwCommitmentFee","constructionCosts","constructionCostsAfterTax","landShareEffective","priceSplit","buildingShare","depreciationBase","buildingValue","movablesInPrice","movablesExtra","movablesTotal","movablesUsefulLife","depreciationVariant","depreciationVariantDerived","depreciationSwitchYear","buildingDepreciationSeries","buildingDepreciationPerYear","movablesDepreciationSeries","movablesDepreciationPerYear","depreciationPerYear","depreciationTaxSavingFirstYear","taxableResultPerYear","taxEffectPerYear","cashflowPerYearAfterTax","cashflowPerMonthAfterTax","grossYield","netYield","returnOnEquity","returnOnEquityAfterTax","priceToRentFactor"]},"ProjectionRow":{"type":"object","properties":{"year":{"type":"integer"},"rent":{"type":"number"},"operatingCosts":{"type":"number"},"interest":{"type":"number"},"repayment":{"type":"number"},"depreciation":{"type":"number"},"taxableResult":{"type":"number","description":"Taxable result: rent less running costs — the Erhaltungsrücklage excepted, deductible only once spent — interest and depreciation"},"taxEffect":{"type":"number","description":"Positive when the taxable loss reduces the tax bill"},"cashflowBeforeTax":{"type":"number"},"cashflowAfterTax":{"type":"number"},"cumulativeCashflow":{"type":"number","description":"Cumulative after-tax cashflow at the end of this year"},"remainingDebt":{"type":"number","description":"Restschuld: loan balance at the end of this year, bank and KfW tranche together"},"remainingDebtKfw":{"type":"number","description":"The KfW tranche of the Restschuld at the end of this year; 0 without one"},"propertyValue":{"type":"number","description":"What the property is worth at the end of this year, indexed"},"equity":{"type":"number","description":"The share of that value the owner holds outright"},"gainOnSale":{"type":"number","description":"What selling at the end of this year would have earned over the equity put in, before tax: the equity built plus the cash taken (the Bauphase costs included), less the equity invested. Selling costs are not modelled"},"capitalGainsTax":{"type":"number","description":"Spekulationssteuer, § 23 EStG: tax on the gain over the acquisition cost less the depreciation taken, at the marginal rate, while the sale falls within ten years of the purchase contract. The period runs from contract to contract, so the Bauphase counts towards it: 0 from year ten after handover on for a property that is ready, a year earlier per full year of Bauphase — see saleTaxFreeYear"},"gainOnSaleAfterTax":{"type":"number","description":"gainOnSale less capitalGainsTax"}},"required":["year","rent","operatingCosts","interest","repayment","depreciation","taxableResult","taxEffect","cashflowBeforeTax","cashflowAfterTax","cumulativeCashflow","remainingDebt","remainingDebtKfw","propertyValue","equity","gainOnSale","capitalGainsTax","gainOnSaleAfterTax"]},"Verdict":{"type":"object","properties":{"key":{"type":"string","enum":["pending","carries","taxFlips","taxSaves","subsidised","doesNotCarry"]},"text":{"type":"string","description":"One sentence in the language of the answer (`language`: de or en), the same wording as the web calculator"}},"required":["key","text"]},"SummaryLine":{"type":"object","properties":{"key":{"type":"string","enum":["object","costs","financing","rent","yield","cashflow","tax","dates","assumptions"]},"text":{"type":"string"}},"required":["key","text"]},"Warning":{"type":"object","properties":{"code":{"type":"string","enum":["kfwAmountCapped","kfwTermBelowMinimum","kfwTermAboveMaximum","kfwBulletAboveMaximum","kfwGraceBelowMinimum","kfwGraceAboveMaximum","special7bAboveCostLimit"]},"field":{"type":"string","description":"The input field the warning is about"},"text":{"type":"string","description":"One sentence in the language of the answer (`language`: de or en), the same wording as the web calculator"}},"required":["code","field","text"]},"Guide":{"type":"object","properties":{"revision":{"type":"string","description":"The contract revision the guide describes, YYYY-MM-DD"},"text":{"type":"string","description":"The guide as markdown"}},"required":["revision","text"]},"Comparison":{"type":"object","properties":{"objects":{"type":"array","items":{"$ref":"#/components/schemas/ObjectRow"}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/ComparisonRow"}},"url":{"type":"string","format":"uri","description":"The same comparison in the web app"}},"required":["objects","rows","url"]},"ObjectRow":{"type":"object","properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/ObjectStatus"},"source":{"$ref":"#/components/schemas/ObjectSource"},"externalId":{"type":"string"},"listingUrl":{"type":"string"},"title":{"type":"string"},"address":{"$ref":"#/components/schemas/Address"},"kpis":{"$ref":"#/components/schemas/Kpis"},"verdict":{"type":"string","enum":["pending","carries","taxFlips","taxSaves","subsidised","doesNotCarry"],"description":"The one-word verdict key; the sentence comes from calculateProperty"},"assumedCount":{"type":"integer","description":"How many input fields are assumptions"},"lists":{"type":"array","items":{"type":"string"},"description":"List names"},"noteCount":{"type":"integer"},"coverUrl":{"type":"string","format":"uri","description":"The cover photo's bytes, when one is set; needs the caller's credential"},"url":{"type":"string","format":"uri","description":"The object in the web app"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"discardedAt":{"type":"string","format":"date-time","description":"When it went to the trash; absent while active"}},"required":["id","status","source","title","address","kpis","verdict","assumedCount","lists","noteCount","url","createdAt","updatedAt"],"description":"The compact shape lists answer with: enough to pick, not to edit"},"ObjectStatus":{"type":"string","enum":["active","discarded"],"description":"active — in the list; discarded — in the trash (Papierkorb): hidden from lists, still known to check_known_listings so the listing is not saved again, deleted for good 90 days later"},"ObjectSource":{"type":"string","enum":["manual","immoscout24","immowelt","kleinanzeigen","neubaukompass","other"],"description":"Where the object came from. manual is the web form; neubaukompass is the new-build portal (its externalId is the unit or project id); other is any portal not listed — put its page in listingUrl"},"Address":{"type":"object","properties":{"formatted":{"type":"string","maxLength":200,"description":"One line as the listing shows it, e.g. \"Malplaquetstraße 27, 13347 Berlin\""},"street":{"type":"string","maxLength":120,"description":"Straße"},"houseNumber":{"type":"string","maxLength":20,"description":"Hausnummer; portals often hide it — leave it out rather than guess"},"postalCode":{"type":"string","pattern":"^\\d{5}$","description":"PLZ"},"city":{"type":"string","maxLength":80,"description":"Ort"},"district":{"type":"string","maxLength":80,"description":"Ortsteil / Stadtteil"},"stateCode":{"type":"string","enum":["BW","BY","BE","BB","HB","HH","HE","MV","NI","NW","RP","SL","SN","ST","SH","TH"],"description":"Bundesland; taken from input.stateCode when left out"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180},"precision":{"type":"string","enum":["house","street","district","city"],"description":"How exact the point is; street or worse means the house number was not known"}}},"Kpis":{"type":"object","properties":{"purchasePrice":{"type":"number","description":"Kaufpreis in EUR"},"livingArea":{"type":"number","description":"Wohnfläche in m²"},"pricePerSqm":{"type":"number","description":"Kaufpreis per m² of living area"},"grossYield":{"type":"number","description":"Bruttomietrendite in percent"},"netYield":{"type":"number","description":"Nettomietrendite in percent"},"cashflowPerMonth":{"type":"number","description":"Before tax, in EUR"},"cashflowPerMonthAfterTax":{"type":"number","description":"After income tax, in EUR"},"priceToRentFactor":{"type":"number","description":"Kaufpreisfaktor"},"returnOnEquity":{"type":"number","description":"Eigenkapitalrendite before tax, in percent"},"totalInvestment":{"type":"number","description":"Gesamtinvestition in EUR"},"ltv":{"type":"number","description":"Beleihungsauslauf in percent"},"equityIrr":{"type":["number","null"],"description":"Internal rate of return on the equity over 10 years, in percent p.a. — the same figure calculate_property answers; null when there is no equity or no rate fits"}},"required":["purchasePrice","livingArea","pricePerSqm","grossYield","netYield","cashflowPerMonth","cashflowPerMonthAfterTax","priceToRentFactor","returnOnEquity","totalInvestment","ltv","equityIrr"],"description":"The headline numbers every screen shows; the full result comes from calculateProperty on the object's input"},"ComparisonRow":{"type":"object","properties":{"metric":{"type":"string","description":"The result field the row shows, e.g. netYield"},"unit":{"type":"string"},"values":{"type":"array","items":{"type":"number"},"description":"One per object, in the order of objects"},"bestIndex":{"type":["integer","null"],"description":"Index of the best value, null when it does not matter or they tie"}},"required":["metric","unit","values","bestIndex"]},"CompareRequest":{"type":"object","properties":{"objectIds":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":5}},"required":["objectIds"]},"TileJson":{"type":"object","properties":{"tilejson":{"type":"string"},"tiles":{"type":"array","items":{"type":"string"},"description":"Tile URL templates; each carries the archive version as `?v=`"},"minzoom":{"type":"integer"},"maxzoom":{"type":"integer"},"bounds":{"type":"array","items":{"type":"number"}},"attribution":{"type":"string","description":"Licence attribution to show with the layer"},"vector_layers":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["tilejson","tiles"],"additionalProperties":{}},"Zones":{"type":"object","properties":{"zones":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/BrwZone"},{"$ref":"#/components/schemas/Designation"},{"$ref":"#/components/schemas/CensusCell"},{"$ref":"#/components/schemas/FloodZone"},{"$ref":"#/components/schemas/LocationClass"},{"$ref":"#/components/schemas/NoiseZone"},{"$ref":"#/components/schemas/TransitStation"},{"$ref":"#/components/schemas/ParcelZone"}]},"description":"Zones containing the point, shaped by the layer (BrwZone for brw, Designation for designations, CensusCell for census, FloodZone for flood, LocationClass for location — the nearest address point within 40 m, NoiseZone for noise — the nearest building within 40 m when the point lies in none, with its distance, TransitStation for transit — every station within 3 km, nearest first, ParcelZone for parcels — the Flurstück containing the point); empty outside the covered area"}},"required":["zones"]},"BrwZone":{"type":"object","properties":{"brw":{"type":"number","description":"Bodenrichtwert in EUR per m²"},"nutzung":{"type":"string"},"nutzung_text":{"type":["string","null"]},"stichtag":{"type":"string","description":"Valuation date the value refers to"},"gfz":{"type":["number","null"]},"verfahrensart":{"type":["string","null"]},"verfahrensgrund":{"type":["string","null"]},"beitragszustand":{"type":["string","null"]},"ort":{"type":["string","null"]},"name":{"type":["string","null"],"description":"Zone name where the valuation board gives one"},"entwicklung":{"type":["string","null"],"description":"Development state (VBORIS): B building land, R Rohbauland, E Bauerwartungsland, LF agriculture/forestry, SF other; null when the source has none"},"land":{"type":"string"},"src":{"type":"string"},"src_id":{"type":"string"},"history":{"type":"array","items":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"string"}]},"description":"[year, brw, nutzung-if-different] per Stichtag, ascending"},"variants":{"type":"array","items":{"type":"array","prefixItems":[{"type":"string"},{"type":"number"}]},"description":"[nutzung, brw] — the zone's values for other uses where the board sets several (Hamburg: EFH, GH, BH, LAD, PL next to the MFH headline); empty elsewhere"},"distance":{"type":"number","description":"Metres from the point to the zone: 0 when the zone contains the point; otherwise the nearest zone within 30 m answered, because the zones there are building blocks and the point was on the street — treat the value as an assumption"}},"required":["brw","nutzung","nutzung_text","stichtag","gfz","verfahrensart","verfahrensgrund","beitragszustand","ort","name","entwicklung","land","src","src_id","history","variants","distance"]},"Designation":{"type":"object","properties":{"kind":{"type":"string","enum":["social_conservation","urban_conservation","redevelopment","monument"],"description":"social_conservation: Milieuschutz (§ 172 (1) 2 BauGB) — conversion into condominiums and modernisation need consent, the district holds a right of first refusal; urban_conservation: § 172 (1) 1 — demolition and alteration need consent; redevelopment: Sanierungsgebiet (§ 136) — § 7h EStG depreciation, settlement charge in the comprehensive procedure; monument: listed (§ 7i EStG depreciation, alterations need consent)"},"name":{"type":["string","null"],"description":"Name of the area; monuments carry none"},"sub":{"type":["string","null"],"description":"redevelopment: comprehensive | simplified; monument: building | complex | ensemble | garden | archaeological; social_conservation: planned when the statute is still being drawn up (already binding under § 172 (2) BauGB)"},"since":{"type":["string","null"],"description":"ISO date the designation came into force, when the source records it"},"district":{"type":["string","null"]},"link":{"type":["string","null"],"description":"Link into the source register (monuments: Denkmaldatenbank)"},"land":{"type":"string"},"src":{"type":"string"},"src_id":{"type":"string"}},"required":["kind","name","sub","since","district","link","land","src","src_id"]},"CensusCell":{"type":"object","properties":{"id":{"type":"string","description":"INSPIRE grid cell id, e.g. CRS3035RES100mN3272400E4550400"},"res":{"type":"integer","description":"Cell size in metres; 100 for a lookup"},"population":{"type":["integer","null"]},"rent":{"type":["number","null"],"description":"Average net cold rent of rented flats in EUR per m² of living space (Zensus 2022, 15 May 2022); null when not published"},"rent_uncertain":{"type":"boolean","description":"True when the statistical office flags the value as unreliable (small cell)"},"vacancy":{"type":["number","null"],"description":"Vacancy rate of flats in %; null when the census did not publish it for the cell"},"vacancy_uncertain":{"type":"boolean"},"vacancy_active":{"type":["number","null"],"description":"Market-active vacancy (lettable within three months) in %; null when the census did not publish it for the cell"},"vacancy_active_uncertain":{"type":"boolean"},"owner_rate":{"type":["number","null"],"description":"Share of owner-occupied flats in %; null when the census did not publish it for the cell"},"owner_rate_uncertain":{"type":"boolean"},"avg_age":{"type":["number","null"],"description":"Average age of residents in years"},"avg_age_uncertain":{"type":"boolean"},"flat_area":{"type":["number","null"],"description":"Average living space per flat in m²"},"flat_area_uncertain":{"type":"boolean"}},"required":["id","res","population","rent","rent_uncertain","vacancy","vacancy_uncertain","vacancy_active","vacancy_active_uncertain","owner_rate","owner_rate_uncertain","avg_age","avg_age_uncertain","flat_area","flat_area_uncertain"]},"FloodZone":{"type":"object","properties":{"kind":{"type":"string","enum":["high","medium","low"],"description":"high: HQhäufig — a flood expected about every 10–20 years; medium: HQ100 — the 100-year flood the statutory Überschwemmungsgebiete follow, insurers rate it as a high hazard class; low: HQextrem — a rarer-than-100-year flood, dykes and walls failing. A point in a medium zone is in the low zone too"},"source":{"type":"string","enum":["fluvial","coastal"],"description":"fluvial: river flood; coastal: storm surge"},"land":{"type":["string","null"],"description":"Federal state that mapped the zone (AdV key, e.g. NW)"},"src":{"type":"string"},"src_id":{"type":"string"}},"required":["kind","source","land","src","src_id"]},"LocationClass":{"type":"object","properties":{"class":{"type":"string","enum":["simple","medium","good"],"description":"The Wohnlage of the city's rent index (Mietspiegel) at the nearest address: simple (einfach), medium (mittel), good (gut). It fixes which row of the index applies, and with it the ortsübliche Vergleichsmiete that caps a new letting (Mietpreisbremse: +10 %) and a rent rise"},"label":{"type":["string","null"],"description":"The index's own word for the class — einfach / mittel / gut in Berlin, normal / gut in Hamburg, whose two classes map to medium and good"},"index":{"type":"string","description":"The rent index the class comes from, e.g. \"Berliner Mietspiegel 2026\""},"east":{"type":"boolean","description":"True where the index keeps a separate table for the former East (Berlin)"},"address":{"type":["string","null"],"description":"The address the class was read at"},"plz":{"type":["string","null"]},"district":{"type":["string","null"]},"land":{"type":"string"},"src":{"type":"string"},"src_id":{"type":"string"},"distance":{"type":"number","description":"Metres from the point to that address — a few for a geocoded house; tens of metres means the pin is not on the house"},"referenceRent":{"$ref":"#/components/schemas/ReferenceRent"}},"required":["class","label","index","east","address","plz","district","land","src","src_id","distance"]},"ReferenceRent":{"type":"object","properties":{"index":{"type":"string","description":"The rent index the cell comes from, e.g. \"Berliner Mietspiegel 2026\""},"stichtag":{"type":"string","description":"The index's Stichtag, ISO"},"row":{"type":"string","description":"The row of the index table the flat falls into, as printed — cite it"},"low":{"type":"number","description":"Lower end of the span, EUR per m² net cold per month"},"mean":{"type":"number","description":"The mean of the cell: the ortsübliche Vergleichsmiete to start from, EUR per m²"},"high":{"type":"number","description":"Upper end of the span, EUR per m²"},"cap":{"type":"number","description":"The most a new letting may ask under the Mietpreisbremse (§ 556d BGB): mean + 10 %, EUR per m² — an orientation; first lettings of buildings ready after 1 Oct 2014 and comprehensively modernised flats are exempt"}},"required":["index","stichtag","row","low","mean","high","cap"],"description":"The index cell for the flat — present when yearBuilt and livingArea were given (a lookup) or known (an object) and the index has a row for them; absent for buildings newer than the index covers. The table assumes a fully fitted flat (bath, central heating)"},"NoiseZone":{"type":"object","properties":{"kind":{"type":"string","enum":["band","building"],"description":"band: a 5 dB isophone class of one source, the indicator set to its lower bound; building: the loudest facade of the building, both indicators"},"source":{"type":"string","enum":["road","rail","air","industry","total"],"description":"What makes the noise; total is every mapped source together (Berlin buildings)"},"lden":{"type":["number","null"],"description":"L_DEN, the day-evening-night level in dB(A); null when not mapped"},"lnight":{"type":["number","null"],"description":"L_Night, 22–6 h in dB(A); null when not mapped"},"road_lden":{"type":["number","null"],"description":"Road, L_DEN (buildings only) in dB(A); null when not mapped"},"road_lnight":{"type":["number","null"],"description":"Road, L_Night in dB(A); null when not mapped"},"rail_lden":{"type":["number","null"],"description":"Rail, L_DEN in dB(A); null when not mapped"},"rail_lnight":{"type":["number","null"],"description":"Rail, L_Night in dB(A); null when not mapped"},"air_lden":{"type":["number","null"],"description":"Aircraft, L_DEN in dB(A); null when not mapped"},"air_lnight":{"type":["number","null"],"description":"Aircraft, L_Night in dB(A); null when not mapped"},"land":{"type":["string","null"]},"src":{"type":"string"},"src_id":{"type":"string"},"distance":{"type":"number","description":"Metres from the point to the building: 0 when it contains the point, else the nearest building within 40 m answered (a building newer than the map has no contour, and the geocoder puts an address on the kerb)"}},"required":["kind","source","lden","lnight","road_lden","road_lnight","rail_lden","rail_lnight","air_lden","air_lnight","land","src","src_id","distance"]},"TransitStation":{"type":"object","properties":{"name":{"type":"string","description":"The station as the timetable names it"},"modes":{"type":"array","items":{"$ref":"#/components/schemas/TransitMode"},"description":"The modes serving the station"},"lines":{"type":"object","properties":{"subway":{"type":"array","items":{"type":"string"}},"suburban":{"type":"array","items":{"type":"string"}},"rail":{"type":"array","items":{"type":"string"}},"long_distance":{"type":"array","items":{"type":"string"}},"tram":{"type":"array","items":{"type":"string"}},"bus":{"type":"array","items":{"type":"string"}},"ferry":{"type":"array","items":{"type":"string"}}},"description":"Line names per mode, e.g. { \"subway\": [\"U6\"], \"bus\": [\"120\", \"147\"] }"},"lat":{"type":["number","null"],"description":"The station's position"},"lng":{"type":["number","null"]},"land":{"type":["string","null"]},"src":{"type":"string"},"src_id":{"type":"string"},"distance":{"type":"integer","description":"Metres from the point in a straight line (Luftlinie), not the walk"}},"required":["name","modes","lines","lat","lng","land","src","src_id","distance"]},"TransitMode":{"type":"string","enum":["subway","suburban","rail","long_distance","tram","bus","ferry"],"description":"subway: U-Bahn; suburban: S-Bahn; rail: regional trains (RE, RB); long_distance: ICE, IC, EC; tram; bus; ferry"},"ParcelZone":{"type":"object","properties":{"id":{"type":"string","description":"Flurstückskennzeichen as the cadastre writes it: state, Gemarkung, Flur, number, filled with _"},"number":{"type":["string","null"],"description":"Flurstücksnummer: \"57\", or Zähler/Nenner \"57/3\""},"district":{"type":["string","null"],"description":"Gemarkung"},"section":{"type":["string","null"],"description":"Flur; Hamburg and most of Sachsen have none"},"area":{"type":["number","null"],"description":"Amtliche Fläche in m², as the cadastre books it"},"land":{"type":"string"},"src":{"type":"string"}},"required":["id","number","district","section","area","land","src"]},"GeoLookup":{"type":"object","properties":{"best":{"$ref":"#/components/schemas/GeoPoint"},"candidates":{"type":"array","items":{"$ref":"#/components/schemas/GeoPoint"},"description":"Up to five, most likely first"},"layers":{"$ref":"#/components/schemas/GeoLayers"},"attribution":{"type":"array","items":{"type":"string"},"description":"Source notes to show next to the values, one per dataset read: provider, licence with its text, the change note and the dataset URI — and the geocoder when it geocoded"},"geocoded":{"type":"boolean","description":"True when this call spent one geocoding of the monthly quota; cache hits, candidateId and lat/lng do not"},"quota":{"$ref":"#/components/schemas/Quota"}},"required":["candidates","attribution","geocoded","quota"]},"GeoPoint":{"type":"object","properties":{"id":{"type":"string","description":"Candidate id; send it as candidateId to pick this point without geocoding again"},"formatted":{"type":"string","description":"The address as the geocoder writes it"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"district":{"type":"string"},"stateCode":{"type":"string","enum":["BW","BY","BE","BB","HB","HH","HE","MV","NI","NW","RP","SL","SN","ST","SH","TH"]},"lat":{"type":"number"},"lng":{"type":"number"},"precision":{"$ref":"#/components/schemas/Precision"},"confidence":{"type":"number","minimum":0,"maximum":1,"description":"The geocoder's confidence, 0–1"}},"required":["id","formatted","lat","lng","precision","confidence"],"description":"The point the layers were read at; absent when several candidates are plausible — ask the user which, then call again with candidateId"},"Precision":{"type":"string","enum":["house","street","district","city"],"description":"How exact a point is: house — the building; street — the street, house number unknown; district — a district or postcode area; city — the town"},"GeoLayers":{"type":"object","properties":{"brw":{"$ref":"#/components/schemas/LandValue"},"designations":{"type":"array","items":{"$ref":"#/components/schemas/Designation"},"description":"Statutory designations at the point — social_conservation (Milieuschutz: converting into condominiums and modernising need consent, right of first refusal), urban_conservation, redevelopment (Sanierungsgebiet: § 7h EStG depreciation, settlement charge), monument (§ 7i EStG depreciation, alterations need consent). Empty means none where the layer has data; absent means no data for that state — say so"},"census":{"allOf":[{"$ref":"#/components/schemas/CensusCell"},{"description":"Zensus 2022 (15 May 2022) for the 100 m cell around the point: rent is the average net cold rent of rented flats then — a benchmark to test the listing's rent against, not the market rent today; absent outside Germany or where the census published nothing"}]},"flood":{"type":"array","items":{"$ref":"#/components/schemas/FloodZone"},"description":"Flood hazard zones at the point from the EU Floods Directive maps (HWRM-RL, all of Germany): every scenario the point lies in, most probable first — high (HQhäufig), medium (HQ100), low (HQextrem). Empty means the hazard maps show no flooding there; absent means the layer could not be read. Building in a HQ100 zone is restricted (§ 78 WHG) and Elementarschaden cover is dearer or refused — say so"},"heavyRain":{"$ref":"#/components/schemas/HeavyRain"},"noise":{"$ref":"#/components/schemas/Noise"},"location":{"allOf":[{"$ref":"#/components/schemas/LocationClass"},{"description":"The Wohnlage the city's rent index (Mietspiegel) gives the nearest address — the row of the index a flat there falls into. Present only in cities whose index is baked (Berlin, Hamburg) and when an address point lies within 40 m"}]},"transit":{"type":"array","items":{"$ref":"#/components/schemas/TransitStop"},"description":"Public transport within 3 km in a straight line, nearest first: for each mode the nearest station and, for trains and the tram, a further station within 500 m when it adds a line the nearer ones lack (at most 3 per mode) — with the lines calling there, from the DELFI timetable for all of Germany. A station served by U-Bahn and S-Bahn appears once per mode. Empty means no station within reach; absent means the layer could not be read"},"parcel":{"$ref":"#/components/schemas/Parcel"}},"description":"Absent when there was no point to read at"},"LandValue":{"type":"object","properties":{"eurPerSqm":{"type":"number","description":"Bodenrichtwert in EUR per m² of plot"},"stichtag":{"type":"string","description":"Valuation date the value refers to"},"landUse":{"type":"string","description":"Nutzung of the zone, e.g. W for Wohnbaufläche"},"gfz":{"type":["number","null"],"description":"Geschossflächenzahl of the zone, when the board publishes it"},"zoneId":{"type":"string","description":"The zone's id in the source data"},"land":{"type":"string","description":"Federal state whose valuation board set the zone (two-letter code)"},"approximate":{"type":"boolean","description":"True when the point was not house-exact: treat the value as an assumption"}},"required":["eurPerSqm","stichtag","gfz","zoneId","land","approximate"],"description":"Bodenrichtwert at the point; absent outside the baked coverage — say \"no data\", never guess"},"HeavyRain":{"type":"object","properties":{"depth":{"type":["integer","null"],"description":"The deepest water the map shows within `within` metres of the point, as the lower bound of its level in centimetres: 10, 30, 50 or 100; null where it shows less than 10 cm there"},"within":{"type":"integer","description":"Metres around the point that were read (10): the model leaves the building out and runs the water around it, so a house point itself reads dry"},"scenario":{"type":"string","enum":["rare"],"description":"rare: the exceptional cloudburst of the map, regionally per KOSTRA-DWD — about once in a hundred years"},"land":{"type":["string","null"],"description":"Federal state of the map read (e.g. NW)"},"src":{"type":["string","null"],"description":"The dataset read; null when nothing was found within reach"}},"required":["depth","within","scenario","land","src"],"description":"Heavy-rain hazard around the point from the BKG's Hinweiskarte Starkregengefahren: the deepest water an exceptional cloudburst leaves within `within` metres — flash flooding from rain, far from any river, which the flood zones do not show. A Hinweis, not an assessment of the building (the BKG says so): a courtyard or a cellar ramp in the ring can read deeper than the street; it matters for basements, underground car parks and ground floors, and for the Elementarschaden cover — say so and point to the municipality's own Starkregengefahrenkarte. Absent where no map is baked (Bayern and Baden-Württemberg are not published yet) — say \"no data\""},"Noise":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/NoiseStatus"},"lden":{"type":["number","null"],"description":"The loudest L_DEN at the point over every measured cause — a building's loudest facade in Berlin, the innermost band elsewhere in dB(A); null unless status is measured"},"lnight":{"type":["number","null"],"description":"The loudest L_Night at the point in dB(A); null unless status is measured"},"sources":{"type":"object","properties":{"road":{"$ref":"#/components/schemas/NoiseLevel"},"rail":{"$ref":"#/components/schemas/NoiseLevel"},"air":{"$ref":"#/components/schemas/NoiseLevel"}},"required":["road","rail","air"],"description":"Per cause, always all three with their own status. rail is mapped for all of Germany (every federal railway, © Eisenbahn-Bundesamt): no band at the point means below 55 dB(A) L_DEN / 45 L_Night from trains, below_threshold. road and air are mapped where a building of a facade map answers (Berlin); elsewhere no_coverage. At a building, a cause without a level is below_threshold: the map computed that facade and found nothing from it. Band levels are lower bounds of 5 dB classes"},"kind":{"type":"string","enum":["band","building","nearest_building"],"description":"With status measured. building: the point lies in a building of the facade map and the levels are its loudest facade (Berlin). nearest_building: the point lies in no mapped building — a building newer than the map, a pin on the kerb — and the nearest one within 40 m answered; distance says how far, and the levels are the neighbour's, so present them as an assumption. band: isophone bands, so a level is \"at least\""},"distance":{"type":"integer","description":"Metres from the point to the building whose facades answered; only with kind nearest_building"}},"required":["status","lden","lnight","sources"],"description":"Environmental noise at the point from the strategic noise maps (EU END, 2022): railway bands for all of Germany (© Eisenbahn-Bundesamt), building facades with road, rail and air in Berlin. Present whenever the maps could be read; status tells a measured level from \"the maps show nothing here\" (below_threshold) and from \"no map reaches the point\" (no_coverage), per cause in sources. Absent means the layer could not be read. L_DEN ≥ 55 dB(A) or L_Night ≥ 50 is where the WHO and the Lärmaktionsplanung start to act; ≥ 65 / 55 counts as a health risk"},"NoiseStatus":{"type":"string","enum":["measured","below_threshold","no_coverage"],"description":"What a noise reading rests on. measured: a level was read at the point. below_threshold: the map covering the point shows nothing there — quieter than its lowest class, a finding, not a gap. no_coverage: no map reaches the point — unknown, never \"quiet\". For the reading as a whole, no_coverage means nothing was measured and at least one cause is unmapped at the point: sources says which causes are known to be quiet and which are unmapped — tell the user what is unmapped instead of calling the place quiet"},"NoiseLevel":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/NoiseStatus"},"lden":{"type":["number","null"],"description":"L_DEN in dB(A); null unless status is measured"},"lnight":{"type":["number","null"],"description":"L_Night in dB(A); null unless status is measured"}},"required":["status","lden","lnight"],"description":"One cause of noise at the point: what its map knows there and, when measured, the levels"},"TransitStop":{"type":"object","properties":{"mode":{"$ref":"#/components/schemas/TransitMode"},"name":{"type":"string","description":"The station as the timetable names it, e.g. \"U Nauener Platz (Berlin)\""},"lines":{"type":"array","items":{"type":"string"},"description":"The lines of that mode calling there, e.g. [\"S41\", \"S42\"]; empty when the timetable names none"},"distance":{"type":"integer","description":"Metres from the point in a straight line (Luftlinie), not the walk — say so to the user, and that the walk is about a third longer"}},"required":["mode","name","lines","distance"]},"Parcel":{"type":"object","properties":{"id":{"type":"string","description":"Flurstückskennzeichen as the cadastre writes it (state, Gemarkung, Flur, number, filled with _) — what an extract from the Grundbuch, the Liegenschaftskataster or the Baulastenverzeichnis is asked for by"},"number":{"type":["string","null"],"description":"Flurstücksnummer: \"57\", or Zähler/Nenner \"57/3\""},"district":{"type":["string","null"],"description":"Gemarkung"},"section":{"type":["string","null"],"description":"Flur; Hamburg and most of Sachsen have none"},"areaSqm":{"type":["number","null"],"description":"Amtliche Fläche of this Flurstück in m², as the cadastre books it"},"land":{"type":"string","description":"Federal state whose cadastre it comes from (two-letter code)"},"cadastreLink":{"type":["object","null"],"properties":{"portal":{"type":"string","description":"The portal's name, e.g. \"TIM-online\""},"url":{"type":"string"}},"required":["portal","url"],"description":"The parcel on its state's own cadastre map, to hand the user: the portals of Nordrhein-Westfalen, Sachsen and Baden-Württemberg open it by its Kennzeichen and outline it; the others centre their ALKIS map on the point, most with a marker. null where no portal link is known"}},"required":["id","number","district","section","areaSqm","land","cadastreLink"],"description":"The cadastral parcel (Flurstück) the point lies in, from the states' open ALKIS data. Only for a house-exact point. A Grundstück in the Grundbuch can span several Flurstücke, so areaSqm is this parcel's own area: pass it as plotArea as an assumption, and say that the Grundbuch or the Kaufvertrag has the plot. For a flat the plot share still needs the Miteigentumsanteil (coOwnershipShare / coOwnershipTotal). Absent in Bayern, which publishes no parcels, or when the point was not house-exact"},"Quota":{"type":"object","properties":{"used":{"type":"integer"},"limit":{"type":"integer","description":"-1 means unlimited"},"resetsAt":{"type":"string","format":"date-time"}},"required":["used","limit"],"description":"Geocodings used and allowed this month"},"GeoLookupRequest":{"type":"object","properties":{"query":{"type":"string","minLength":3,"maxLength":200,"description":"Free text as the listing shows it, e.g. \"Malplaquetstraße 27, 13347 Berlin\""},"street":{"type":"string","minLength":1,"maxLength":120},"houseNumber":{"type":"string","minLength":1,"maxLength":20},"postalCode":{"type":"string","pattern":"^\\d{5}$"},"city":{"type":"string","minLength":1,"maxLength":80},"candidateId":{"type":"string","minLength":1,"maxLength":200,"description":"One of a previous answer's candidates; nothing is geocoded"},"lat":{"type":"number","minimum":47,"maximum":56,"description":"Skip geocoding when the point is already known, e.g. from the listing page"},"lng":{"type":"number","minimum":5,"maximum":16},"precision":{"allOf":[{"$ref":"#/components/schemas/Precision"},{"description":"How exact a given lat/lng is; defaults to house"}]},"yearBuilt":{"type":"integer","minimum":1000,"maximum":2100,"description":"Year the building was ready, to read the Vergleichsmiete cell of the rent index where the city has one baked (Berlin, Hamburg)"},"livingArea":{"type":"number","exclusiveMinimum":0,"description":"Living space in m², with yearBuilt"}},"description":"One of: lat and lng; candidateId; query; the structured address fields. Add yearBuilt and livingArea to get the rent index cell with the Wohnlage"},"LoginOptions":{"type":"object","properties":{"turnstileSiteKey":{"type":"string","description":"Present when the bot check is on: render the Turnstile widget with this key and action \"login\", and send its token as turnstileToken"}}},"LoginRequested":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"expiresInMinutes":{"type":"integer"}},"required":["ok","expiresInMinutes"]},"LoginRequest":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email"},"turnstileToken":{"type":"string","maxLength":4096,"description":"Required where Turnstile is enabled (the public site)"},"returnTo":{"type":"string","maxLength":500,"pattern":"^\\/(?![/\\\\])\\S*$"}},"required":["email"]},"Account":{"type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string","format":"email"},"plan":{"type":"string","enum":["free","pro"]},"quotas":{"type":"object","properties":{"objects":{"$ref":"#/components/schemas/Quota"},"geoLookups":{"$ref":"#/components/schemas/Quota"},"storage":{"$ref":"#/components/schemas/Quota"}},"required":["objects","geoLookups","storage"],"description":"Allowances in the current period, so an agent can say \"3 geocodings left\" before spending them; geoLookups counts geocodings only, and its limit is 0 where this server has no geocoder (lookups by lat and lng still work); storage is bytes of attachments"},"client":{"$ref":"#/components/schemas/Actor"},"scopes":{"type":"array","items":{"type":"string","enum":["objects:read","objects:write","geo:read"]},"description":"What this credential may do; the web session may do everything"},"planRequest":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"}},"required":["createdAt"],"description":"The request for a larger plan still waiting for an answer, if any; the settings page shows it instead of the form"}},"required":["userId","email","plan","quotas","client","scopes"]},"Actor":{"type":"object","properties":{"kind":{"type":"string","enum":["user","agent"],"description":"user — the person in the web app; agent — something acting with a token"},"client":{"type":"string","description":"The connected app's name or the token's label — what notes are signed with"},"model":{"type":"string","description":"Self-reported by the agent, shown as \"laut Agent\""}},"required":["kind"],"description":"How this credential presents itself"},"SessionRequest":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email"},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["email","code"]},"PlanRequest":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"}},"required":["createdAt"]},"PlanRequestInput":{"type":"object","properties":{"message":{"type":"string","maxLength":500,"description":"What the room is needed for, as the user put it; optional"}}},"TokenList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Token"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"Token":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["pat","oauth"]},"label":{"type":"string","description":"The token's label or the connected app's name"},"prefix":{"type":"string","description":"The first characters of a personal token, to tell them apart"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time"}},"required":["id","kind","label","scopes","createdAt"]},"TokenCreated":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/Token"},"secret":{"type":"string","description":"Shown once; only its hash is stored"}},"required":["token","secret"]},"TokenInput":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":60,"description":"Shown as the author of what the token writes"},"scopes":{"type":"array","items":{"type":"string","enum":["objects:read","objects:write","geo:read"]},"minItems":1,"default":["objects:read","objects:write","geo:read"]}},"required":["label"]},"ActivityList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ActivityEntry"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"ActivityEntry":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"surface":{"type":"string","enum":["rest","mcp"]},"client":{"type":"string","description":"The token's label or the connected app's name; absent for the web session"},"via":{"type":"string","enum":["session","pat","oauth"]},"op":{"type":"string","description":"The operationId that was called"},"tool":{"type":"string","description":"The MCP tool name, when the call came through /mcp"},"ok":{"type":"boolean"},"status":{"type":"integer"},"problemCode":{"type":"string"},"ms":{"type":"integer"}},"required":["at","surface","op","ok","status","ms"]},"Export":{"type":"object","properties":{"exportedAt":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string"},"plan":{"type":"string","enum":["free","pro"]},"createdAt":{"type":"string"}},"required":["userId","email","plan","createdAt"]},"objects":{"type":"array","items":{"$ref":"#/components/schemas/Object"},"description":"Every object, discarded ones included, with its notes, links and attachments (the files themselves by their url)"},"credentials":{"type":"array","items":{"$ref":"#/components/schemas/Token"},"description":"Tokens and connected apps, without secrets"},"activity":{"type":"array","items":{"$ref":"#/components/schemas/ActivityEntry"},"description":"The call log still held, newest first"},"planRequests":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"},"plan":{"type":"string","enum":["free","pro"]},"message":{"type":"string"}},"required":["createdAt","plan"]},"description":"Requests for a larger plan, newest first, with what was written"}},"required":["exportedAt","account","objects","credentials","activity","planRequests"]},"Object":{"type":"object","properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/ObjectStatus"},"source":{"$ref":"#/components/schemas/ObjectSource"},"externalId":{"type":"string"},"listingUrl":{"type":"string"},"title":{"type":"string"},"address":{"$ref":"#/components/schemas/Address"},"kpis":{"$ref":"#/components/schemas/Kpis"},"verdict":{"type":"string","enum":["pending","carries","taxFlips","taxSaves","subsidised","doesNotCarry"],"description":"The one-word verdict key; the sentence comes from calculateProperty"},"assumedCount":{"type":"integer","description":"How many input fields are assumptions"},"lists":{"type":"array","items":{"type":"string"},"description":"List names"},"noteCount":{"type":"integer"},"coverUrl":{"type":"string","format":"uri","description":"The cover photo's bytes, when one is set; needs the caller's credential"},"url":{"type":"string","format":"uri","description":"The object in the web app"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"discardedAt":{"type":"string","format":"date-time","description":"When it went to the trash; absent while active"},"input":{"$ref":"#/components/schemas/SavedPropertyInput"},"assumed":{"type":"array","items":{"type":"string"}},"notes":{"type":"array","items":{"$ref":"#/components/schemas/Note"},"description":"Oldest first"},"links":{"type":"array","items":{"$ref":"#/components/schemas/Link"},"description":"Further pages of the object, oldest first; listingUrl is not repeated here"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"Files kept with the object, oldest first"},"provenance":{"$ref":"#/components/schemas/Provenance"},"summary":{"type":"array","items":{"$ref":"#/components/schemas/SummaryLine"},"description":"The calculation in a few lines of facts, in the requested language: object (with its address when known), side costs, financing, rent, yields, first-year cashflow, depreciation and tax effect, dates; the last line names the assumptions, and a value that is one carries a *. Nothing in it judges — the verdict does that. Quote it rather than recomputing."},"geo":{"allOf":[{"$ref":"#/components/schemas/GeoLayers"},{"description":"The data layers at the object's point, read live; absent when the address has no point"}]}},"required":["id","status","source","title","address","kpis","verdict","assumedCount","lists","noteCount","url","createdAt","updatedAt","input","assumed","notes","links","attachments","provenance","summary"]},"SavedPropertyInput":{"type":"object","properties":{"purchasePrice":{"type":"number","minimum":0,"description":"Kaufpreis, EUR"},"livingArea":{"type":"number","minimum":1,"description":"Wohnfläche, m²"},"yearBuilt":{"type":"number","description":"Baujahr; decides the AfA rate"},"stateCode":{"type":"string","enum":["BW","BY","BE","BB","HB","HH","HE","MV","NI","NW","RP","SL","SN","ST","SH","TH"],"description":"Bundesland, two-letter code (BE, NW, BY …); sets the Grunderwerbsteuer"},"address":{"type":"string","description":"Free-text address, a label only"},"notaryPercent":{"type":"number","minimum":0,"description":"Notar, % of the purchase price"},"landRegistryPercent":{"type":"number","minimum":0,"description":"Grundbuch, % of the purchase price"},"brokerPercent":{"type":"number","minimum":0,"description":"Käuferprovision, % of the purchase price; counts only with hasBroker"},"hasBroker":{"type":"boolean","description":"Whether a Provision is due; \"provisionsfrei\" is false"},"movablesInPrice":{"type":"number","minimum":0,"description":"Inventar (Einbauküche, furniture) inside the purchase price, EUR; outside the Grunderwerbsteuer base"},"movablesExtra":{"type":"number","minimum":0,"description":"Inventar bought on top of the purchase price (Ablöse), EUR"},"movablesUsefulLife":{"type":"number","minimum":1,"description":"Useful life of the Inventar, years: 10 for a kitchen, 13 for furniture"},"renovationCost":{"type":"number","minimum":0,"description":"Renovierung right after the purchase, EUR incl. VAT; the 15 % rule decides AfA or deduction in year one"},"coldRent":{"type":"number","minimum":0,"description":"Kaltmiete per month, EUR"},"parkingRent":{"type":"number","minimum":0,"description":"Stellplatz or garage rent, EUR per month, apart from coldRent"},"serviceCharge":{"type":"number","minimum":0,"description":"Hausgeld, the whole monthly charge, EUR"},"serviceChargeRecoverable":{"type":"number","minimum":0,"description":"Umlagefähiger Teil of the Hausgeld the tenant reimburses, per month, EUR"},"otherCosts":{"type":"number","minimum":0,"description":"Running costs outside the Hausgeld (Sondereigentumsverwaltung, insurance, upkeep), EUR per month"},"serviceChargeReserve":{"type":"number","minimum":0,"description":"Erhaltungsrücklage part of the Hausgeld, EUR per month; not deducted from tax until the WEG spends it"},"equity":{"type":"number","minimum":0,"description":"Eigenkapital, EUR; when given, ltv is ignored"},"ltv":{"type":"number","minimum":0,"description":"Beleihungsauslauf: loan on the object in % of the purchase price, as a bank quotes it; side costs come from equity. Used when equity is left out"},"interestRate":{"type":"number","minimum":0,"description":"Sollzins, % per year"},"repaymentRate":{"type":"number","minimum":0,"description":"Anfängliche Tilgung, % of the loan per year"},"fixedRateYears":{"type":"number","minimum":1,"maximum":40,"description":"Sollzinsbindung of the bank loan, years (1–40); afterwards the loan continues at interestRate plus followUpRateDelta"},"followUpRateDelta":{"type":"number","description":"Rate change after the Sollzinsbindung, percentage points; 0 continues the Sollzins. A scenario, never a listing figure"},"specialRepayment":{"type":"number","minimum":0,"description":"Sondertilgung on the bank loan, EUR per year, during the Sollzinsbindung; a plan, never a listing figure"},"kfwAmount":{"type":"number","minimum":0,"description":"KfW-Darlehen, EUR: a tranche of the loan, never on top of it; 0 or left out means none. Send only what the user names"},"kfwInterestRate":{"type":"number","minimum":0,"description":"Sollzins of the KfW tranche, % per year, for its ten-year Zinsbindung"},"kfwTermYears":{"type":"number","minimum":1,"description":"Laufzeit of the KfW tranche, years (4–35)"},"kfwGraceYears":{"type":"number","minimum":0,"description":"Tilgungsfreie Anlaufjahre of the KfW tranche: 1–2 on a term to 10 years, up to 3 to 25, up to 5 to 35"},"kfwRepaymentMode":{"type":"string","enum":["annuity","bullet"],"description":"annuity (default) or bullet, an endfälliges Darlehen of up to 10 years"},"handover":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Übergabe/Fertigstellung month of a new build bought before completion, YYYY-MM (a quarter as its last month); the months from the contract to it are the Bauphase. Left out, the property is ready"},"contractMonth":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Month of the Kaufvertrag, YYYY-MM, once it is signed; left out, this month. Anchors the Bauphase and the building age"},"commitmentFreeMonths":{"type":"number","minimum":0,"maximum":36,"description":"Bereitstellungszinsfreie Zeit of the bank loan, months (0–36); counts only with a Bauphase"},"commitmentFeeRate":{"type":"number","minimum":0,"maximum":1,"description":"Bereitstellungszins of the bank loan, % per month on the undrawn part once the bereitstellungszinsfreie Zeit is over (0–1; 3 % p. a. is 0.25); counts only with a Bauphase"},"landShare":{"type":"number","minimum":0,"maximum":100,"description":"Bodenwertanteil, % of the price that is land (not depreciable). Send it when the Kaufvertrag or Bauträger states the split; left out, derived from landValuePerSqm and the plot share, else 20 assumed"},"landValuePerSqm":{"type":"number","minimum":0,"description":"Bodenrichtwert, EUR per m² of plot, as lookup_address answers it"},"plotArea":{"type":"number","minimum":0,"description":"Grundstücksfläche: the whole plot, m²"},"coOwnershipShare":{"type":"number","minimum":0,"description":"Miteigentumsanteil numerator: 56 of \"56/10.000\"; 1 for a house bought whole"},"coOwnershipTotal":{"type":"number","minimum":0,"description":"Miteigentumsanteil denominator: 10000 of \"56/10.000\"; 1 for a house bought whole"},"floorAreaRatio":{"type":"number","minimum":0,"description":"GFZ of the Bodenrichtwert zone from lookup_address; estimates the plot share when plotArea and Miteigentumsanteil are unknown"},"taxRate":{"type":"number","minimum":0,"maximum":100,"description":"Grenzsteuersatz incl. Soli, %"},"depreciationVariant":{"type":"string","enum":["linear2","linear25","linear3","declining5","declining7b","monument","remainingLife"],"description":"AfA route: linear2, linear25, linear3, declining5, declining7b, monument, remainingLife; left out, the Baujahr decides"},"renovationShare":{"type":"number","minimum":0,"maximum":100,"description":"Sanierungsanteil under § 7i, % of the building value; monument route only"},"remainingUsefulLife":{"type":"number","minimum":1,"description":"Restnutzungsdauer from an appraisal, years; remainingLife route only"},"rentGrowth":{"type":"number","description":"Mietsteigerung, % per year"},"costGrowth":{"type":"number","description":"Kostensteigerung, % per year"},"valueGrowth":{"type":"number","description":"Wertsteigerung, % per year"},"vacancyMonths":{"type":"number","minimum":0,"maximum":12,"description":"Mietausfallwagnis, vacancy months per year"}},"required":["purchasePrice","livingArea","stateCode","coldRent"],"description":"The calculator input as saved: only what the caller sent. Fields left out take the defaults of PropertyInput and are listed in assumed"},"Note":{"type":"object","properties":{"id":{"type":"string"},"objectId":{"type":"string"},"kind":{"$ref":"#/components/schemas/NoteKind"},"title":{"type":"string"},"body":{"type":"string"},"author":{"$ref":"#/components/schemas/Actor"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"When the note was last replaced by hand; absent while it reads as written"}},"required":["id","objectId","kind","body","author","createdAt"]},"NoteKind":{"type":"string","enum":["analysis","viewing","question","other"],"description":"analysis — an agent's or the user's assessment; viewing — Besichtigung; question — for the Makler; other"},"Link":{"type":"object","properties":{"id":{"type":"string"},"objectId":{"type":"string"},"url":{"type":"string","format":"uri"},"label":{"type":"string"},"author":{"$ref":"#/components/schemas/Actor"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","objectId","url","label","author","createdAt"],"description":"A page that belongs to the object, besides listingUrl"},"Attachment":{"type":"object","properties":{"id":{"type":"string"},"objectId":{"type":"string"},"kind":{"$ref":"#/components/schemas/AttachmentKind"},"name":{"type":"string","description":"The file name as uploaded"},"contentType":{"type":"string"},"size":{"type":"integer","description":"Bytes"},"cover":{"type":"boolean","description":"The photo that stands for the object in lists and exports; at most one per object"},"url":{"type":"string","format":"uri","description":"Where the bytes are served; needs the caller's credential, never public"},"sourceUrl":{"type":"string","format":"uri","description":"Where the bytes were fetched from, when they came by URL"},"author":{"$ref":"#/components/schemas/Actor"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","objectId","kind","name","contentType","size","cover","url","author","createdAt"],"description":"A file kept with the object: a photo, the floor plan, a document. Uploaded in the web app or fetched by URL"},"AttachmentKind":{"type":"string","enum":["photo","floorplan","document"],"description":"photo — a picture of the property; floorplan — the Grundriss; document — the exposé, Baubeschreibung, Teilungserklärung, Energieausweis …"},"Provenance":{"type":"object","properties":{"createdBy":{"$ref":"#/components/schemas/Actor"},"updatedBy":{"$ref":"#/components/schemas/Actor"}},"required":["createdBy","updatedBy"]},"DeleteAccountRequest":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"The account's own e-mail address, typed again: the confirmation"}},"required":["email"]},"ObjectList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ObjectRow"}},"truncated":{"type":"boolean"},"nextCursor":{"type":"string","description":"With truncated: pass as cursor for the rows after these"}},"required":["items","truncated"]},"ObjectSaved":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","updated"]},"object":{"$ref":"#/components/schemas/Object"},"possibleDuplicates":{"type":"array","items":{"$ref":"#/components/schemas/ObjectRow"},"description":"Saved objects at the same address with a living area within 5 m². Nothing was merged — ask the user"}},"required":["outcome","object","possibleDuplicates"]},"ObjectInput":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120,"description":"Defaults to the street line of the address"},"source":{"$ref":"#/components/schemas/ObjectSource"},"externalId":{"type":"string","minLength":1,"maxLength":64,"description":"The portal's listing id (the number in /expose/123456789); with source it makes the save idempotent"},"listingUrl":{"type":"string","maxLength":500,"format":"uri","description":"The listing's page"},"address":{"$ref":"#/components/schemas/Address"},"input":{"allOf":[{"$ref":"#/components/schemas/PropertyInput"},{"description":"The calculator input. Money in EUR, areas in m², rates in percent, rent per month (cold). Every field left out takes its default and is reported back in `assumed`."}]},"assumed":{"type":"array","items":{"type":"string","enum":["purchasePrice","livingArea","yearBuilt","stateCode","address","notaryPercent","landRegistryPercent","brokerPercent","hasBroker","movablesInPrice","movablesExtra","movablesUsefulLife","renovationCost","coldRent","parkingRent","serviceCharge","serviceChargeRecoverable","otherCosts","serviceChargeReserve","equity","ltv","interestRate","repaymentRate","fixedRateYears","followUpRateDelta","specialRepayment","kfwAmount","kfwInterestRate","kfwTermYears","kfwGraceYears","kfwRepaymentMode","handover","contractMonth","commitmentFreeMonths","commitmentFeeRate","landShare","landValuePerSqm","plotArea","coOwnershipShare","coOwnershipTotal","floorAreaRatio","taxRate","depreciationVariant","renovationShare","remainingUsefulLife","rentGrowth","costGrowth","valueGrowth","vacancyMonths"]},"maxItems":40,"default":[],"description":"Fields of input you filled in without a source (a typical Hausgeld, a guessed rent); fields left out count as assumed on their own"},"status":{"allOf":[{"$ref":"#/components/schemas/ObjectStatus"},{"description":"Defaults to active"}]},"lists":{"type":"array","items":{"type":"string","minLength":1,"maxLength":60},"maxItems":20,"default":[],"description":"List names to put the object in; created when missing"},"note":{"$ref":"#/components/schemas/NoteInput"}},"required":["address","input"]},"NoteInput":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/NoteKind"},"title":{"type":"string","minLength":1,"maxLength":120},"body":{"type":"string","minLength":1,"maxLength":20000,"description":"Markdown, up to 20 000 characters, in the language the user writes in"},"model":{"type":"string","minLength":1,"maxLength":80,"description":"Which model wrote it, if an agent did — shown as \"laut Agent\""},"idempotencyKey":{"type":"string","minLength":1,"maxLength":64,"description":"A retry with the same key answers the note already written"}},"required":["body"],"description":"A first note, written in the same request"},"CloneObjectRequest":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120,"description":"What the copy is called; say what differs — \"Malplaquetstraße 27 · 30 % Eigenkapital\""},"input":{"$ref":"#/components/schemas/PropertyInputOverride"},"assumed":{"type":"array","items":{"type":"string","enum":["purchasePrice","livingArea","yearBuilt","stateCode","address","notaryPercent","landRegistryPercent","brokerPercent","hasBroker","movablesInPrice","movablesExtra","movablesUsefulLife","renovationCost","coldRent","parkingRent","serviceCharge","serviceChargeRecoverable","otherCosts","serviceChargeReserve","equity","ltv","interestRate","repaymentRate","fixedRateYears","followUpRateDelta","specialRepayment","kfwAmount","kfwInterestRate","kfwTermYears","kfwGraceYears","kfwRepaymentMode","handover","contractMonth","commitmentFreeMonths","commitmentFeeRate","landShare","landValuePerSqm","plotArea","coOwnershipShare","coOwnershipTotal","floorAreaRatio","taxRate","depreciationVariant","renovationShare","remainingUsefulLife","rentGrowth","costGrowth","valueGrowth","vacancyMonths"]},"maxItems":40,"default":[],"description":"Field names of `input` the caller filled in without a source. A field the original had as a guess and the copy replaces is a guess no longer unless named here again"}},"required":["title"]},"PropertyInputOverride":{"type":"object","properties":{"purchasePrice":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"livingArea":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"yearBuilt":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"stateCode":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"address":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"notaryPercent":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"landRegistryPercent":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"brokerPercent":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"hasBroker":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"movablesInPrice":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"movablesExtra":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"movablesUsefulLife":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"renovationCost":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"coldRent":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"parkingRent":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"serviceCharge":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"serviceChargeRecoverable":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"otherCosts":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"serviceChargeReserve":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"equity":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"ltv":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"interestRate":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"repaymentRate":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"fixedRateYears":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"followUpRateDelta":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"specialRepayment":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"kfwAmount":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"kfwInterestRate":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"kfwTermYears":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"kfwGraceYears":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"kfwRepaymentMode":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"handover":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"contractMonth":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"commitmentFreeMonths":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"commitmentFeeRate":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"landShare":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"landValuePerSqm":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"plotArea":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"coOwnershipShare":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"coOwnershipTotal":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"floorAreaRatio":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"taxRate":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"depreciationVariant":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"renovationShare":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"remainingUsefulLife":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"rentGrowth":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"costGrowth":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"valueGrowth":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]},"vacancyMonths":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"boolean"},{"type":"null"}]}},"description":"Only the fields that change, named and valued as in the calculator input (PropertyInput, calculate_property's arguments); left out, the copy carries the original's figures. null puts a field back to its default and lists it in assumed — not purchasePrice, livingArea, coldRent or stateCode"},"SetObjectStatusRequest":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ObjectStatus"}},"required":["status"]},"SetObjectTitleRequest":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":120,"description":"How the object is called on its page and in lists; the address stays as it is"}},"required":["title"]},"ListMembership":{"type":"object","properties":{"objectId":{"type":"string"},"lists":{"type":"array","items":{"$ref":"#/components/schemas/List"}}},"required":["objectId","lists"]},"List":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"objectCount":{"type":"integer"}},"required":["id","name","objectCount"]},"SetObjectListsRequest":{"type":"object","properties":{"lists":{"type":"array","items":{"type":"string","minLength":1,"maxLength":60},"maxItems":20,"description":"The whole membership; names that do not exist yet are created"}},"required":["lists"]},"KnownIds":{"type":"object","properties":{"known":{"type":"array","items":{"type":"object","properties":{"externalId":{"type":"string"},"objectId":{"type":"string"},"status":{"$ref":"#/components/schemas/ObjectStatus"},"purchasePrice":{"type":"number","description":"The last saved price, so a changed price shows on the results page"}},"required":["externalId","objectId","status","purchasePrice"]}},"unknown":{"type":"array","items":{"type":"string"}}},"required":["known","unknown"]},"KnownIdsRequest":{"type":"object","properties":{"source":{"$ref":"#/components/schemas/ObjectSource"},"externalIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":200}},"required":["source","externalIds"]},"NoteList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Note"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"LinkList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Link"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"LinkInput":{"type":"object","properties":{"url":{"type":"string","maxLength":500,"format":"uri","description":"The page: the same flat on another portal, the developer's project page, a Bebauungsplan, a Mietspiegel entry"},"label":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it; defaults to the host name, e.g. immowelt.de"}},"required":["url"]},"AttachmentList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"AttachmentUpload":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/AttachmentKind"},"file":{"type":"string","format":"binary","description":"JPEG, PNG, WebP, PDF, DOCX or XLSX, up to 20 MB"}},"required":["file"]},"AttachmentsFromUrlsResult":{"type":"object","properties":{"attachments":{"type":"array","items":{"$ref":"#/components/schemas/Attachment"},"description":"What was stored, in the order of the items that succeeded; a URL the object already had answers the existing attachment"},"failed":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"message":{"type":"string","description":"What went wrong, for the caller to tell the user"}},"required":["url","message"]},"description":"In the order sent"}},"required":["attachments","failed"]},"AttachmentsFromUrlsRequest":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2000,"format":"uri","description":"http(s); the file itself, not the page it is on"},"kind":{"allOf":[{"$ref":"#/components/schemas/AttachmentKind"},{"description":"Left out: an image is a photo, anything else a document — say floorplan for the Grundriss"}]},"name":{"type":"string","minLength":1,"maxLength":120,"description":"The file name to keep; defaults to what the server or the URL says"}},"required":["url"]},"minItems":1,"maxItems":20}},"required":["items"]},"SetObjectCoverRequest":{"type":"object","properties":{"attachmentId":{"type":"string","description":"A photo of this object"}},"required":["attachmentId"]},"ListList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/List"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]}},"parameters":{}},"paths":{"/health":{"get":{"operationId":"health","tags":["account"],"summary":"Is the Worker up.","security":[],"x-mcp":false,"responses":{"200":{"description":"The Worker is up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/calculations":{"post":{"operationId":"calculateProperty","tags":["calc"],"summary":"Run the Immoprobe calculation for one property.","description":"Deterministic maths over PropertyInput: purchase side costs incl. Grunderwerbsteuer by StateCode, financing, cashflow before and after tax, AfA, yields, Kaufpreisfaktor, and a year-by-year projection. Financing takes either `equity` (EUR) or `ltv` (loan in percent of the purchase price, 80 by default); the Sollzins holds for `fixedRateYears` (10 by default) and the projection continues past them at the Anschlusszins, the Sollzins moved by `followUpRateDelta` percentage points; a KfW loan is a tranche of that loan (`kfwAmount`, with its own rate, term and tilgungsfreie Anlaufjahre), never money on top of it. A new build bought before completion takes `handover` (YYYY-MM), counted from `contractMonth` (YYYY-MM, this month unless given); its Bauphase — interest while the Bauträger bills, commitment fees — is the first, negative cashflow and starts the projection below zero. Fields left out take schema defaults and are echoed back in `assumed`. Free and anonymous; rate limited per IP.","security":[],"x-mcp":{"name":"calculate_property","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"parameters":[{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PropertyInput"},{"description":"The calculator input. Money in EUR, areas in m², rates in percent, rent per month (cold). Every field left out takes its default and is reported back in `assumed`."}]}}}},"responses":{"200":{"description":"The calculation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Calculation"}}}},"400":{"description":"Input failed validation; `details.fields` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"getCalculation","tags":["calc"],"summary":"The calculation behind a calculator link, as text — for an assistant that can only open URLs.","description":"The same maths as POST, driven by the query string of a calculator link (`/calculator?purchasePrice=…&livingArea=…&stateCode=…&coldRent=…`): an assistant without tools builds the link for the user and opens this address with the same query to quote the numbers instead of computing them itself. Answers text (markdown: the summary lines, the verdict, the warnings, the assumptions, the projection milestones and the link to the web calculator) unless `Accept: application/json` asks for the JSON of POST. Required: purchasePrice, livingArea, stateCode, coldRent; every other field takes its default and is named in the assumptions. Free and anonymous; rate limited per IP.","security":[],"x-mcp":false,"parameters":[{"schema":{"type":"string","description":"Kaufpreis, EUR"},"required":false,"description":"Kaufpreis, EUR","name":"purchasePrice","in":"query"},{"schema":{"type":"string","description":"Wohnfläche, m²"},"required":false,"description":"Wohnfläche, m²","name":"livingArea","in":"query"},{"schema":{"type":"string","description":"Baujahr; decides the AfA rate"},"required":false,"description":"Baujahr; decides the AfA rate","name":"yearBuilt","in":"query"},{"schema":{"type":"string","description":"Notar, % of the purchase price"},"required":false,"description":"Notar, % of the purchase price","name":"notaryPercent","in":"query"},{"schema":{"type":"string","description":"Grundbuch, % of the purchase price"},"required":false,"description":"Grundbuch, % of the purchase price","name":"landRegistryPercent","in":"query"},{"schema":{"type":"string","description":"Käuferprovision, % of the purchase price; counts only with hasBroker"},"required":false,"description":"Käuferprovision, % of the purchase price; counts only with hasBroker","name":"brokerPercent","in":"query"},{"schema":{"type":"string","description":"Inventar (Einbauküche, furniture) inside the purchase price, EUR; outside the Grunderwerbsteuer base"},"required":false,"description":"Inventar (Einbauküche, furniture) inside the purchase price, EUR; outside the Grunderwerbsteuer base","name":"movablesInPrice","in":"query"},{"schema":{"type":"string","description":"Inventar bought on top of the purchase price (Ablöse), EUR"},"required":false,"description":"Inventar bought on top of the purchase price (Ablöse), EUR","name":"movablesExtra","in":"query"},{"schema":{"type":"string","description":"Useful life of the Inventar, years: 10 for a kitchen, 13 for furniture"},"required":false,"description":"Useful life of the Inventar, years: 10 for a kitchen, 13 for furniture","name":"movablesUsefulLife","in":"query"},{"schema":{"type":"string","description":"Renovierung right after the purchase, EUR incl. VAT; the 15 % rule decides AfA or deduction in year one"},"required":false,"description":"Renovierung right after the purchase, EUR incl. VAT; the 15 % rule decides AfA or deduction in year one","name":"renovationCost","in":"query"},{"schema":{"type":"string","description":"Kaltmiete per month, EUR"},"required":false,"description":"Kaltmiete per month, EUR","name":"coldRent","in":"query"},{"schema":{"type":"string","description":"Stellplatz or garage rent, EUR per month, apart from coldRent"},"required":false,"description":"Stellplatz or garage rent, EUR per month, apart from coldRent","name":"parkingRent","in":"query"},{"schema":{"type":"string","description":"Running costs outside the Hausgeld (Sondereigentumsverwaltung, insurance, upkeep), EUR per month"},"required":false,"description":"Running costs outside the Hausgeld (Sondereigentumsverwaltung, insurance, upkeep), EUR per month","name":"otherCosts","in":"query"},{"schema":{"type":"string","description":"Hausgeld, the whole monthly charge, EUR"},"required":false,"description":"Hausgeld, the whole monthly charge, EUR","name":"serviceCharge","in":"query"},{"schema":{"type":"string","description":"Umlagefähiger Teil of the Hausgeld the tenant reimburses, per month, EUR"},"required":false,"description":"Umlagefähiger Teil of the Hausgeld the tenant reimburses, per month, EUR","name":"serviceChargeRecoverable","in":"query"},{"schema":{"type":"string","description":"Erhaltungsrücklage part of the Hausgeld, EUR per month; not deducted from tax until the WEG spends it"},"required":false,"description":"Erhaltungsrücklage part of the Hausgeld, EUR per month; not deducted from tax until the WEG spends it","name":"serviceChargeReserve","in":"query"},{"schema":{"type":"string","description":"Mietausfallwagnis, vacancy months per year"},"required":false,"description":"Mietausfallwagnis, vacancy months per year","name":"vacancyMonths","in":"query"},{"schema":{"type":"string","description":"Mietsteigerung, % per year"},"required":false,"description":"Mietsteigerung, % per year","name":"rentGrowth","in":"query"},{"schema":{"type":"string","description":"Kostensteigerung, % per year"},"required":false,"description":"Kostensteigerung, % per year","name":"costGrowth","in":"query"},{"schema":{"type":"string","description":"Wertsteigerung, % per year"},"required":false,"description":"Wertsteigerung, % per year","name":"valueGrowth","in":"query"},{"schema":{"type":"string","description":"Eigenkapital, EUR; when given, ltv is ignored"},"required":false,"description":"Eigenkapital, EUR; when given, ltv is ignored","name":"equity","in":"query"},{"schema":{"type":"string","description":"Darlehen, EUR: the form's third financing input, turned into the equity that leaves exactly this loan"},"required":false,"description":"Darlehen, EUR: the form's third financing input, turned into the equity that leaves exactly this loan","name":"loan","in":"query"},{"schema":{"type":"string","description":"Beleihungsauslauf: loan on the object in % of the purchase price, as a bank quotes it; side costs come from equity. Used when equity is left out"},"required":false,"description":"Beleihungsauslauf: loan on the object in % of the purchase price, as a bank quotes it; side costs come from equity. Used when equity is left out","name":"ltv","in":"query"},{"schema":{"type":"string","description":"Sollzins, % per year"},"required":false,"description":"Sollzins, % per year","name":"interestRate","in":"query"},{"schema":{"type":"string","description":"Anfängliche Tilgung, % of the loan per year"},"required":false,"description":"Anfängliche Tilgung, % of the loan per year","name":"repaymentRate","in":"query"},{"schema":{"type":"string","description":"Sollzinsbindung of the bank loan, years (1–40); afterwards the loan continues at interestRate plus followUpRateDelta"},"required":false,"description":"Sollzinsbindung of the bank loan, years (1–40); afterwards the loan continues at interestRate plus followUpRateDelta","name":"fixedRateYears","in":"query"},{"schema":{"type":"string","description":"Rate change after the Sollzinsbindung, percentage points; 0 continues the Sollzins. A scenario, never a listing figure"},"required":false,"description":"Rate change after the Sollzinsbindung, percentage points; 0 continues the Sollzins. A scenario, never a listing figure","name":"followUpRateDelta","in":"query"},{"schema":{"type":"string","description":"Sondertilgung on the bank loan, EUR per year, during the Sollzinsbindung; a plan, never a listing figure"},"required":false,"description":"Sondertilgung on the bank loan, EUR per year, during the Sollzinsbindung; a plan, never a listing figure","name":"specialRepayment","in":"query"},{"schema":{"type":"string","description":"KfW-Darlehen, EUR: a tranche of the loan, never on top of it; 0 or left out means none. Send only what the user names"},"required":false,"description":"KfW-Darlehen, EUR: a tranche of the loan, never on top of it; 0 or left out means none. Send only what the user names","name":"kfwAmount","in":"query"},{"schema":{"type":"string","description":"Sollzins of the KfW tranche, % per year, for its ten-year Zinsbindung"},"required":false,"description":"Sollzins of the KfW tranche, % per year, for its ten-year Zinsbindung","name":"kfwInterestRate","in":"query"},{"schema":{"type":"string","description":"Laufzeit of the KfW tranche, years (4–35)"},"required":false,"description":"Laufzeit of the KfW tranche, years (4–35)","name":"kfwTermYears","in":"query"},{"schema":{"type":"string","description":"Tilgungsfreie Anlaufjahre of the KfW tranche: 1–2 on a term to 10 years, up to 3 to 25, up to 5 to 35"},"required":false,"description":"Tilgungsfreie Anlaufjahre of the KfW tranche: 1–2 on a term to 10 years, up to 3 to 25, up to 5 to 35","name":"kfwGraceYears","in":"query"},{"schema":{"type":"string","description":"Bereitstellungszinsfreie Zeit of the bank loan, months (0–36); counts only with a Bauphase"},"required":false,"description":"Bereitstellungszinsfreie Zeit of the bank loan, months (0–36); counts only with a Bauphase","name":"commitmentFreeMonths","in":"query"},{"schema":{"type":"string","description":"Bereitstellungszins of the bank loan, % per month on the undrawn part once the bereitstellungszinsfreie Zeit is over (0–1; 3 % p. a. is 0.25); counts only with a Bauphase"},"required":false,"description":"Bereitstellungszins of the bank loan, % per month on the undrawn part once the bereitstellungszinsfreie Zeit is over (0–1; 3 % p. a. is 0.25); counts only with a Bauphase","name":"commitmentFeeRate","in":"query"},{"schema":{"type":"string","description":"Bodenwertanteil, % of the price that is land (not depreciable). Send it when the Kaufvertrag or Bauträger states the split; left out, derived from landValuePerSqm and the plot share, else 20 assumed"},"required":false,"description":"Bodenwertanteil, % of the price that is land (not depreciable). Send it when the Kaufvertrag or Bauträger states the split; left out, derived from landValuePerSqm and the plot share, else 20 assumed","name":"landShare","in":"query"},{"schema":{"type":"string","description":"Bodenrichtwert, EUR per m² of plot, as lookup_address answers it"},"required":false,"description":"Bodenrichtwert, EUR per m² of plot, as lookup_address answers it","name":"landValuePerSqm","in":"query"},{"schema":{"type":"string","description":"Grundstücksfläche: the whole plot, m²"},"required":false,"description":"Grundstücksfläche: the whole plot, m²","name":"plotArea","in":"query"},{"schema":{"type":"string","description":"Miteigentumsanteil numerator: 56 of \"56/10.000\"; 1 for a house bought whole"},"required":false,"description":"Miteigentumsanteil numerator: 56 of \"56/10.000\"; 1 for a house bought whole","name":"coOwnershipShare","in":"query"},{"schema":{"type":"string","description":"Miteigentumsanteil denominator: 10000 of \"56/10.000\"; 1 for a house bought whole"},"required":false,"description":"Miteigentumsanteil denominator: 10000 of \"56/10.000\"; 1 for a house bought whole","name":"coOwnershipTotal","in":"query"},{"schema":{"type":"string","description":"GFZ of the Bodenrichtwert zone from lookup_address; estimates the plot share when plotArea and Miteigentumsanteil are unknown"},"required":false,"description":"GFZ of the Bodenrichtwert zone from lookup_address; estimates the plot share when plotArea and Miteigentumsanteil are unknown","name":"floorAreaRatio","in":"query"},{"schema":{"type":"string","description":"Grenzsteuersatz incl. Soli, %"},"required":false,"description":"Grenzsteuersatz incl. Soli, %","name":"taxRate","in":"query"},{"schema":{"type":"string","description":"Sanierungsanteil under § 7i, % of the building value; monument route only"},"required":false,"description":"Sanierungsanteil under § 7i, % of the building value; monument route only","name":"renovationShare","in":"query"},{"schema":{"type":"string","description":"Restnutzungsdauer from an appraisal, years; remainingLife route only"},"required":false,"description":"Restnutzungsdauer from an appraisal, years; remainingLife route only","name":"remainingUsefulLife","in":"query"},{"schema":{"type":"string","description":"Bundesland, two-letter code (BE, NW, BY …)"},"required":false,"description":"Bundesland, two-letter code (BE, NW, BY …)","name":"stateCode","in":"query"},{"schema":{"type":"string","description":"true when a Provision is due"},"required":false,"description":"true when a Provision is due","name":"hasBroker","in":"query"},{"schema":{"type":"string","description":"ltv, equity or loan: which financing input counts when several are given; left out, the one given"},"required":false,"description":"ltv, equity or loan: which financing input counts when several are given; left out, the one given","name":"finMode","in":"query"},{"schema":{"type":"string","description":"AfA route: linear2, linear25, linear3, declining5, declining7b, monument, remainingLife"},"required":false,"description":"AfA route: linear2, linear25, linear3, declining5, declining7b, monument, remainingLife","name":"depreciationVariant","in":"query"},{"schema":{"type":"string","description":"bullet for an endfälliges KfW-Darlehen; annuity otherwise"},"required":false,"description":"bullet for an endfälliges KfW-Darlehen; annuity otherwise","name":"kfwRepaymentMode","in":"query"},{"schema":{"type":"string","description":"Übergabe month of a new build, YYYY-MM"},"required":false,"description":"Übergabe month of a new build, YYYY-MM","name":"handover","in":"query"},{"schema":{"type":"string","description":"Month of the Kaufvertrag, YYYY-MM"},"required":false,"description":"Month of the Kaufvertrag, YYYY-MM","name":"contractMonth","in":"query"},{"schema":{"type":"string","description":"The address as a label; no input to the maths"},"required":false,"description":"The address as a label; no input to the maths","name":"address","in":"query"},{"schema":{"type":"string","description":"Latitude of the property; no input to the maths"},"required":false,"description":"Latitude of the property; no input to the maths","name":"lat","in":"query"},{"schema":{"type":"string","description":"Longitude of the property; no input to the maths"},"required":false,"description":"Longitude of the property; no input to the maths","name":"lng","in":"query"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"responses":{"200":{"description":"The calculation as markdown; JSON (the schema of POST /v1/calculations) with `Accept: application/json`.","content":{"text/markdown":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The link names no price, area, state or rent, or a value out of range; `details.fields` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/guide":{"get":{"operationId":"getGuide","tags":["calc"],"summary":"How to work with Immoprobe: the field mapping, the layers, the conduct.","description":"The guide an agent reads before the first calculation: how a listing maps to PropertyInput, what counts as an assumption, how to read each layer lookup_address answers, how to write notes and save, conduct on portals. The same text as the MCP resource immoprobe://guide.","security":[],"x-mcp":{"name":"get_guide","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"responses":{"200":{"description":"The guide.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guide"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/comparisons":{"post":{"operationId":"compareObjects","tags":["calc"],"summary":"Put 2–5 saved objects side by side.","description":"The same KPIs as the web comparison, one row per metric with the best value marked. Stateless: nothing is stored. `url` opens the same comparison in the browser.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"compare_objects","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareRequest"}}}},"responses":{"200":{"description":"The comparison.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Comparison"}}}},"404":{"description":"One of the ids is not an object of this account (`details.objectId`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/geo/{layer}":{"get":{"operationId":"getGeoLayer","tags":["geo"],"summary":"TileJSON of a baked layer.","description":"Bounds, zoom range and attribution as baked by the ETL; the tile URLs carry the archive version.","security":[],"x-mcp":false,"parameters":[{"schema":{"type":"string","pattern":"^[a-z0-9_-]+$","example":"brw","description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)"},"required":true,"description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)","name":"layer","in":"path"}],"responses":{"200":{"description":"The TileJSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TileJson"}}}},"404":{"description":"No such layer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/geo/{layer}/{z}/{x}/{tile}":{"get":{"operationId":"getGeoTile","tags":["geo"],"summary":"One vector tile of a baked layer.","description":"Served as stored in the archive (gzip), cached at the edge by archive version. Empty tiles answer 204.","security":[],"x-mcp":false,"parameters":[{"schema":{"type":"string","pattern":"^[a-z0-9_-]+$","example":"brw","description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)"},"required":true,"description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)","name":"layer","in":"path"},{"schema":{"type":"string","pattern":"^[0-9]+$","example":"14","description":"Zoom level"},"required":true,"description":"Zoom level","name":"z","in":"path"},{"schema":{"type":"string","pattern":"^[0-9]+$","example":"8801"},"required":true,"name":"x","in":"path"},{"schema":{"type":"string","pattern":"^[0-9]+\\.mvt$","example":"5432.mvt","description":"The y coordinate with the `.mvt` extension"},"required":true,"description":"The y coordinate with the `.mvt` extension","name":"tile","in":"path"}],"responses":{"200":{"description":"The tile.","content":{"application/vnd.mapbox-vector-tile":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such layer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/geo/{layer}/at":{"get":{"operationId":"lookupGeoLayer","tags":["geo"],"summary":"The zones of a layer at a WGS84 point.","description":"Reads one max-zoom tile and runs point-in-polygon; answers are cached per ~11 m grid cell for 30 days. Empty `zones` means the point is outside the baked coverage — say \"no data\", never guess. For brw, building land comes first; Brandenburg also lists the municipality-wide agricultural zones (entwicklung LF/SF) after it.","security":[],"x-mcp":false,"parameters":[{"schema":{"type":"string","pattern":"^[a-z0-9_-]+$","example":"brw","description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)"},"required":true,"description":"A baked layer: `brw` (Bodenrichtwert), `designations` (conservation and redevelopment areas, listed monuments), `census` (Zensus 2022 grid: rent, vacancy, population), `flood` (EU Floods Directive hazard zones) , `location` (Wohnlage per address from the local rent index), `noise` (strategic noise maps: isophone bands and building facades), `transit` (public transport stations with their modes and lines) or `parcels` (cadastral parcels, ALKIS Flurstücke)","name":"layer","in":"path"},{"schema":{"type":"string","pattern":"^-?[0-9]+(\\.[0-9]+)?$","example":"13.36"},"required":true,"name":"lng","in":"query"},{"schema":{"type":"string","pattern":"^-?[0-9]+(\\.[0-9]+)?$","example":"52.548"},"required":true,"name":"lat","in":"query"}],"responses":{"200":{"description":"Zones at the point.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Zones"}}}},"404":{"description":"No such layer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/geo/lookups":{"post":{"operationId":"lookupAddress","tags":["geo"],"summary":"Geocode an address and read the data layers at that point.","description":"Resolves a German address (free text or structured fields) to a point, then answers what the baked layers say there: the Bodenrichtwert, the statutory designations (Milieuschutz, Erhaltungsgebiet, Sanierungsgebiet, Denkmal), the Zensus 2022 cell (rent benchmark, vacancy), the flood hazard zones (HWRM-RL: HQhäufig, HQ100, HQextrem), the Wohnlage of the Mietspiegel (Berlin, Hamburg) and the strategic noise maps (rail nationwide, road and air in Berlin). Portals often hide the house number: `precision` says how exact the point is, and a value read at street precision or worse is marked `approximate` so it lands as an assumption. When the geocoder returns several plausible candidates, `best` is absent and `candidates` carries them — ask the user, then call again with `candidateId`. Only a call that actually geocodes counts against the monthly quota; `lat`/`lng` from the listing, a `candidateId` and a repeated address are free.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"lookup_address","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},"x-scope":"geo:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoLookupRequest"}}}},"responses":{"200":{"description":"Point, layers and attribution — or candidates to choose from.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoLookup"}}}},"400":{"description":"Nothing to resolve in the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Unknown candidateId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The monthly geocoding quota is spent (`details.limit`, `details.used`, `details.resetsAt`), or this server has no geocoder (`limit` 0) — lookups by lat and lng still work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many lookups this minute, or geocoding is paused for the day across Immoprobe; `details.retryAfter` in seconds, a lookup by point still works.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/login-options":{"get":{"operationId":"getLoginOptions","tags":["account"],"summary":"What the sign-in form needs before it sends: the Turnstile site key when the bot check is on.","security":[],"x-mcp":false,"responses":{"200":{"description":"The options; no key means no widget.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginOptions"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/login-requests":{"post":{"operationId":"requestLogin","tags":["account"],"summary":"Send the sign-in mail: a link and a six-digit code.","description":"Answers the same whether or not the address has an account. The mail carries a link that signs in on click and a code to type where the sign-in was started; both expire together.","security":[],"x-mcp":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"A mail is on its way if the address is deliverable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequested"}}}},"400":{"description":"Not an address, or the Turnstile check failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/sessions":{"post":{"operationId":"createSession","tags":["account"],"summary":"Sign in with the code from the mail.","description":"Redeems the six-digit code for the newest sign-in mail of the address and sets the session cookie. Five wrong codes invalidate the mail.","security":[],"x-mcp":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionRequest"}}}},"responses":{"200":{"description":"Signed in; the session cookie is set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"400":{"description":"Wrong, expired or locked code — `details.reason` says which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteSession","tags":["account"],"summary":"Sign out of this browser.","description":"Ends this session on the server — the cookie stops working wherever a copy of it is — and clears it. Needs no valid session: a cookie that has expired or was ended elsewhere is cleared all the same. Other browsers stay signed in.","security":[],"x-mcp":false,"responses":{"204":{"description":"Signed out."},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me":{"get":{"operationId":"getMe","tags":["account"],"summary":"The account behind the credential, its plan and remaining quotas.","description":"Also how an agent learns whether it is connected at all: an anonymous call answers 401 with a sign-in challenge.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"get_account","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account/plan-requests":{"post":{"operationId":"requestPlan","tags":["account"],"summary":"Ask for a larger plan; answered by hand.","description":"Mails the request with the account's usage to Immoprobe and records it on the account (`planRequest` on the account until it is answered). One open request at a time. Web session only.","security":[{"sessionCookie":[]}],"x-mcp":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequestInput"}}}},"responses":{"201":{"description":"Sent; the answer comes by mail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanRequest"}}}},"409":{"description":"A request is already open (`details.createdAt`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tokens":{"get":{"operationId":"listTokens","tags":["account"],"summary":"Personal access tokens and connected apps, without secrets.","security":[{"sessionCookie":[]}],"x-mcp":false,"responses":{"200":{"description":"The credentials acting for the account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenList"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createToken","tags":["account"],"summary":"Mint a personal access token; the secret is shown once.","security":[{"sessionCookie":[]}],"x-mcp":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenInput"}}}},"responses":{"201":{"description":"The token with its secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenCreated"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/tokens/{tokenId}":{"delete":{"operationId":"deleteToken","tags":["account"],"summary":"Revoke a personal token or a connected app's grant.","security":[{"sessionCookie":[]}],"x-mcp":false,"parameters":[{"schema":{"type":"string"},"required":true,"name":"tokenId","in":"path"}],"responses":{"204":{"description":"Revoked."},"404":{"description":"No such token of this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/activity":{"get":{"operationId":"listActivity","tags":["account"],"summary":"The account's latest calls, newest first.","description":"Every call made with the account's credentials on either surface: who (web session, token label or connected app), what (operation and MCP tool), whether it worked, how long it took.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"parameters":[{"schema":{"type":"string","pattern":"^[0-9]{1,3}$","description":"Rows to answer at most, 1–200; default 50","example":"50"},"required":false,"description":"Rows to answer at most, 1–200; default 50","name":"limit","in":"query"}],"responses":{"200":{"description":"The calls.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityList"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/export":{"get":{"operationId":"exportAccount","tags":["account"],"summary":"Everything the account holds, as one JSON document.","description":"Objects with notes, links, attachments (as metadata with their urls), lists, credentials without secrets, the call log. Web session only.","security":[{"sessionCookie":[]}],"x-mcp":false,"responses":{"200":{"description":"The export.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/account":{"delete":{"operationId":"deleteAccount","tags":["account"],"summary":"Delete the account with everything in it.","description":"Objects, notes, links, attachments with their bytes, lists, tokens, connected apps, sign-in tokens, the call log and the account itself; the session ends. Irreversible — the body repeats the e-mail address as confirmation. Web session only.","security":[{"sessionCookie":[]}],"x-mcp":false,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountRequest"}}}},"responses":{"204":{"description":"Deleted; the session cookie is cleared."},"400":{"description":"The e-mail address does not match the account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects":{"get":{"operationId":"listObjects","tags":["objects"],"summary":"The caller's objects, newest first.","description":"Compact rows: address, headline KPIs, status, lists, note count. Filter by `status`, `list` (id or name), `source` or a free-text `q` over title and address. Discarded objects show only when asked for by status. Answers at most `limit` rows and says `truncated` when there were more; `nextCursor`, passed as `cursor`, reads on after them.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"find_objects","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:read","parameters":[{"schema":{"type":"string","maxLength":120,"description":"Free text over title and address"},"required":false,"description":"Free text over title and address","name":"q","in":"query"},{"schema":{"$ref":"#/components/schemas/ObjectStatus"},"required":false,"description":"active — in the list; discarded — in the trash (Papierkorb): hidden from lists, still known to check_known_listings so the listing is not saved again, deleted for good 90 days later","name":"status","in":"query"},{"schema":{"type":"string","maxLength":60,"description":"List id or exact name"},"required":false,"description":"List id or exact name","name":"list","in":"query"},{"schema":{"$ref":"#/components/schemas/ObjectSource"},"required":false,"description":"Where the object came from. manual is the web form; neubaukompass is the new-build portal (its externalId is the unit or project id); other is any portal not listed — put its page in listingUrl","name":"source","in":"query"},{"schema":{"type":"string","pattern":"^[0-9]{1,3}$","description":"Rows to answer at most, 1–100; default 25","example":"25"},"required":false,"description":"Rows to answer at most, 1–100; default 25","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"nextCursor of the previous page"},"required":false,"description":"nextCursor of the previous page","name":"cursor","in":"query"}],"responses":{"200":{"description":"The rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectList"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createObject","tags":["objects"],"summary":"Save a property; updates it when the listing is already known.","description":"Creates an object from a PropertyInput plus address and provenance. With `source` and `externalId` (the portal's listing id) the call is idempotent: a second call updates the existing object instead of duplicating it. A saved object at the same address with a living area within 5 m² does not block the save; it is reported in `possibleDuplicates` for the caller to decide. `note` writes the first note and `lists` adds memberships in the same request. The object is in the list at once; who saved it is recorded as provenance. Never deletes anything.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"save_object","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectInput"}}}},"responses":{"200":{"description":"Updated an object with the same source and externalId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectSaved"}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectSaved"}}}},"400":{"description":"Input failed validation; `details.fields` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The plan holds no more objects (`details.limit`, `details.used`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}":{"get":{"operationId":"getObject","tags":["objects"],"summary":"One object in full, with its notes, links, attachments and provenance.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"get_object","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:read","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"responses":{"200":{"description":"The object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"operationId":"updateObject","tags":["objects"],"summary":"Replace the object's input, address, title and provenance.","description":"Whole-resource replace with the same body as create. Notes and lists are kept; `lists` and `note` in the body are ignored here — they have their own endpoints.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectInput"}}}},"responses":{"200":{"description":"Replaced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Another object of this account already carries that source and externalId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteObject","tags":["objects"],"summary":"Delete the object with its notes, links and attachments. REST and web only.","description":"Deliberately not an MCP tool: nothing irreversible is reachable through an agent. An agent that wants an object out of the way sets its status to discarded — the trash, emptied after 90 days.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"responses":{"204":{"description":"Deleted."},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/clones":{"post":{"operationId":"cloneObject","tags":["objects"],"summary":"Save a copy of the object under a new title, with some of its figures changed — a variant to compare.","description":"The copy takes the address, the calculator input with its assumptions, the source and the listing page, and sits in the same lists; `input` replaces the fields it names (null puts one back to its default). It is active whatever the original is, and starts its own history: notes, links and files stay with the original, and the portal's externalId is not carried over, so check_known_listings keeps answering the original and the copy is never mistaken for the listing. Made for variants — the same flat with another financing, a negotiated price. Counts against the plan like any new object.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"clone_object","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneObjectRequest"}}}},"responses":{"201":{"description":"The copy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"400":{"description":"The input with the overrides applied failed validation; `details.fields` names the fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The plan holds no more objects (`details.limit`, `details.used`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/status":{"put":{"operationId":"setObjectStatus","tags":["objects"],"summary":"Move an object into the trash (discarded) or back to the list (active).","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"set_object_status","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetObjectStatusRequest"}}}},"responses":{"200":{"description":"The object with its new status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/title":{"put":{"operationId":"setObjectTitle","tags":["objects"],"summary":"Rename the object.","description":"Only the title changes; input, address and everything else stay. An agent renames through save_object with the same source and externalId.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"},{"schema":{"type":"string","maxLength":35,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German"},"required":false,"description":"Language of the worded parts (verdict, summary, warnings): de or en, the user's language; any other tag answers English, translate yourself. Left out: the account's language, else German","name":"language","in":"query"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetObjectTitleRequest"}}}},"responses":{"200":{"description":"The object with its new title.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Object"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/lists":{"put":{"operationId":"setObjectLists","tags":["lists"],"summary":"Replace which lists the object belongs to.","description":"Names that do not exist yet are created. Sent whole, like the membership it replaces; an empty array removes the object from every list.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"set_object_lists","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetObjectListsRequest"}}}},"responses":{"200":{"description":"The object's lists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMembership"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/known-ids":{"post":{"operationId":"checkKnownListings","tags":["objects"],"summary":"Which of these portal listing ids are already saved?","description":"The sweep cursor: hand over the ids from one results page and open only the unknown ones. Answers both lists, with the last saved price of the known ones, so a changed price can be noticed from the results page.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"check_known_listings","annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:read","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIdsRequest"}}}},"responses":{"200":{"description":"Known and unknown ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIds"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/notes":{"get":{"operationId":"listNotes","tags":["notes"],"summary":"Notes on the object, oldest first.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:read","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"responses":{"200":{"description":"The notes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteList"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createNote","tags":["notes"],"summary":"Attach a note — an assessment, a viewing, a question for the Makler.","description":"Markdown body up to 20 000 characters. The author is derived from the credential (the user in the web app, or the connected app or token label); `model` is self-reported and shown as such. Send `idempotencyKey` when a retry must not write a second copy.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"add_note","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteInput"}}}},"responses":{"200":{"description":"A retry with a known idempotencyKey: the note already written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Note"}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Note"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/notes/{noteId}":{"put":{"operationId":"updateNote","tags":["notes"],"summary":"Replace a note's kind, title and body. REST and web only.","description":"Whole-resource replace with the same body as create. The author and `createdAt` stay as written; `updatedAt` records the edit. `model` and `idempotencyKey` in the body are ignored here. Deliberately not an MCP tool: an agent corrects a note by writing another.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"noteId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteInput"}}}},"responses":{"200":{"description":"Replaced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Note"}}}},"404":{"description":"Not the caller's note.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteNote","tags":["notes"],"summary":"Delete a note. REST and web only.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"noteId","in":"path"}],"responses":{"204":{"description":"Deleted."},"404":{"description":"Not the caller's note.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/links":{"get":{"operationId":"listLinks","tags":["links"],"summary":"The object's links, oldest first.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:read","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"responses":{"200":{"description":"The links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkList"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createLink","tags":["links"],"summary":"Add a page to the object: the same flat elsewhere, the developer, a plan.","description":"One link per URL per object: sending a URL the object already has answers the existing link. The object's own listingUrl is refused — it is not a second page.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"add_link","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkInput"}}}},"responses":{"200":{"description":"The object already had this URL: the existing link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Link"}}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Link"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The URL is the object's listingUrl.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/links/{linkId}":{"delete":{"operationId":"deleteLink","tags":["links"],"summary":"Remove a link. REST and web only.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"linkId","in":"path"}],"responses":{"204":{"description":"Deleted."},"404":{"description":"Not the caller's link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/attachments":{"get":{"operationId":"listAttachments","tags":["attachments"],"summary":"The object's files, oldest first.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:read","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"responses":{"200":{"description":"The attachments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentList"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createAttachment","tags":["attachments"],"summary":"Upload a photo, a floor plan or a document to the object.","description":"A multipart form with `file` and `kind`. JPEG, PNG, WebP, PDF, DOCX or XLSX, up to 20 MB each; the plan caps the bytes an account keeps in total (`quotas.storage` on the account). The first photo becomes the cover. Web and REST only: a file does not travel through a tool call — an agent names URLs to createAttachmentsFromUrls instead.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/AttachmentUpload"}}}},"responses":{"201":{"description":"Stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Attachment"}}}},"400":{"description":"Not an accepted type, or over 20 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The plan holds no more bytes (`details.limit`, `details.used`, `details.size`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/attachments/from-urls":{"post":{"operationId":"createAttachmentsFromUrls","tags":["attachments"],"summary":"Fetch files by URL into the object: the listing's photos, its floor plan, a linked exposé.","description":"The API downloads each URL itself — http(s), JPEG, PNG, WebP, PDF, DOCX or XLSX, up to 20 MB, 15 s — and stores what it gets like an upload; the plan's storage quota applies. Up to 20 URLs per call. A URL the object already holds answers the existing attachment instead of a second copy. Pages behind a login or a bot check fail; `failed` names each URL with the reason, the rest are stored — the call answers 200 either way.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":{"name":"add_attachments_from_urls","annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}},"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentsFromUrlsRequest"}}}},"responses":{"200":{"description":"What was stored and what failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentsFromUrlsResult"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/attachments/{attachmentId}/content":{"get":{"operationId":"getAttachmentContent","tags":["attachments"],"summary":"The bytes of an attachment, for the owner.","description":"Served with the type and name as uploaded, `inline` for images and PDF. With `w`, an image comes resized as WebP; the copy is made on the first request and kept. Cacheable privately for a year: an id never names another file. Honors If-None-Match.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:read","parameters":[{"schema":{"type":"string"},"required":true,"name":"attachmentId","in":"path"},{"schema":{"type":"string","enum":["400","1600"],"description":"For an image: a copy no wider than this, as WebP — 400 for tiles and covers, 1600 for a large view. Never upscaled; ignored for other files"},"required":false,"description":"For an image: a copy no wider than this, as WebP — 400 for tiles and covers, 1600 for a large view. Never upscaled; ignored for other files","name":"w","in":"query"}],"responses":{"200":{"description":"The file.","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Not the caller's attachment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/attachments/{attachmentId}":{"delete":{"operationId":"deleteAttachment","tags":["attachments"],"summary":"Delete an attachment with its bytes. REST and web only.","description":"When the cover goes, the oldest remaining photo takes its place.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"attachmentId","in":"path"}],"responses":{"204":{"description":"Deleted."},"404":{"description":"Not the caller's attachment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/objects/{objectId}/cover":{"put":{"operationId":"setObjectCover","tags":["attachments"],"summary":"Choose which photo stands for the object.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"objectId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetObjectCoverRequest"}}}},"responses":{"200":{"description":"The object's attachments with the new cover.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentList"}}}},"400":{"description":"The attachment is not a photo of this object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not the caller's object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/lists":{"get":{"operationId":"listLists","tags":["lists"],"summary":"The caller's lists with object counts.","security":[{"bearerToken":[]},{"sessionCookie":[]}],"x-mcp":false,"x-scope":"objects:read","responses":{"200":{"description":"The lists, alphabetical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListList"}}}},"default":{"description":"Any other failure; `code` is a closed list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}