Naming

All field names are camelCase and match the downstream HAVHO Property Feed contract exactly. Send numbers as numbers, dates as ISO-8601, and omit optional fields you have no value for rather than sending null or "".

providerId must be a stable, agreed identity

Never send providerId on a property, agent, agency or deletion — it is set on our side, once, for the feed as a whole, and returned on every one of those records. It must be agreed with the consumer up front and must not change once that agreement is made (a staging value and a production value are normally different, deliberately chosen strings, e.g. havho-sandbox). It is not derived from anything that can shift on its own — not a database row id, not an internal feed number.

The normal way to set it is explicitly, on the feed's own configuration. If you would rather agree it up front and have us capture it automatically, your feed document (not any property/agent/agency inside it) may declare a top-level providerId — see JSON / XML Format. That value is read only the first time this feed imports, when nothing has been agreed yet, and is immediately locked in on our side; a later pull sending a different value is logged and ignored rather than applied, so the identity stays stable even if your own value ever drifts.

Agency

The organisation a listing belongs to. Sent as the agency object inside each property and stored with it (agency_id, agency_name). Keyed by id.

FieldTypeRequiredNotes
idstringRequiredPermanent, unique id for the organisation. Must not change if the agency renames, moves or is restructured. Max 255 chars.
namestringRequiredAgency / organisation name.
providerIdstringOptionalSet automatically from the feed; do not send it.
sourceUpdatedAtstring (ISO-8601)OptionalSet automatically when the record is stored or its values change. Only send it if your source has a reliable timestamp.

Agent

The person responsible for a listing. Sent as the agent object inside each property, linked to that property's agency and stored with the property (agent_* columns). Keyed by id.

FieldTypeRequiredNotes
idstringRequiredPermanent, unique id. Must stay the same if the agent changes name, email, phone, office, agency, or location. Max 255 chars.
providerIdstringOptionalSet automatically from the feed and always the same as the agency's; do not send it. The REST API returns it.
fullNamestringRequiredAgent's full name.
emailstringRequiredAgent's email address.
phonestringRequiredAgent's contact number, including country code where possible.
sourceUpdatedAtstring (ISO-8601)OptionalSet automatically when the record is stored or its values change. Only send it if your source has a reliable timestamp.
locationstringOptionalFree-text location / area the agent works.
photoUrlstring (HTTPS URL)OptionalAgent profile image, absolute HTTPS URL pointing at the file.
ffcNumberstringOptionalFidelity Fund Certificate number.

Property listing

An active listing with its agency and agent nested inside it. Keyed by id. A feed is just an array of these.

Identity & relationships

FieldTypeRequiredNotes
idstringRequiredPermanent, unique id of the listing. Sole key for create / update / removal matching. Max 255 chars.
agentobjectRequiredThe responsible Agent, nested inside the property.
agencyobjectRequiredThe owning Agency, nested inside the property.
providerIdstringOptionalSet automatically from the feed; do not send it.
sourceUpdatedAtstring (ISO-8601)OptionalSet automatically when the record is stored or its values change. Only send it if your source has a reliable timestamp. The REST API returns it on every record.

Classification

FieldTypeRequiredAllowed values / format
typestringRequiredsale | rent
statusstringOptionalLeave it out: everything in properties is an active listing. If you send it, it must be active.
propertyTypestringRequiredhouse | apartment | townhouse | vacant land | farm/ smallholding | commercial property | industrial property | development
titlestringRequiredListing headline.
descriptionstringRequiredPlain text or simple HTML. HTML entities are decoded on import.

Pricing

FieldTypeRequiredNotes
pricenumberRequiredNumeric ZAR, no symbols or thousands separators (4250000, not R 4,250,000). Up to 2 decimals. For type: "rent" this is the monthly amount.

Location

FieldTypeRequiredNotes
addressstringRequiredFull property address, single line. Max 500 chars.
suburbstringRecommendedSuburb.
citystringRecommendedCity / town.
provincestringRecommendedProvince.

Size & structure

FieldTypeRequiredNotes
bedsintegerOptionalBedrooms, whole number ≥ 0.
bathsintegerOptionalBathrooms, whole number ≥ 0.
garageintegerOptionalGarages, whole number ≥ 0.
parkingintegerOptionalOpen parking spaces, whole number ≥ 0.
erfSizenumberOptionalErf / land size, in m².
unitSizenumberOptionalUnit / building size, in m².

Media

FieldTypeRequiredNotes
photoUrlsarray<string>Required1 to 21 unique absolute HTTPS URLs, in display order. The first is the primary / thumbnail image. URLs must point directly at the file and stay reachable while the listing is active.

Features & availability

FieldTypeRequiredNotes
featuresarray<string>OptionalFree-text labels, up to 100 per listing and 100 characters each (e.g. Borehole, Solar Inverter, Sea Views). Labels are trimmed, de-duplicated case-insensitively and kept as sent. These five known labels also drive the site's filters and are normalised to this exact spelling: Pet friendly, Garden, Flatlet, Pool, Security Estate. Commas inside a label are replaced by a space.
availableNowbooleanRentalsRequired when type: "rent". Whether the unit is available immediately.
availableFromstring (date)RentalsRequired when type: "rent". YYYY-MM-DD.

Removed listing

Optional. You do not need to send removals: a listing that is no longer in properties is deleted from our database automatically on the next sync (only after a complete, readable snapshot). If you do send a top-level deletions array it is honoured and recorded with your reason.

Identity. A distinct deletion event is the combination of providerId, listingId and deletedAt. The same event is never returned twice by the REST API /property-deletions endpoint, which also adds a stable deletionId (derived from those three values) so consumers can de-duplicate. If a listing is removed, re-listed and removed again, each removal is a separate event with its own deletedAt. Feeds may repeat a deletion on every pull; the importer collapses identical events.

FieldTypeRequiredNotes
providerIdstringRequiredUpstream source system id.
listingIdstringRequiredThe id of the listing that was removed.
deletedAtstring (ISO-8601)RequiredWhen the listing was removed.
reasonstringRequiredsold | rented | withdrawn | expired | deleted | source_removed

How the records link

id relationships
Property      id: "CPT-S-1842"
  ↳ agency    id: "AGY-77"
  ↳ agent     id: "AGT-1049"

These ids stay stable when ordinary listing, agent, or agency information changes.

Namespaces. The listing, agent and agency ids are unique within a provider, not across providers. The provider is the feed itself, identified by its providerId (set on the feed's configuration, or captured once from the feed document's own top-level providerId — see the callout near the top of this page — never sent per-record). An agent has no namespace of its own: it always shares its agency's, and the REST API returns that providerId on every agent record. The same agent / agency repeats on each of its properties and is de-duplicated by id.

This page describes what you send us. For what a consuming application reads back — different field names, pagination, incremental sync — see the REST API Reference.