> ## Documentation Index
> Fetch the complete documentation index at: https://api.housera.ro/llms.txt
> Use this file to discover all available pages before exploring further.

# Viewing agenda

> How you read the viewings on all your listings and work with them: the client who called, another time, cancelling and “How did it go?”.

The agenda gathers the viewings, requests and open houses of every listing you run, like the agenda in your account. Through the API you read it and work with each viewing. Reading needs the `listings:read` ability. The clients' details also need `leads:read`.

## What the agenda holds

A key's agenda is its account's agenda in the portal:

* the account's active listings;
* the listings whose viewings a colleague gave to it;
* for the owner, the admins and the managers, the team's listings (a manager sees their team);
* for a developer's consultant, the projects they work on, and there only the clients who came to them and the open days.

A listing that is not public does not appear. A key bound to a firm sees only that firm's listings and the account's listings without a firm.

## Reading the agenda

`GET /account/viewings` returns the hours of a range, one page at a time, in order of time:

```bash theme={null}
curl "https://housera.ro/api/v1/account/viewings?from=2026-11-16&to=2026-11-22&status=pending,booked" \
  -H "Authorization: Bearer $HOUSERA_KEY"
```

| Parameter | What it does |
| - | - |
| `from`, `to` | The range: a day (`2026-11-16`, on Romania's clock, as the agenda reads it) or a moment with its time zone. A day sent as `to` is taken in whole. By default, from today for 14 days. At most 93 days. |
| `listing` | A listing's code. A code outside the agenda gives an empty list. |
| `agent` | A colleague's email: only what they run. The owner, an admin or a manager can name any colleague in their agenda. Anyone else can name only their own email. Otherwise the list is empty. |
| `status` | The viewings' states, comma separated: `pending`, `booked`, `completed`, `available`. By default `pending,booked,completed`. `available` also needs `listing`. |
| `kind` | `viewing` or `open_house`. Without it, both. Open houses are listed whatever the `status`. |
| `per_page`, `cursor` | The page size (at most 100, 50 by default) and the cursor from `meta.next_cursor`. |

```json theme={null}
{ "data": [ {
    "id": 9182, "kind": "viewing", "status": "completed",
    "starts_at": "2026-11-10T08:00:00+00:00", "ends_at": "2026-11-10T08:30:00+00:00", "request_expires_at": null,
    "visitor": { "name": "Ana Popescu", "email": "ana@exemplu.ro", "phone": "+40740123456", "account": true },
    "source": "manual", "duration_min": 30, "held": false, "bookable": null,
    "booked_at": "2026-11-08T10:12:00+00:00", "proposed_starts_at": null,
    "attendance_confirmed_at": "2026-11-09T07:00:00+00:00", "reminded_day_at": "2026-11-09T07:00:00+00:00", "reminded_soon_at": null,
    "outcome": "again", "outcome_at": "2026-11-10T09:00:00+00:00",
    "agent": { "name": "Maria Popa", "email": "maria@exemplu.ro" },
    "place": null, "place_rooms": null, "seat": 0,
    "listing": { "code": "HEWPUTA8", "external_ref": "crm-118", "title": "Apartament 3 camere, Copou", "url": "https://housera.ro/anunt/hewputa8" },
    "outcome_note": "Vrea să revină cu soția."
  } ],
  "links": { "first": null, "last": null, "prev": null, "next": null },
  "meta": { "path": "https://housera.ro/api/v1/account/viewings", "per_page": 50, "next_cursor": null, "prev_cursor": null,
    "pending_requests": 2, "next_at": "2026-11-16T10:00:00+00:00" } }
```

`meta.pending_requests` counts every request waiting for your answer in the agenda, not only those in the range. A request you answered with another time waits for the client and is not counted, as in the portal. `meta.next_at` is the next viewing or request.

`GET /viewings/{id}` returns one hour, in the same shape. An hour outside the agenda answers `404`.

## The states

* `available`: a free hour.
* `pending`: a request waiting for your answer, until `request_expires_at`.
* `booked`: a booked viewing that has not passed.
* `completed`: a booked viewing whose hour has ended. The state is worked out when you read it: a viewing becomes `completed` as soon as its hour ends.

`completed` appears only here and in `GET /viewings/{id}`. A listing's agenda (`GET /account/listings/{code}/viewings`) shows only the hours that have not passed, with the states `available`, `pending` and `booked`.

## The client and their details

`visitor` and `outcome_note` come only to a key with the `leads:read` ability. For other keys they are `null`, while `outcome` and `outcome_at` stay.

* `account: true`: the client has a Housera account, with a name, an email and a phone.
* `account: false`: a client without an account, added by hand. They have a name and, if you entered it, a phone, but no email. A listing's agenda does not name them (`visitor: null`).

Housera keeps these details no longer than in the portal. The name and phone of a client added by hand are erased 90 days after the viewing, and `visitor` is then `null`. The note from “How did it go?” is erased after 24 months.

## Free hours

* `source`: `schedule` for the hours generated by the [weekly schedule](https://api.housera.ro/en/guides/viewing-schedule.md), `manual` for those put in by hand.
* `held`: the hour is kept for a short while for someone signing in to book it.
* `bookable`: whether a client can book the hour now. It is `false` when the hour is too close (the notice), when whoever runs the viewings is busy at the same time at another listing (with the break between viewings), or when the hour is kept. On hours that are not free it is `null`.

## Developers' projects

On a project with several consultants, each hour has one row per consultant, with the same `starts_at` and a different `seat`. The rows are not duplicates: they are the hour's places. Once booked, `agent` is the consultant who meets the client, and `place` says where: `office` (the sales office), `show_flat` (the show flat) or `unit` (a finished flat, with `place_rooms` rooms). Elsewhere, `agent` is whoever runs the listing's viewings. `agent.email` goes to the keys of the holder, the admins and the manager who has that person on their team, and to the person's own key; any other key gets `null`, as on the Team page in the portal.

## A listing's agenda

`GET /account/listings/{code}/viewings` gets the same new fields on each hour (`source`, `duration_min`, `held`, `bookable`, `booked_at`, `proposed_starts_at`, `attendance_confirmed_at`, `reminded_day_at`, `reminded_soon_at`, `outcome`, `outcome_at`, `agent`, `place`, `place_rooms`, `seat`), plus `timezone` and `schedule_mode` on the listing.

## The client who called

`POST /listings/{code}/viewings/clients` puts in the agenda a client without an account with whom you agreed a time on the phone, as „Adaugă în agendă” does in the portal. It needs `listings:write`, `leads:read` and `leads:write`, since it adds a contact.

```json theme={null}
{ "starts_at": "2026-11-14T11:00", "duration_min": 30, "name": "Ion Marinescu", "phone": "+40740123456" }
```

* `starts_at` is read as for published times: without a time zone, on the clock of the listing's place. `duration_min` is 15, 30, 45, 60 or 90. `name` has at most 120 characters, and `phone` is optional.
* The time is booked at once, under the same overlap rules as a time published on the listing. The free times under it go.
* The client lands in the contacts with the source `other`. With a phone, the contact moves to the Viewing stage.
* The answer is `201`, with the time (`visitor` with `account: false`).
* With the `Idempotency-Key` header, the same request again within 10 minutes adds nothing and returns the time added the first time, as it is now, with `200`. The same key with another body is refused on `/idempotency_key`. Without a key, a second request for the same time gets `409 listing_action_refused`, since the time is no longer free.
* A time that overlaps another viewing or an open house, one in the past or one that does not exist at a clock change is refused on `/starts_at`.

The viewing of such a client is moved directly, with `PATCH /viewings/{id}`.

## Another hour for a client with an account

`POST /viewings/{id}/proposal` with `{"starts_at": "2026-11-15T10:00"}` proposes another time to the client. The viewing keeps its time until they answer, and the answer now has `proposed_starts_at`. The client gets the message as from the portal. When they accept, `starts_at` becomes the proposed time and `proposed_starts_at` is `null` again.

An open house, a viewing without a client with an account and one that is over get `409 listing_action_refused`. A time that overlaps another, or at which whoever runs the viewings is not free (with the break between viewings, at all their listings), is refused on `/starts_at`.

## Cancelling with a note

`POST /viewings/{id}/cancel` optionally takes `note`, at most 300 characters, which goes into the message to the client with an account (a client added by hand gets no messages from Housera). `note` is optional.

## “How did it go?”

`PUT /viewings/{id}/outcome` says what came of a viewing that took place:

```json theme={null}
{ "outcome": "offer", "note": "Revine joi cu soția." }
```

* `outcome` is `offer` (wants to make an offer), `again` (comes back), `not_interested` or `no_show`. `note` has at most 500 characters.
* The answer can be changed at any time, and a new request without `note` clears the note.
* On `offer`, the client's contact on the listing moves to the Offer stage, if it had not gone that far. The answer has `meta.lead_moved_to_offer`.
* It needs `listings:write` and `leads:write`, since it moves a contact between stages.
* A viewing that has not taken place yet gets `409 listing_action_refused`.

## The line of viewings in “My listings”

`GET /account/listings?include=viewings` adds the `viewings` field to each listing:

```json theme={null}
{ "next_at": "2026-11-16T10:00:00+00:00", "next_kind": "viewing", "next_pending": false, "more": 3, "bookable": true, "on_request": false }
```

`next_at` and `next_kind` are the next appointment (a viewing or an open house), `more` how many follow it, `bookable` whether it has free hours to book, `on_request` whether it is “On request”. Without `include`, the answer doesn't have these fields, and the listing's `etag` does not change.

`GET /account/listings?viewings=none` returns only the active listings with no schedule: no free hours, nothing scheduled, and not “On request”.

## What the CRM learns without asking

From the day we announce to you, every change to a viewing also goes out as an event, the same from the portal and from the API. That way you also learn what a colleague did in the portal, without reading the agenda every minute.

| What happened | The event |
| - | - |
| A client booked or requested a time | `viewing.booked` |
| You confirmed or declined a request | `viewing.confirmed`, `viewing.declined` |
| You proposed another time to the client, and they answered | `viewing.proposal_sent`, `viewing.proposal_answered` |
| The viewing moved to another time | `viewing.rescheduled` |
| The client cancelled or the request expired | `viewing.cancelled` |
| You or a colleague cancelled the viewing or withdrew its time | `viewing.cancelled_by_lister` |
| The client answered “Yes, I'm coming” | `viewing.attendance_confirmed` |
| The viewing is over | `viewing.completed` |
| “How did it go?” was answered | `viewing.outcome_recorded` |

The events carry `slot_id` and the time, with no client details. With `slot_id` you read the viewing through `GET /viewings/{id}`, and only a key with `leads:read` sees the client's name and phone.

When a client with an account moves their own viewing to another free time, you get `viewing.cancelled` and then `viewing.booked`, with a new id.

When the listing comes off the market (you pause it, mark it sold or withdraw it, or it is rejected), its upcoming viewings are released without an event for each one. You learn it from the listing's event, for example `listing.paused` or `listing.sold`, and clients with an account hear it from Housera.

`viewing.completed` can arrive up to an hour late. The state in `GET /account/viewings` is `completed` as soon as the time is over: if you need it right away, read it.

The full list, with each event's fields and who receives it, is in [Webhooks](https://api.housera.ro/en/webhooks.md#viewing-events).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.