# Open houses

> Host open houses for other agents, list and RSVP to theirs, and serve the keyless invitation page.

An open house opens one of your listings to agents from other agencies for a time window, with what the introducing agency earns. Agents on Fondaro RSVP from the dashboard; anyone else RSVPs from the invitation link after confirming their email. Open houses are free for every agency.

The `/network/open-houses` routes take a signed-in dashboard session with an active organization (`Authorization: Bearer <session token>`); a property API key is refused. On the [MCP server](https://www.fondaro.com/docs/api/mcp.md), `list_open_houses` and `get_open_house` give the same reads. The `/public/open-houses` routes need no authentication.

| Reader | Street address and map | Commission offered |
|---|---|---|
| A signed-in agent | Yes | Yes |
| Anyone with the link | Only after confirming their RSVP | Yes |

The listing owner's private commission is never shown.

## Host an open house

Only your agency's admin or the listing's agent can create or change one (`403 OPEN_HOUSE_HOST_ONLY` otherwise).

### `POST /network/open-houses`

Creates a draft on one of your own Fondaro listings.

| Field | Type | Rules |
|---|---|---|
| `listingRef` | object | Required. `{ "source": "internal", "id": "<listing id>" }`; any other source is `422 OPEN_HOUSE_LISTING_NOT_OWN` |
| `startsAt`, `endsAt` | ISO 8601 | Required; the end must be after the start |
| `timezone` | string | IANA zone, default `Europe/Madrid` |
| `visibility` | string | `network` (listed for agents, the default) or `link` (only people with the link) |
| `commissionOffer` | number | Percent to the introducing agency, 0 to 100 |
| `commissionNotes` | string | Up to 1,000 characters |
| `invitation` | object | `{ body, headline?, points?, language? }`: body up to 5,000 characters, up to 12 points |
| `disclosedAddress`, `mapUrl` | string | Where to meet |
| `contactName`, `contactPhone` | string | Who to call on the day |

```bash
curl -X POST https://api.fondaro.com/network/open-houses \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "listingRef": { "source": "internal", "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" },
    "startsAt": "2026-10-09T09:00:00Z",
    "endsAt": "2026-10-09T12:00:00Z",
    "timezone": "Europe/Madrid",
    "visibility": "network",
    "commissionOffer": 4,
    "invitation": { "body": "Come and see it before it goes to the portals." }
  }'
```

Returns the open house with `"status": "draft"` and its `slug`.

### `PATCH /network/open-houses/:id`

Changes any field above except `listingRef`; `null` clears an optional one. A cancelled open house returns `409 OPEN_HOUSE_CLOSED`. Agents who are going are told when the time, address or commission changes.

### `POST /network/open-houses/:id/publish`

Publishes a draft. The start must be in the future, the listing must be on the market and the invitation body written. Without a `commissionOffer`, the listing's shared commission is used; with neither, the publish is refused. The commission is fixed at publishing, so a later listing change cannot alter the offer.

### `POST /network/open-houses/:id/cancel`

Cancels it and tells everyone going.

### `GET /network/open-houses/:id/rsvps`

Who is coming, host only.

```json
[
  {
    "id": "5e0c2a1b-...",
    "status": "going",
    "kind": "network",
    "attendeeUserId": "user_2xyz",
    "agencyName": "Costa Homes",
    "attendeeName": "Ana García",
    "attendeeImageUrl": "https://...",
    "guestName": null,
    "guestEmail": null
  },
  {
    "id": "7a91c3d4-...",
    "status": "going",
    "kind": "guest",
    "agencyName": "Guest Agency",
    "guestName": "Pat Guest",
    "guestEmail": "pat@example.com"
  }
]
```

An agent on Fondaro shows as other agencies see them, never by their sign-in email. A guest shows what they typed.

## List open houses

### `GET /network/open-houses`

| Query | Default | Meaning |
|---|---|---|
| `scope` | `network` | `network`: published events other agents can see, soonest first. `mine`: your agency's, in every status |
| `from` | now | Events ending after this time |
| `to` | none | Events starting before this time |
| `days` | none | Events starting within this many days of `from` (1 to 31); the earlier of `to` and `days` wins |
| `area` | none | The listing's town, area or community (case and accents ignored) |
| `placeId`, `placeIds` | none | Places from `GET /places` (up to 5): the listing is in one or inside it |
| `source` | none | Only listings from this source |
| `listingSource`, `listingId`, `listingCountry` | none | Exactly one listing, as a search returned its `ref` |
| `limit` | 50 | 1 to 100 |

```bash
curl "https://api.fondaro.com/network/open-houses?area=nueva%20andalucia&days=14" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

```json
{
  "items": [
    {
      "id": "0b4d6c1e-8a53-4c2e-9f61-2b1f0e7d9a10",
      "slug": "q3H8vZ0bRk2mXw4t",
      "status": "published",
      "visibility": "network",
      "listingRef": { "source": "internal", "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" },
      "startsAt": "2026-10-09T09:00:00.000Z",
      "endsAt": "2026-10-09T12:00:00.000Z",
      "timezone": "Europe/Madrid",
      "disclosedAddress": "Calle Ejemplo 7, Los Granados Golf",
      "commissionOffer": 4,
      "commissionNotes": "+ VAT, payable on invoice",
      "invitation": { "headline": "Garden apartment on the golf", "body": "Come and see it before it goes to the portals." },
      "host": { "organizationId": "...", "name": "Example Realty", "logoUrl": "https://..." },
      "hostUserId": "user_2abc",
      "listing": { "title": "Garden apartment", "price": 650000, "currency": "EUR", "bedrooms": 3, "city": "Marbella" },
      "goingCount": 4,
      "myRsvp": null,
      "isHost": false
    }
  ],
  "hasMore": false
}
```

`listing` is read live, so price and photos are current; it is `null` once the listing is off the market. Message the host by starting a conversation with `hostUserId`. `listingId` without `listingSource`, or a `source` that contradicts it, is `400 OPEN_HOUSE_FILTER_INVALID`.

### `GET /network/open-houses/:id`

One open house in the same shape. Another agency's event is readable once published, including one shared by link only; a draft is the host's alone.

### `GET /network/open-houses/by-slug/:slug`

The same read by the slug in an invitation link, so an app opening the link while signed in can show the full event.

## RSVP as an agent

### `POST /network/open-houses/:id/rsvp`

| Field | Values |
|---|---|
| `status` | `going` (default) or `declined` |

```bash
curl -X POST https://api.fondaro.com/network/open-houses/0b4d6c1e-8a53-4c2e-9f61-2b1f0e7d9a10/rsvp \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"going"}'
```

One RSVP per agent; send `declined` to take it back. Returns the open house with your `myRsvp`.

## The public invitation

### `GET /public/open-houses/:slug`

The invitation page's data, with no street address, postcode or map. Never listed publicly; the response carries `X-Robots-Tag: noindex, nofollow` and is cached up to five minutes.

```bash
curl https://api.fondaro.com/public/open-houses/q3H8vZ0bRk2mXw4t
```

```json
{
  "slug": "q3H8vZ0bRk2mXw4t",
  "status": "published",
  "startsAt": "2026-10-09T09:00:00.000Z",
  "endsAt": "2026-10-09T12:00:00.000Z",
  "timezone": "Europe/Madrid",
  "commissionOffer": 4,
  "invitation": { "body": "Come and see it before it goes to the portals." },
  "host": { "name": "Example Realty", "logoUrl": "https://..." },
  "listing": { "title": "Garden apartment", "price": 650000, "city": "Marbella" },
  "goingCount": 4,
  "rsvpOpen": true
}
```

### `POST /public/open-houses/:slug/rsvp`

| Field | Rules |
|---|---|
| `name` | Required, up to 200 characters |
| `email` | Required |
| `agency` | Optional |
| `website` | Hidden form field; must stay empty |

Answers `202 { "status": "pending_confirmation" }` and emails a confirmation link. Nothing is saved until it is confirmed, and the answer is the same whether or not the email has RSVPed before.

### `POST /public/open-houses/:slug/rsvp/confirm`

Confirms with the emailed `token` (valid 48 hours). It is a `POST` so an email scanner opening the link cannot confirm for someone.

```bash
curl -X POST https://api.fondaro.com/public/open-houses/q3H8vZ0bRk2mXw4t/rsvp/confirm \
  -H "Content-Type: application/json" \
  -d '{"token":"v2:..."}'
```

```json
{
  "status": "confirmed",
  "openHouse": { "slug": "q3H8vZ0bRk2mXw4t", "goingCount": 5, "rsvpOpen": true },
  "disclosedAddress": "Calle Ejemplo 7, Los Granados Golf",
  "mapUrl": "https://maps.example.com/?q=36.49,-4.98",
  "listingAddress": { "line1": "Calle Ejemplo 7", "postalCode": "29660" },
  "latitude": 36.4912,
  "longitude": -4.9876
}
```

## Notifications

Agents with the mobile app get a push with `{ "type": "open_house.<event>", "route": "open_house", "openHouseId": "..." }`. Each type is also a preference key for `PUT /notifications/preferences`, on by default.

| Type | Sent to |
|---|---|
| `open_house.rsvp_created` | The creator, when someone is going |
| `open_house.updated` | Everyone going, when the time, address or commission changes |
| `open_house.cancelled` | Everyone going |

## Errors and limits

| Status | Code | When |
|---|---|---|
| `400` | `OPEN_HOUSE_FILTER_INVALID` | A one-listing filter is incomplete or contradicts `source`, or a place id is unknown |
| `400` | `OPEN_HOUSE_WINDOW_INVALID`, `OPEN_HOUSE_TIMEZONE_INVALID` | A bad date, an end before the start, or an unknown time zone |
| `400` | `OPEN_HOUSE_OWN_EVENT` | RSVP to your own agency's event |
| `400` | `OPEN_HOUSE_RSVP_TOKEN_INVALID` | The confirmation link is invalid or expired |
| `403` | `OPEN_HOUSE_HOST_ONLY` | Not the agency admin or the listing's agent |
| `404` | `OPEN_HOUSE_NOT_FOUND` | Missing, or not visible to you |
| `409` | `OPEN_HOUSE_CLOSED`, `OPEN_HOUSE_RSVP_CLOSED` | `OPEN_HOUSE_CLOSED`: cancelled; `OPEN_HOUSE_RSVP_CLOSED`: cancelled or past its end |
| `422` | `OPEN_HOUSE_LISTING_NOT_OWN`, `OPEN_HOUSE_LISTING_UNREADABLE`, `OPEN_HOUSE_LISTING_NOT_ACTIVE` | Not your Fondaro listing, unreadable, or off the market |
| `422` | `OPEN_HOUSE_WINDOW_PAST`, `OPEN_HOUSE_COMMISSION_REQUIRED`, `OPEN_HOUSE_INVITATION_REQUIRED` | Publishing without a future start, a commission or an invitation |
| `503` | `OPEN_HOUSE_EMAIL_FAILED` | The confirmation email could not be sent; try again |

The public read allows 60 requests a minute per address, the public RSVP 5 a minute (20 per 10 minutes), and confirmation 20 a minute.

Source: https://www.fondaro.com/docs/api/open-houses
