Docs
Sites API

Bootstrap, routing & content

The site manifest, every renderable path, resolving a URL, content types and entries, and group sets.

These are the endpoints a build reads first. Between them they answer: what is this site, which URLs does it have, and what serves each one?

The site manifest

GET /site — everything site-wide in one call. Tags: site, plus property:{id} and inventory:{id} per property.

KeyHolds
websiteid, name, type (property or portfolio), platform, status, locale, environment (production or preview)
domains[]hostname, is_primary, managed_by
trackingAccount-level tracking ids, for example gtm_id. A free-form map; {} when none
redirects[]from, to, status
routingThe URL patterns; see below
properties[]A card per enabled property, in the website's order — the same card GET /properties returns. At most 500; meta.properties gives { total, listed }, and a larger portfolio reads the rest from GET /properties
theme.tokensDesign tokens, or null
searchThe site's search: app_id, search_key (search-only, safe to publish) and indices (properties, units, each a name or null). By default this is Resi-managed search, whose key is limited to this site's indices and domains. A website set to search its account's own Algolia connection gets that connection's application, search-only key and indices instead. null until the website's search is provisioned or its connection can be searched, or where it has none

URL patterns

"routing": {
  "patterns": {
    "property": "/property/{slug}",
    "property_archive": "/properties",
    "floor_plan": "/floor-plan/{slug}",
    "floor_plan_archive": "/floor-plans",
    "unit": "/unit/{slug}",
    "unit_archive": "/units"
  },
  "uniform": true
}

The segments come from the account's website settings, with the defaults above. uniform is false when any property overrides a segment; when it is, do not build URLs from the patterns — use each record's own path, which is always right. On a property website the property's path is /. patterns is null for a website with no properties.

Every property, floor plan, unit and entry carries a ready-made path. Prefer it over assembling URLs yourself.

Every renderable path

GET /paths — for generateStaticParams and the sitemap. Tag: paths.

Parameter
typeOne or more of property, floor_plan, unit, entry, comma-separated. Unknown is a 422
page, per_pageDefault 500, ceiling 2000
{
  "data": [
    { "path": "/property/contract-property", "type": "property", "slug": "contract-property", "property_slug": "contract-property", "content_type": null, "updated_at": "2026-01-15T12:00:00+00:00" },
    { "path": "/team/ada", "type": "entry", "slug": "ada", "property_slug": null, "content_type": "team", "updated_at": "2026-01-15T12:00:00+00:00" }
  ],
  "meta": { "pagination": { "total": 4, "page": 1, "per_page": 500, "last_page": 1 }, "cache": { "tags": ["paths"], "ttl": 3600 } }
}

Paths come in a fixed order — properties, floor plans, units, entries — so paging is stable. Grouped floor plans appear once, under their primary. updated_at is the sitemap's lastmod.

Resolving a path

GET /resolve?path=/team/ada — what serves a URL the build did not generate. Checked in this order: content entry, property, floor plan, unit, redirect. A path something occupies always wins over a redirect.

data.type tells you which branch you got:

typeAlongside it
entryentry — the full entry
propertyproperty: { id, slug, name } — fetch it from GET /properties/{slug}
floor_planproperty and floor_plan references — fetch from GET /floor-plans/{slug}
unitproperty and unit references (name is the unit number) — fetch from GET /units/{slug}
redirectredirect: { "to": "/new", "status": 301 }
{ "data": { "type": "redirect", "path": "/old", "redirect": { "to": "/new", "status": 301 } }, "meta": { "cache": { "tags": ["site"], "ttl": 3600 } } }

Nothing there is a 404 you can branch on:

{ "message": "Nothing serves this path.", "type": "not_found", "path": "/nothing-here" }

Content types

GET /content-types — the content types this website renders, with the fields that shape each one's entries. Tag: site. Ordered by plural name; not paginated.

A content type is a shape of content — team member, blog post, neighbourhood guide — defined by Resi staff. An entry is one record of that shape. A type is listed when it belongs to the website's account, is enabled, and is either account-wide, authored for this website, or a library type this website has opted into.

{
  "key": "team",
  "singular": "Team member",
  "plural": "Team members",
  "scope": "account",
  "url_pattern": "/team/{slug}",
  "template": "default",
  "schema_type": "Person",
  "expiry_field": null,
  "fields": [
    { "key": "name", "label": "Name", "type": "text", "required": true, "help": null, "options": null, "fields": null, "conditions": null },
    { "key": "role", "label": "Role", "type": "select", "required": false, "help": null, "options": { "agent": "Leasing agent", "manager": "Manager" }, "fields": null, "conditions": null },
    { "key": "links", "label": "Links", "type": "repeater", "required": false, "help": null, "options": null, "conditions": null,
      "fields": [{ "key": "url", "label": "URL", "type": "url", "required": true, "help": null, "options": null, "fields": null, "conditions": null }] }
  ]
}
Key
keyWhat /entries/{type} accepts
scopeaccount (every website in the account), site (this website only) or library (shared, opt-in per website)
url_patternThe pattern this website uses. A library type lets each site choose its own — /blog/{slug} on one, /journal/{slug} on another. null when entries have no page of their own and are only embedded
templateThe template this website renders the type with; default unless it chose another
schema_typeThe schema.org type behind each entry's schema JSON-LD
fields[]What an entry's data holds, keyed by fields[].key

Field type is one of text, textarea, rich_text, number, boolean, date, datetime, select, multi_select, email, url, phone, image, images, file, video, property, property_group, entry_reference, repeater, group, colour, markdown, json. Render the ones you know and ignore the rest; new types may be added.

  • options — for select and multi_select: stored value → label. An entry's data holds the stored value.
  • fields — for repeater (a list of items) and group (one nested object): the nested definitions.
  • conditions — the field applies only when siblings at the same level match: { "role": "manager" }, or { "role": ["manager", "agent"] } for any of several.

Use it to generate types for data, or to render a type generically without knowing it in advance.

Content entries

{type} is the content type's key (team), never an id.

GET /entries/{type} — tag entries:{type}.

Parameter
termTerm slugs or ids, comma-separated; an entry matching any is returned
propertyA property slug; entries written for that property
page, per_pageDefault 50, ceiling 200

meta.content_type describes the type: key, singular, plural, url_pattern, schema_type.

GET /entries/{type}/{slug} returns { "type": "entry", "path", "entry" } — or { "type": "redirect", … } when the slug was renamed, so old links keep working.

{
  "id": "01a0b9f5-b5e5-73b5-b172-06d40e30f8b6",
  "slug": "ada",
  "path": "/team/ada",
  "status": "published",
  "content_type": "team",
  "property_id": null,
  "data": { "name": "Ada Lovelace", "bio": "Community manager." },
  "seo": {},
  "terms": [],
  "schema": { "@context": "https://schema.org", "@type": "Person", "name": "Ada Lovelace", "url": "https://www.example-apartments.com/team/ada" },
  "published_at": null,
  "expires_at": null,
  "updated_at": "2026-01-15T12:00:00+00:00"
}
  • data is the entry's fields, shaped by the content type's fields (see above); media and group references are expanded. seo is its overrides. Both are maps, {} when empty.
  • schema is ready-to-print JSON-LD when the content type names a schema_type.
  • A deliver token sees only published, unexpired entries. A preview token also sees drafts and in-review entries; status tells them apart.

Group sets

GET /groups — tag site. Account-wide group sets, plus those belonging to the website's own properties (property_slug says which; null means account-wide).

{
  "id": "01000000-0000-4000-8000-000000000017",
  "key": "towers",
  "name": "Towers",
  "object_type": "building",
  "allows_multiple_membership": true,
  "property_slug": null,
  "groups": [{ "id": "0100…0018", "parent_id": null, "label": "North Tower", "slug": "north-tower", "description": null, "color": null, "sort_order": 1 }]
}

A group's id is what the group filter accepts on /properties, /floor-plans and /units. Filtering by a parent includes its descendants.

Last updated on

On this page