> ## 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.

# Editing

> JSON Merge Patch, versions with ETag and If-Match, and updating by your id.

You edit a listing with `PATCH /listings/{code}` or, if you work with your ids, with `PUT /listings/ref/{ref}`. Both send only what has changed and both protect you from overwriting a change made in the meantime in the portal.

## JSON Merge Patch

The body of a `PATCH` follows the JSON Merge Patch rules (RFC 7386):

* **a sent field** replaces its value;
* **a sent field with `null`** is emptied;
* **a not sent field** remains as it was.

```bash theme={null}
curl -X PATCH https://housera.ro/api/v1/listings/HEWPUTA8 \
  -H "Authorization: Bearer $HOUSERA_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "9b2f6c1e4a7d3b8f0c5e2a1d6f4b7c9e"' \
  -d '{ "price": 94500, "negotiable": false, "year_built": null }'
```

Here the price becomes 94,500 EUR, the listing is no longer negotiable, the year of construction is emptied, while the rest remains untouched.

The lists (`features`, `heating_types`, `orientations`) are completely replaced. To add an amenity, you send the complete list, including the new amenity.

After combining, the resulting listing goes through the same rules as an edit from the form. A `PATCH` cannot empty a mandatory field: `{"street": null}` receives `422` with `required` on `/street`.

<Warning>
  The type (`type`) and transaction (`transaction`) of a listing do not change after creation. An apartment for sale listed for rent is a new listing.
</Warning>

## What response do you receive

| Listing status | What happens | Status |
| - | - | - |
| `pending` | The change is applied immediately, the listing remains under moderation. | `200` |
| `rejected` or `draft` | The change is applied and the listing is (re)submitted for moderation: it becomes `pending`. | `200` |
| `active`, `paused`, `unconfirmed` | The public version remains, the change is awaiting moderation (`moderation.edit_pending: true`). | `202` |
| `sold`, `hidden` | The listing is no longer edited. | `404` |

The body of the response is the listing, plus `warnings[]` (see below). At `202`, the listing in the response still has the public values: your change is visible after approval.

## Versions: `ETag` and `If-Match`

A listing submitted via API remains editable from the portal: the agent can correct a price directly on housera.ro. To ensure that an automatic synchronization does not silently delete such a correction, each listing has a version.

1. When you read the listing, you receive the version in the `ETag` header and in the `etag` field.
2. When you edit, you send it back in the `If-Match` header.
3. If the listing has changed in the meantime (an edit in the portal, another request), you receive `412 Precondition Failed` and nothing is written.

```json theme={null}
{
  "type": "https://housera.ro/problems/listing_changed_meanwhile",
  "title": "Anunțul a fost modificat între timp, din altă parte. Redeschide-l ca să vezi versiunea curentă — altfel ai suprascrie modificarea celuilalt.",
  "status": 412,
  "code": "listing_changed_meanwhile",
  "detail": "Anunțul a fost modificat între timp, din altă parte. Redeschide-l ca să vezi versiunea curentă — altfel ai suprascrie modificarea celuilalt."
}
```

At `412`, you read the listing again (`GET /account/listings/{code}`), decide which version wins, and resubmit with the new `ETag`.

The version changes when the content of the listing changes, including an edit that is awaiting moderation. When someone edits the listing in the portal, you also receive the webhook `listing.edited_in_portal`, with the new `etag`.

<Tip>
  `If-Match` is optional. Without it, the edit applies over any version. We recommend it for any automatic synchronization: it’s the difference between "I updated the price" and "I deleted the agent’s correction".
</Tip>

## `PUT /listings/ref/{ref}` applies just like a patch

On an id that Housera already knows, `PUT /listings/ref/{ref}` does not replace the whole listing, but behaves like a `PATCH`:

* the fields you send get updated;
* fields **that you do not send retain their value**, they are never emptied;
* an explicitly sent field with `null` is emptied;
* `If-Match` is respected in the same way, with `412` on an old version.

This way, a CRM that does not recognize a field (for example, one added later in the API) does not accidentally empty it at every synchronization.

## Identical body: `X-Housera-Unchanged`

If you send to `PUT /listings/ref/{ref}` exactly the same body as the last successful write, Housera does not write anything and does not submit anything for moderation. You receive `200` with the current listing and header:

```http theme={null}
X-Housera-Unchanged: true
```

You can therefore resynchronize your entire stock without worry: only listings that have actually changed reach moderation. The comparison is made on the body bytes, so keep the same order of fields and the same number format from one synchronization to another.

## Warnings: `warnings[]`

Any successful response to a write (`POST`, `PUT`, `PATCH`) contains the list `warnings[]`, with what you sent and was not taken:

```json theme={null}
{
  "data": { "code": "HEWPUTA8", "status": "pending" },
  "warnings": [
    { "pointer": "/deposit_months", "code": "not_for_transaction" },
    { "pointer": "/features/loading_dock", "code": "not_for_type" }
  ]
}
```

| Code | What it means |
| - | - |
| `not_for_type` | The field or amenity does not exist for the listing type (e.g., a loading ramp for an apartment). |
| `not_for_transaction` | The field does not exist for the transaction (e.g., warranty in months for a sale). |
| `unknown_feature` | The amenity code is not in the catalog. |

A warning does not block the write, but tells you that your mapping is sending something unnecessary. The list of fields and amenities allowed for each type and transaction is in [vocabulary](/en/vocabulary).

## Concurrency conflicts

If two writes arrive at the same time on the same listing, the second may receive `409 listing_busy`. Nothing was written: you retry after a second or two.


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