Immobilien

Portal-independent property files, brokerage records and optional ImmoScout24 preparation.

The bundled React module at /modules/immobilien manages workspace-global residential brokerage files. Install it through Module Store. Web, iOS and macOS use the same /api/real-estate API. Creator IDs record attribution; other authenticated workspace users can read and edit the same records.

Current workflow

Create a property with a unique number and title. Add address, sale/rent, apartment/house, nullable price/area/rooms, owner/contact, description, stage and next step. Stages cover intake, documents, marketing, viewings, negotiation, closed and archived. Search by title, number or city and optionally include archived properties. Forms save explicitly and surface revision conflicts instead of overwriting another editor's changes.

Add inquiries, viewing plans, document references and tasks to the property. Each record has a title, contact, notes and open/done/cancelled status. Viewing plans require timezone-qualified start/end timestamps with a positive duration. Internal links open existing document, customer, task or calendar surfaces under their original access controls. A link does not copy or share private source content. No private mailbox ingestion occurs automatically.

These are property-local records. Creating a viewing plan does not send an invitation or create a calendar event; creating a task record does not create an Aufgaben entity. Existing calendar/tasks/documents may be linked explicitly. Exposé generation, automated inquiry ingestion, matching and a public owner portal remain future scope. No background automation is implied by this initial version.

All mutations and their snapshots are recorded transactionally in real_estate_audit. Updates require expected_revision; stale changes return 409. Duplicate property numbers return 409. Records cannot be updated through a different parent property. No hard-delete endpoint is exposed.

Optional ImmoScout24 app preparation

Administrators can configure Sandbox and Production independently under Settings → App connections. Consumer key and secret are encrypted using the existing application secret crypto. API responses contain only environment, revision and update timestamp. Blank credential inputs retain existing values; removing configuration does not alter properties or remote listings. Configuration changes also reject stale revisions.

This prepares configuration only: connected and publish_enabled remain false. OAuth authorization, remote import, media transfer, publication, deactivation and incoming lead APIs are not enabled. The module makes no provider network calls. The property preview performs a local basic-field check and explicitly does not certify all ImmoScout24 publication requirements. It remains useful without credentials or internet access.

Official references: OAuth 1.0a, business account and paid production access, listing resources. Provider-specific behavior belongs in optional adapters, never in core property persistence.

Agent access

real_estate_workspace is exposed to chat/live agents and gated by module installation. list and get retrieve current data and revisions. property_create, property_update, record_create and record_update use the same authenticated business API as the UI. data on updates is a complete replacement, so read the current record first. Page context includes module and selected property ID/revision. Agents must report internal saves accurately and cannot publish listings or send invitations through this tool.

Migration and assets

Migration 308_real_estate.sql creates properties, records, audit and separate optional ImmoScout configuration storage. Apply before using the new APIs. Both app-store and navigation PNGs are included, with a native asset catalog counterpart. They were generated using the built-in image tool: rounded white tile with navy house/building; separate full-canvas navy house with blue window and removable chroma-key background. Sources remain in the task's generated_images directory.

See the implementation contracts in Agent tool contracts and API reference. The broader product proposal remains in docs/immobilien-module-design.md; it describes subsequent work as well as this initial module.

Validation notes

The implementation was checked with focused validation, transaction, API-auth and configuration tests plus real PostgreSQL tests in an isolated schema. The database checks cover cross-user access, concurrent updates, duplicate numbers and parent-bound record updates. Web production and local source-built Docker validation, SwiftPM macOS and the iOS Simulator scheme are required for this change. Browser QA uses a clearly marked local QA property and inquiry, verifies persistence after reload and the local portal checklist. The general module-store Hub catalogue was unavailable during QA; bundled-module installation still worked independently.

The generated assets are public/assets/module-app-icons/immobilien.png (1024×1024) and public/icons/navigation/immobilien.png (256×256 RGBA), with an optimized web copy and native immobilien.imageset. Prompts: a quiet white app tile with a navy house and building; a separate solid navy house silhouette with four square window cutouts, flat uniform color and crisp geometric edges, no shading or halo. Built-in image generation was used. The replacement navigation source used an opaque white key background after the chroma-key output produced alpha artifacts; local background removal and fill normalization produce transparent RGBA assets. The 256px source and 32px sidebar rendering were visually checked; web and native copies match.

Final local verification: 45 focused tests passed across validation, service transactions, middleware scopes, route authorization, encrypted configuration, page capabilities and isolated PostgreSQL. Production web build and local Docker source build succeeded; macOS SwiftPM and iOS Simulator builds succeeded without Swift/Xcode warnings. Lint reported 0 errors and 17 existing warnings outside this feature. The running Docker tool dispatcher successfully performed getrecord_updateget → property archive → get on the local QA record. The QA property remains archived, preserving the test audit trail. Browser checks verified create, save, reload, record persistence, local portal preparation and separate admin configuration. Native runtime/device interaction was not exercised. The browser viewport override did not take effect (it stayed at 1280px), so the 390px browser layout remains unverified.

Create from a listing URL

Web, iPhone/iPad and Mac offer Create from URL next to manual creation. Paste a complete HTTP(S) listing URL and choose Create with agent. The existing chat opens and automatically starts the request; progress, retrieval problems and the final property link appear there. Follow the link to review and complete the saved property. This works independently of the ImmoScout24 app connection and uses the configured agent and its available web tools. A site requiring login or blocking retrieval may not be importable.

The localized request tells the agent to treat retrieved content as data, use only supported facts, check listing number/source for an existing record, retain the source URL and uncertainty in the description, and create through real_estate_workspace in intake. If retrieval fails, no property should be created. Required information that cannot be determined triggers a question. These extraction and duplicate checks are agent instructions, not a new deterministic scraping API or database uniqueness rule for URLs. Existing property validation, module permissions and audit still apply. No portal publication is performed.

Web handoff uses the existing /chat?message=…&send=1&context=/modules/immobilien route. Native uses the existing AppModel chat submission flow. HTTP(S) validation rejects embedded credentials and malformed/oversized URLs before handoff. No server-side URL fetcher or new API contract is introduced.

URL-import verification: 19 focused URL/property-validation tests passed. Browser checks covered disabled empty submission, credential-bearing URL rejection, automatic chat submission, a non-listing URL without record creation, an existing ImmoScout listing without a duplicate, and submitting a second URL. The property view observes query changes when retained in a tab and accepts both the canonical property query key and propertyId links returned by agents. Swift/macOS and iOS Simulator builds passed without compiler warnings. The local Docker production build passed; host Next.js compilation/type checking passed but standalone packaging encountered transient .git/Swift build files disappearing during concurrent workspace activity.

Property images

Saved properties have a workspace-global gallery on web, iPhone/iPad and Mac. Upload JPEG, PNG or WebP files (up to 5 MB and 32 megapixels each, at most 40 active images per property). Apple also provides the system photo picker. Select a photo to view it larger; choose a cover image or remove a photo from the gallery. The first upload becomes the cover; removing the cover promotes the oldest remaining image. Removing is a soft removal: the image stops being served but its stored bytes and audit record are retained.

The server decodes the image, applies orientation, strips metadata and stores a WebP up to 2048 pixels per side in PostgreSQL (309_real_estate_images.sql). Metadata/list responses never contain binary/base64 content. Image retrieval requires the same workspace authentication as property access and uses private, no-store caching. Creator identity is audit attribution, not a visibility filter. Cover/removal operations require the current image revision and are serialized per property.

Agents can list, upload, import, select the cover and remove photos with real_estate_workspace. Remote import accepts a direct public HTTPS image URL; it pins the connection to a validated public IPv4 address and retains hostname certificate verification. It does not follow redirects, send cookies, or access private networks. Retrieval and decoding failures are reported separately from successful property creation. A listing URL is not itself an image URL. No ImmoScout app connection is needed. Images remain internal workspace media and are not automatically published to a portal.

Gallery verification: 35 focused tests passed, including real PostgreSQL image lifecycle/concurrency coverage. Live tool-proxy checks exercised image upload, direct public HTTPS import, listing, cover selection and unauthenticated retrieval (401). Browser checks confirmed image decoding, full preview, cover reordering and removal/promotion; QA photos were soft-removed afterwards. Web production build, local Docker source build, macOS Swift build and iOS Simulator build passed; no new native compiler warnings remain. Native picker interactions were not exercised on a device. Repository lint had zero errors and 17 existing warnings outside this change.

Drag-and-drop gallery

Drop several image files anywhere inside the web gallery to upload them. The gallery highlights during a file drag, shows local thumbnail previews and per-file progress during upload, and keeps an Add images tile for keyboard, touch and file-picker access. Existing photo tiles retain preview, cover and remove controls. A batch is checked for supported file types, file size and remaining gallery capacity before upload; a second drop while busy is ignored. Successful uploads remain visible if a later upload fails. Local preview URLs are released when the batch finishes.

The Apple gallery also accepts image/file drops from Finder or Files and keeps the system photo picker and add tile available. Both entry points use the existing authenticated image_add contract and the same server limits. Dragging does not reorder photos; the cover action determines the first image.

Property list responses include nullable cover_image_id, selected from active gallery images (cover first). Web, iOS and macOS property cards display this authenticated image as a preview; gallery cover changes and removals update the card. Properties without images remain text-only. The real_estate_workspace list action returns the same metadata; no new action or image permissions are introduced.

Property location

Each detail view includes an address-based map: Google Maps on web and native Apple Maps on iOS/macOS, with an external Maps search link. Empty addresses show a prompt to complete the record. City-only addresses show the surrounding area; a search result is not a verified property boundary or exact building location. Updating the address refreshes the map. Map providers receive the displayed address; the workflow does not request the user's device location. Maps require provider connectivity. If an embedded map is unavailable, use the external search link.

Property details use responsive tiles for price, living area, rooms and contact, alongside a square map with the address below it. Currency and decimal values follow the selected UI language; rental prices are labelled as monthly rent excluding charges. The existing property and agent contracts are unchanged.