Developer Documentation
Platform Overview
Authentication
API Services
Overview Accounts Accounts: Associations Accounts: Metadata Accounts: Profile Appstore: Users Broker Distributions Broker Tours Consumers Consumers: Linked Agents Contacts Contacts: Activity Contacts: Export Contacts: Portal Accounts Contacts: Tags Developers: Authorizations Developers: Billing Summary Developers: Change History Developers: Domains Developers: Identities Developers: Keys Developers: Roles Developers: Syndications Developers: Templates Developers: Usage Detail Developers: Usage Summary Devices Flexmls: Email Links Flexmls: Listing Meta Field List Translations Flexmls: Listing Meta Origins Flexmls: Listing Meta Translations Flexmls: Listing Reports Flexmls: Mapping Layers Flexmls: Mapping Shapegen IDX IDX Links Incomplete Listings Incomplete Listings: Documents Incomplete Listings: Documents Metadata Incomplete Listings: Document Uploads Incomplete Listings: Floor Plans Incomplete Listings: Floor Plans Metadata Incomplete Listings: Floor Plan Uploads Incomplete Listings: Photos Incomplete Listings: Photos Metadata Incomplete Listings: Photo Uploads Incomplete Listings: Required Documents Incomplete Listings: Rooms Incomplete Listings: Tickets Incomplete Listings: Units Incomplete Listings: Videos Incomplete Listings: Videos Metadata Incomplete Listings: Virtual Tours Incomplete Listings: Virtual Tours Metadata Listing Carts Listing Carts: Portal/VOW Carts Listings Listings: Clusters Listings: Documents Listings: Documents Metadata Listings: Document Uploads Listings: Floor Plans Listings: Floor Plans Metadata Listings: Floor Plan Uploads Listings: Historical Listings: History Listings: Hot Sheet Parameters Listings: Notes Listings: Open Houses Listings: Photos Listings: Photos Metadata Listings: Photo Uploads Listings: Rental Calendar Listings: Required Documents Listings: Rooms Listings: Rules Listings: Search Parameters Listings: Tickets Listings: Tour of Homes Listings: Units Listings: Validation Listings: Videos Listings: Videos Metadata Listings: Virtual Tours Listings: Virtual Tours Metadata Listing Meta: Custom Field Groups Listing Meta: Custom Fields Listing Meta: Field Order Listing Meta: Field Relations Listing Meta: Property Types Listing Meta: Rooms Listing Meta: Standard Fields Listing Meta: Units Market Statistics News Feed: Groups Notifications Open Houses Overlays Overlays: Geometries Portals Portals: Listing Categories Portals: Metadata Preferences Registered Listings Saved Searches Saved Searches: Provided Saved Searches: Restrictions Saved Searches: Tags Search Templates: Quick Searches Search Templates: Sorts Search Templates: Views Shared Links System Info System Info: Languages System Info: Search Templates
Webhooks
Supporting Documentation
Examples
RESO Web API
RETS
Terms of Use

RESO Web API Add/Edit

Version 3 Add/Edit lets authorized applications create incomplete Properties, update and publish listings, and manage related resources. Use /Version/3/Reso/OData/ on your assigned API host. These examples use relative request paths; replace keys and example values with values valid for your MLS.

  1. Access and supported operations
  2. Discover fields and validate input
  3. Create a Property draft
  4. Update, publish, and delete a Property
  5. Rooms and Units
  6. Open Houses
  7. Responses and concurrency
  8. Errors and warnings
 

Access and supported operations

Use the existing authentication flow and an access token authorized by a user who can edit the target listing. Request Add/Edit access through your API access provider.

Write operations require a private client with the appropriate client keyrole listed below, or a private_full client, which does not require those operation-specific keyroles. These keyroles are provisioned on the API client, not supplied through the OAuth authorization request's scope parameter. MLS, listing, and resource-specific permissions still apply to both client types.

Resource / endpoint suffixOperationsRequired client keyrole
PropertyPOST collection; PATCH entitylisting_creation for POST; listing_update for PATCH
Property('ListingKey')DELETE incomplete Property onlyincomplete_listings
Property('ListingKey')/Room, /Unit, /OpenHouse, /MediaPOST collection; PATCH or DELETE entitylisting_update

All endpoint suffixes above follow /Version/3/Reso/OData/. Entity URLs append a quoted key, for example Property('ListingKey')/Room('RoomKey'). PUT is not supported. Related-resource writes use these nested URLs, not top-level resource collections. Binary Media uploads have a separate workflow described in Media Add/Edit.

Send RESO-Dictionary-Version: 2.0 on these requests and Content-Type: application/json on POST and PATCH. Other dictionary versions are rejected for Add/Edit. OData requests accept OData-Version: 4.0 or 4.01; omitted request versions default to 4.0. Embedded Room or Unit collections on Property PATCH require 4.01. Response headers report 4.0 even for those requests.

 

Discover fields and validate input

Retrieve $metadata with Dictionary 2.0 for field definitions, types, and lookup relationships. Use the Lookup resource for available lookup values. Metadata includes read-only fields and does not provide a complete contract for writable fields or fields required for publication. Field availability, required values, and validation rules depend on the MLS and property type. Do not copy an entire GET response into a write request.

The Version 3 Rules resource provides additional field constraints, including required-field, read-only, and conditional rules. It requires full-access or FBS privileges and is unavailable to ordinary private clients with operation-specific keyroles. Rules is optional for Add/Edit integrations; clients with access can use it to inform field handling. Use the MLS class identifier for ClassName, rather than assuming the RESO PropertyType label is the class identifier:

GET /Version/3/Reso/OData/Rules?ClassName=A
Authorization: Bearer ACCESS_TOKEN
RESO-Dictionary-Version: 2.0

Create requires a supported PropertyType. Create and update requests validate submitted data, but successfully saving an incomplete draft does not guarantee publication readiness. Publication applies additional requirements. Handle field-specific errors and warnings from write responses; server-side validation remains authoritative.

 

Create a Property draft

POST creates an incomplete Property and assigns its ListingKey. It does not publish a listing. Do not request ListingInputStatus: Published on POST; publishing is a subsequent PATCH.

POST /Version/3/Reso/OData/Property
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0
Prefer: return=representation

{"PropertyType":"Residential","ListAgentKey":"20140227182254339191000000"}

Required fields vary by MLS and user permissions. The PropertyType and ListAgentKey above are illustrative; use a supported PropertyType and the key of a listing agent permitted for your user. This example is a draft starting point and may require additional fields.

A successful representation response is HTTP 201 with the Property, including its ListingKey, @odata.editLink, and concurrency token when available. Save the returned key and edit URL for later requests. The following response excerpt shows the identifiers; API_HOST and the key are illustrative:

HTTP/1.1 201 Created
Location: https://API_HOST/Version/3/Reso/OData/Property('20100000000000000000000000')
Preference-Applied: return=representation
OData-Version: 4.0

{
  "ListingKey":"20100000000000000000000000",
  "@odata.id":"https://API_HOST/Version/3/Reso/OData/Property('20100000000000000000000000')",
  "@odata.editLink":"https://API_HOST/Version/3/Reso/OData/Property('20100000000000000000000000')"
}

Retrieve the draft using the returned ListingKey on the same API host with an access token authorized to edit it:

GET /Version/3/Reso/OData/Property('20100000000000000000000000')
Authorization: Bearer ACCESS_TOKEN
RESO-Dictionary-Version: 2.0
 

Update, publish, and delete a Property

PATCH explicitly updates the supplied scalar fields on either an incomplete or published Property. Omitted scalar fields are not explicitly updated, but server-side rules and derived values can change additional fields. A draft save can omit ListingInputStatus or set it to Incomplete.

PATCH /Version/3/Reso/OData/Property('ListingKey')
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0
Prefer: return=representation

{"PublicRemarks":"Updated remarks"}

To publish, complete the required fields and PATCH the draft with ListingInputStatus: Published. Publishing validates the complete listing. The ListingKey is retained, but Room and Unit keys are rebuilt; retrieve them again after publishing. A published Property cannot be returned to Incomplete.

PATCH /Version/3/Reso/OData/Property('ListingKey')
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0

{"ListingInputStatus":"Published"}

Delete an incomplete Property with DELETE /Version/3/Reso/OData/Property('ListingKey'), the access token, and Dictionary 2.0 header. Success is HTTP 204 with no body. Deleting a published Property returns HTTP 405.

 

Room and Unit support individual POST, PATCH, and DELETE operations on incomplete and published Properties. Room editing requires permission to edit rooms and a listing that supports rooms. Unit editing requires permission to edit units and an MLS/listing that supports units. Field mappings and lookups depend on the MLS and property type.

For example, create a Room with the common authentication, JSON, and Dictionary 2.0 headers:

POST /Version/3/Reso/OData/Property('ListingKey')/Room
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0

{"RoomType":"Kitchen","RoomLength":12,"RoomWidth":10}

Use the returned RoomKey for PATCH or DELETE on Property('ListingKey')/Room('RoomKey'). Units follow the same pattern with Unit and UnitTypeKey; an example Unit payload is {"unit_nbr":"B2","UnitTypeActualRent":1100.0}. Required fields differ between Rooms and Units and vary by MLS and property type. These sample values require corresponding MLS fields and lookups.

Individual Room and Unit PATCH operations merge submitted fields with existing member values before validation. Embedded Property PATCH instead replaces the submitted collection state before validation, so an existing member may need required fields that an individual member PATCH could omit. Room and Unit required-field rules differ and depend on the MLS and property type.

You can also embed Room and Unit arrays in Property POST (deep insert) or PATCH (deep update). POST assigns all child keys and ignores supplied child keys. Before submitting a replacement, retrieve GET Property('ListingKey')/Room or GET Property('ListingKey')/Unit under /Version/3/Reso/OData/. Include every existing member you intend to preserve, using its current RoomKey or UnitTypeKey. On PATCH, each included collection replaces its membership:

PATCH /Version/3/Reso/OData/Property('ListingKey')
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0
OData-Version: 4.01

{
  "Room": [
    {"RoomKey":"ExistingRoomKey","RoomType":"Kitchen","RoomLength":14},
    {"RoomType":"Kitchen","RoomLength":12,"RoomWidth":10}
  ],
  "Unit": []
}

This example keeps and updates the identified Room, creates another Room, deletes all other Rooms, and clears every Unit. Property and embedded collection changes are atomic: validation or persistence failure rolls them back together. OData delta/removal annotations are not supported for these collection updates.

 

Open Houses

OpenHouse writes apply to published Properties only. Use POST on Property('ListingKey')/OpenHouse, then PATCH or DELETE on Property('ListingKey')/OpenHouse('OpenHouseKey'). Open House access is required; Broker and Livestream Broker are tour-of-homes events and also require the applicable MLS tour-of-homes permissions.

A successful creation response with a representation includes OpenHouseKey and ListingKey. Retain the returned OpenHouseKey or edit link for subsequent PATCH and DELETE requests. Unfiltered OpenHouse collection reads return only in-person Public events; use an appropriate OpenHouseType filter to retrieve Broker or livestream events.

For an in-person event, choose a future date and valid start/end timestamps for the event:

POST /Version/3/Reso/OData/Property('ListingKey')/OpenHouse
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
RESO-Dictionary-Version: 2.0

{
  "OpenHouseType":"Public",
  "OpenHouseDate":"07/01/2027",
  "OpenHouseStartTime":"2027-07-01T14:00:00+00:00",
  "OpenHouseEndTime":"2027-07-01T16:00:00+00:00",
  "OpenHouseRemarks":"Standard in-person open house"
}

Supported types are Public (the create default), Broker, Livestream Public, and Livestream Broker, subject to MLS settings. Livestream creation also requires LivestreamOpenHouseURL and OpenHouseRemarks. The livestream URL must use a service supported by the MLS; an otherwise valid URL may be rejected if its provider is unsupported. PATCH cannot switch between Public and Broker or between in-person and livestream; delete and recreate the event to change its type. Legacy broker-tour records are a separate event category and are not supported for Add/Edit; this restriction does not exclude the supported Broker and Livestream Broker tour-of-homes events.

 

Responses and concurrency

Request preferenceCreateUpdateBody
No Prefer header201200Entity representation
Prefer: return=representation201200Entity representation
Prefer: return=minimal204204Empty

Successful writes include Location and OData-Version: 4.0. For POST and PATCH, explicit return preferences add Preference-Applied; minimal responses also include EntityId containing the quoted entity key. Successful DELETE requests always return HTTP 204 without a body and ignore Prefer return preferences; they do not return Preference-Applied or EntityId. Representation responses include @odata.editLink and a weak @odata.etag when a concurrency timestamp is available.

ListingKey, EntityId, Location, and @odata.editLink identify a resource. ETags identify a resource version for concurrency checks; resource identifiers cannot be used in place of an ETag.

For example, a successful create with Prefer: return=minimal returns the following headers and no body (host and key are placeholders):

HTTP/1.1 204 No Content
Preference-Applied: return=minimal
Location: https://API_HOST/Version/3/Reso/OData/Property('ListingKey')
EntityId: "ListingKey"
OData-Version: 4.0

The following concurrency behavior applies specifically to Property. An individual Property GET supplies an HTTP ETag header. Copy that header unchanged into If-Match for a conditional Property PATCH or DELETE. When response metadata permits, @odata.etag contains the same token. With odata.metadata=none, the body annotation is omitted but the HTTP header remains. $select, including keys-only requests, does not remove the Property entity ETag. These guarantees do not extend to Rooms, Units, Open Houses, or Media.

Successful Property POST/PATCH representation responses provide an updated ETag when a usable concurrency timestamp is available. Prefer: return=minimal responses and successful DELETE responses do not provide concurrency tokens. A successful write is not guaranteed to include an ETag.

A nonmatching If-Match returns HTTP 412 with the gateway OData error envelope and does not apply the mutation. Omitting If-Match skips version checking; POST does not require it. If-Match: * bypasses version comparison for an existing target, but does not bypass authorization or operation restrictions.

After a 412, retrieve the Property and reconcile changes before retrying. A published Property GET may temporarily return the same stale ETag because of cached listing data; an immediate GET does not guarantee a fresh token. Avoid automatic unbounded retries. A concurrent update can also invalidate a newly retrieved token.

 

Errors and warnings

Validation failures return HTTP 400 with an OData error object: code is "20100", message describes the batch, and target is Create, Update, or Delete. Each details item contains a stable code and message; field-specific details include a target when available. Embedded errors can identify Room[0].RoomType or Unit[0].UnitTypeActualRent; individual subresource errors use flat field names. Responses include Content-Language.

For example, a Room creation request missing a required RoomType can return the following validation response excerpt. Field labels and messages vary by MLS and language:

HTTP/1.1 400 Bad Request
Content-Language: en

{
  "error": {
    "code": "20100",
    "message": "Errors",
    "target": "Create",
    "details": [
      {"code": "30201", "message": "Room Type is required.", "target": "RoomType"}
    ]
  }
}

Permission failures (403), missing entities (404), unsupported operations (405, such as deleting a published Property), stale concurrency tokens (412), and unsupported content types (415) use the gateway OData error form rather than the validation details batch. Request-level HTTP 400 errors can also use this form. Gateway errors contain a numeric HTTP status in error.code (for example, 405) and a message, without validation details; validation batches instead use the string code "20100". Handle the HTTP status as well as the error body.

Successful representation responses may include @FBS.Warnings for ignored or read-only input fields. Inspect these notices rather than assuming every submitted field was saved. Prefer: return=minimal suppresses the response body and therefore hides the notices; request Prefer: return=representation when clients need to inspect warnings.

For example, a successful Room creation response can include this annotation when an unrecognized FooBar field was ignored (excerpt):

{
  "@FBS.Warnings": [
    {
      "code": "field_ignored",
      "message": "Field 'FooBar' is not recognized and was ignored.",
      "target": "FooBar"
    }
  ]
}