Docs
API guides

Core concepts & data model

How accounts, properties, buildings, floor plans, and units relate — and where pricing, media, and content attach.

Understanding five relationships will save you most of the guesswork.

The hierarchy

Account  ─ a client: a property management company or owner
  └── Property  ─ a physical community; the unit of marketing
        ├── Building  ─ optional physical grouping within a property
        ├── Floor Plan  ─ a unit layout: "A1 — 1 bed / 1 bath / 777 sqft"
        └── Unit  ─ a leasable apartment; belongs to a property,
                    optionally to a building, optionally to a floor plan
  • Account is the tenancy boundary. Your token is pinned to one, and each request runs against one (see Authentication).
  • Property is what a renter thinks of as "the apartment community." Nearly every read you do will be filtered by property_id.
  • Building is optional. Single-building communities often have none; a unit's building_id can be null.
  • Floor Plan is the marketable layout. A unit's floor_plan_id can be null if the PMS never mapped it — do not assume it is present.
  • Unit is the leasable thing. It is the record that changes most often, because availability and pricing live here.

Inventory is not nested in the URL structure: you fetch /units?property_id=…, not /properties/{id}/units. Every collection is flat and filtered.

Ids, slugs, and your own keys

FieldWhat it isUse it for
idResi's UUID. Stable forever.Your foreign key to Resi
slugURL-safe handle, e.g. pawnee-placePublic URLs. Can change — never key on it
reference_idThe id this record has in the source system (usually the PMS)Matching Resi records to PMS records
external_idsUnits only: ids in other systems, keyed by provider, e.g. {"sight-map": "258702"}Matching units to map, tour and listing tools

reference_id is the field that makes reconciliation possible. A unit synced from a PMS carries that PMS's unit id — "23820098" in the example below. If you are writing records into Resi from your own system, set reference_id to your id so you can find your own records later.

reference_id is not enforced unique. It is a filter on amenities and media files, but not on units, floor plans, buildings or properties. Keep your own id↔id map rather than relying on lookup by reference_id.

What a unit looks like

This is a unit from GET /api/v2/units:

{
  "id": "019dd605-61bc-71bf-acf9-1f1dd97ac1ca",
  "property_id": "019dd604-8468-7388-9a52-4c31a2e1209a",
  "building_id": "019eb254-e966-71d2-8b14-2b6fdc8a9fc4",
  "floor_plan_id": "019dd605-5bf2-739a-8725-9425f8e631e1",
  "is_enabled": true,
  "number": "149-304",
  "slug": "149-304-fr57yk",
  "floor": null,
  "bedrooms": 1,
  "bathrooms": 1,
  "interior_sqft": 777,
  "exterior_sqft": null,
  "description": null,
  "application_link": "https://…/oleapplication.aspx?…",
  "tour_link": null,
  "quote_link": null,
  "specials": null,
  "is_handicap_accessible": false,
  "is_furnished": false,
  "is_affordable": false,
  "is_model": false,
  "is_guest_suite": false,
  "is_penthouse": false,
  "is_available": true,
  "is_featured": false,
  "is_hidden": false,
  "available_at": "2024-11-12T00:00:00.000000Z",
  "made_ready_at": null,
  "vacate_at": null,
  "deposit": 0,
  "min_rent": 1510,
  "max_rent": null,
  "min_base_rent": null,
  "max_base_rent": null,
  "reference_id": "23820098",
  "external_ids": {},
  "tags": [],
  "start_date": null,
  "images": [],
  "pdfs": [],
  "videos": [],
  "virtual_tours": [],
  "created_at": "2026-04-28T21:36:10.000000Z",
  "updated_at": "2026-06-10T16:20:34.000000Z"
}

GET /api/v2/units/{unit} returns the same fields plus the nested property, building, floor_plan and amenities.

number on a read is the unit number as the property displays it. When the property is set to prefix unit numbers with the building number, that is the composed form — 149-304 above. Create and update responses echo the stored number without the prefix, so send the stored number back when you update.

The three flags that decide whether a unit should be shown

This trips up nearly every new integration. A unit is publicly marketable only when all three line up:

FlagMeaning
is_enabledThe record is active in Resi at all. false = treat as if it does not exist.
is_availableIt is available to lease.
is_hiddenExplicitly suppressed from public display, even if available.
show = is_enabled && is_available && !is_hidden

Also check is_model and is_guest_suite before syndicating — those are real units that are not for rent to the public.

Dates on a unit

  • available_at — when the unit can be moved into. This is the date a renter cares about.
  • made_ready_at — when the unit was physically ready (turned).
  • vacate_at — when the current resident is scheduled to leave.
  • start_date — lease start, where the PMS supplies one.

A unit with is_available: true and a future available_at is a pre-lease. Most ILS feeds want those; most "move in today" filters do not.

Pricing

Pricing lives on the unit (and the floor plan) as a range:

  • min_rent / max_rent — the Total Monthly Leasing Price (TMLP): base rent plus the monthly equivalent of the mandatory fees that apply. Annual fees count as one twelfth, quarterly fees as one third, and one-time fees are left out.
  • min_base_rent / max_base_rent — rent before those fees.

All four are numbers, and any of them can be null. max_* is frequently null when the PMS supplies a single price rather than a range.

Whether a property shows base rent, total rent, or both is a display setting, not something you infer from the data. V2 returns the stored values whatever the setting says, so if your integration renders a price to a renter, read the property's pricing_display_settings from GET /api/v2/properties/{property} and honor it:

KeyMeaning
pricing_display_modeBASE_AND_TOTAL, TOTAL_ONLY or BASE_ONLY
hide_all_pricingShow no prices at all
hide_price_when_no_availabilityHide the price when nothing is available
hidden_price_labelThe text to show in place of a price while hide_all_pricing is on
no_availability_price_labelThe text to show in place of a price hidden because nothing is available
no_price_labelThe text to show when there is no price, such as when the PMS sends no rent
tmlp_labelThe label for the total price

The values are the ones in effect: the property's own setting where it has one, the account's otherwise. GET /api/v2/properties/{property}/settings returns the property's full resolved settings, including the fees assigned to it.

The fee-by-fee breakdown is not on the V2 unit or floor plan, and V2 has no pricing endpoint.

The breakdown is served on V1, from the same stored pricing: unitFees / unitFeeTotal on a unit, floorPlanFees / floorPlanFeeTotal on a floor plan, and fees / feeTotal / minTmlp / maxTmlp per bed-count group on GET /api/v1/property/{property}/unit-types. Lease-term pricing is at GET /api/v1/property/{property}/unit/{unit}/price-matrix. See Website & content data.

Fees and amenities are shared, not owned

Fee and Amenity records belong to the account and are linked to the records they apply to. That is why you filter them with ?property_id=, which resolves through the link, rather than reading a property_id column off the record.

  • Fees are assigned to a property, a floor plan or a unit. When the same fee is assigned at more than one level, the most specific assignment wins: unit, then floor plan, then property. Manage assignments with /api/v2/fee-assignments: each one places a fee on a property, floor plan or unit, can be narrowed to bedroom counts (unit_types) or floor plans (floor_plan_ids) and to dates, and can override the fee's amount, frequency or requirement for that scope. A change reprices the property in the background. property_ids on a fee is a shortcut for plain property-level assignments. ?property_id= on the fees collection matches property-level assignments.
  • Amenities are linked directly to properties, floor plans and units (property_ids, floor_plan_ids, unit_ids). A unit's amenities are the ones linked to it; they are not inherited from the property.

Groups: how portfolios get sliced

Group sets and groups are Resi's arbitrary taxonomy over records — region, brand, asset class, lease-up phase, whatever the client defines. Groups can nest. Groups covers creating them and managing their members.

The property, building, floor plan and unit collections accept a cascade-aware group filter:

# One group (includes descendants)
curl "https://v2.getresi.com/api/v2/units?group=$GROUP_ID"

# Any of several
curl "https://v2.getresi.com/api/v2/units?groups[]=$A&groups[]=$B"

Two things to know:

  1. An unknown or cross-account group id yields 404, not an empty list.
  2. Group assignment also silently narrows results. If the user who issued your token is assigned to specific groups within the account, what the token can see is limited to those groups automatically — on collections, on single records (which 404), and on the content, media and settings attached to them. A vendor seeing fewer properties than the client expects is usually looking at a group-scoped user — check with the client before debugging your code.

Media

Media comes in two kinds, and they behave differently:

  • Files (kind: file) — images and PDFs stored by Resi. A file is an account-level library asset attached to zero or more parents. Editing its caption changes it everywhere.
  • Embeds (kind: embed) — video and virtual-tour URLs (YouTube, Vimeo, Matterport). Not downloaded; belong to exactly one parent.

On a resource payload they surface as arrays such as images, pdfs, videos and virtual_tours; properties also carry property_images and background_image. Full detail in Media.

Content

Marketing content is modeled separately from inventory, and each type is its own V2 resource: content blocks, FAQs, galleries, announcements, reviews, and neighborhood places. Content blocks, FAQs, galleries and announcements can be global (is_global: true, shared across the account) or linked to specific properties — which is why they filter by ?property_id= rather than owning one. Reviews and neighborhood places belong to one property.

Connections

A connection is a configured integration between a Resi account and an external system — a PMS, a CRM, a tour provider. Connections are attached to properties, and they are what actually pulls pricing and availability into Resi on a schedule. If a client asks why unit data is stale, the answer is almost always at the connection layer, not the API. See Manage PMS & CRM connections.

Enums you will meet

FieldValues
Media media_type (file slot)image, background_image, video, panorama, virtual_tour, pdf, property_image, floor_plan_2d, floor_plan_3d, plus fallback_image_thumb, fallback_virtual_tour_thumb, fallback_video_thumb
Media embed_typevideo, virtual_tour, other
Media attachable_typeproperty, unit, building, floor_plan, gallery, amenity, announcement, neighborhood_place, content_block, content_item
Account membership roleadmin, manager, member, viewer, or the slug of a role the account has defined
Property pricing_display_modeBASE_AND_TOTAL, TOTAL_ONLY, BASE_ONLY

Most enum values are lowercase snake_case strings; the pricing display settings use uppercase. Treat an unrecognized value as forward compatibility, not an error — log it and skip, don't crash.

Last updated on

On this page