Portal Feeds

Public token-bound XML feeds for portal syndication, including caching, crawl evidence and eligibility semantics.

Fondaro exposes organization-specific XML feeds for property portals that pull a complete listing snapshot. Each connection has an opaque bearer token and is bound to one organization and one portal format.

Public feed route

GET /properties/feeds/:slug/:token.xml

The route is public because third-party crawlers cannot use a Fondaro session or API key. The token is the authorization secret. It is generated from 32 random bytes, encoded for a URL path, and must be stored and transmitted as an opaque value. Do not parse it, put it in query parameters, log it, or expose it in a client application.

curl -i \
  https://api.fondaro.com/properties/feeds/kyero/REDACTED_OPAQUE_TOKEN.xml

A successful response has Content-Type: application/xml; charset=utf-8.

Slugs, families and binding

Feed familyAccepted live or Beta slugs
Kyero V3kyero, thinkspain, a-place-in-the-sun, properstar, spain-property-portal, spainhouses, indomio, arkadia
Thribeetrovit, mitula, nestoria, nuroa
Green-Acresgreen-acres
Homesgofasthomesgofast

For a non-grouped portal, a token resolves only with that connection's slug. Combining a Kyero token with the green-acres slug, for example, returns 404.

Trovit, Mitula, Nestoria and Nuroa are one Thribee feed group. They share a single physical connection, token, XML snapshot, crawl evidence and publication selection. Fondaro generates the canonical URL with the trovit slug; the resolver binds each of the four group slugs to that same connection. A Thribee token does not work with any slug outside the group.

Every generated feed is absolute, not incremental. It contains the complete set of currently selected, Active and eligible listings for that organization. A listing that is removed, becomes ineligible, or leaves Active status is absent from the next response, which tells the portal to remove it on its next processing cycle.

Not-found behaviour

The route returns plain 404 Feed not found without revealing which check failed when:

  • the slug is unknown or belongs to a coming-soon portal;
  • the token does not exist or is bound to another slug or group;
  • the owning organization does not exist or is disabled; or
  • a required connection fact is missing, including the Green-Acres account reference.

Internal generation failures return plain 500 Feed unavailable. Neither response exposes server details or organization data.

Conditional requests and cache headers

Every successful build returns:

  • Cache-Control: no-store, because the path contains a bearer secret and the feed is generated from current organization data;
  • ETag, derived from the complete XML response; and
  • Last-Modified, set to the latest updatedAt among eligible listings when the feed is not empty.

Send the previous ETag in If-None-Match:

curl -i \
  -H 'If-None-Match: "PREVIOUS_ETAG"' \
  https://api.fondaro.com/properties/feeds/kyero/REDACTED_OPAQUE_TOKEN.xml

An exact strong or weak ETag match, or If-None-Match: *, returns 304 Not Modified with no XML body. Last-Modified is response metadata; this endpoint does not currently evaluate If-Modified-Since. An empty feed has no listing-derived Last-Modified value.

no-store prevents treating the response as reusable cached content. The ETag still defines how the server responds when a crawler supplies an If-None-Match validator on a later direct request.

Crawl and publication evidence

Fondaro records evidence only after the HTTP response finishes. Both a 200 XML response and a 304 validation are successful crawl evidence. The connection stores its first and latest crawl time, the latest truncated user agent and a crawl count. Repeated writes are throttled to at most once per minute per connection within each running API process. Multiple API replicas can therefore record more than one write in a minute, so crawlCount is an operational signal rather than a raw request counter.

Before recording the crawl timestamp, Fondaro marks each listing actually present in that feed as served. This drives the publication statuses:

StatusEvidence
queuedThe listing is not Active, or it has not yet been served in the selected feed.
in_feedThe listing was served, but no connection crawl at or after that first serve proves the portal fetched it. JamesEdition also stops at this state because its shared route has no per-organization connection evidence.
liveThe portal connection was crawled at or after the listing first entered the feed. This does not prove downstream acceptance or display.
not_connectedThe publication is selected but its organization connection does not exist.
ineligibleAt least one material portal rule currently fails.

Rotate a token

Organization administrators rotate a connection through the dashboard or the authenticated management route:

POST /properties/portal-connections/:portal/rotate

Rotation replaces the token and invalidates the old URL immediately. It also resets first-crawl evidence for the replacement URL. Give the new URL to the portal and keep the rotation warning visible until a crawl of the replacement is recorded. Rotating any Thribee member rotates the shared four-brand connection.

The connection list redacts feedUrl by default. An authenticated organization administrator may explicitly request secrets with GET /properties/portal-connections?includeFeedUrl=true. Connection creation, account-reference updates, rotation and disconnection are admin-only operations.

Green-Acres account reference

Green-Acres requires its agency identifier in every <account_id>. Save the value supplied by Green-Acres through the portal detail page or:

PATCH /properties/portal-connections/green_acres
Content-Type: application/json

{ "accountRef": "YOUR_GREEN_ACRES_ACCOUNT_ID" }

The connection token can exist before this value is saved, but the public feed returns the same plain 404 response until a non-empty account reference is present. Updating accountRef does not rotate the URL.

Eligibility reasons

Listing detail and publication responses use these exact PortalIneligibilityReason values. A response can contain more than one.

ReasonMeaning
not_activeThe listing is not Active. This is projected as queued, not as a material ineligible status.
no_priceThe portal family requires a price and none is stored.
price_out_of_rangeThe numeric price is invalid or outside that serializer's supported range.
no_cityA city is required.
no_provinceA province or state is required.
no_postcodeA postcode is required.
no_coordinatesBoth latitude and longitude are required and must be finite numbers.
country_not_supportedThe selected portal does not accept the listing country. Kyero itself is restricted to Spain in this integration.
currency_not_supportedThe feed format does not support the listing currency.
type_not_supportedThe property type has no mapping for that portal family.
no_imagesNo usable image URL meets the portal's image rule. Kyero-family URLs must end directly in GIF, JPEG, JPG or PNG with no query-string suffix.
rent_period_unsupportedThe listing type cannot be represented as a supported sale or rental period.
description_too_shortNo authoritative description meets the family's language or minimum plain-text length rule. Thribee requires at least 30 characters after HTML is removed.
no_public_urlThribee cannot resolve a stable public URL for the advert.

Eligibility is authoritative on the server. Clients should display these receipts and must not reimplement the portal rules.

Legacy JamesEdition route

JamesEdition remains on its existing Fondaro-wide multi-office endpoint:

GET /properties/feeds/jamesedition.xml

It does not use :slug/:token.xml, has no organization connection token, and is not part of the cache and ETag contract described above. Per-listing JamesEdition selection uses the shared publication model, but the XML envelope and crawler schedule remain the legacy JamesEdition 4.3 contract. See JamesEdition Feed.