JSON Format
The JSON document — a properties array — and one annotated property with its agency and agent. Field definitions live in the Field Reference.
Document
A feed is a single properties array. Each property carries its own agency and agent objects, so there are no separate agency or agent lists and no separate removal list.
{
"providerId": "acme-realty", /* optional - see below */
"properties": [
{
/* one property, with its agency and agent nested inside */
}
]
}
| Field | Type | Required | Notes |
|---|---|---|---|
properties | array | Required | Every active listing, each with its agency and agent. [] is valid — “no active listings”, distinct from a failure. A bare top-level array is accepted too. |
Send the current set of active listings every time (large feeds can be split into pages — see Paging). A listing that is no longer in properties is removed from our database on the next sync — there is nothing to send for removals. A document that fails to download or cannot be read never removes anything.
Optional envelope keys (provider: "HAVHO", feed_version: "2.0", generated_at, updated_since) are tolerated and, when present, checked: an unrecognised provider or major feed_version is rejected. An explicit top-level deletions array is still honoured if you send one.
A top-level providerId is also accepted as an optional envelope key — unlike every other field, it is only ever read here (never on a property/agent/agency), and only the first time this feed imports, to seed the agreed identity described in the Field Reference callout. Once agreed, sending a different value here does nothing except get logged.
Property object
{
"id": "CPT-S-1842",
"agency": {
"id": "AGY-77",
"name": "Atlantic Edge Properties"
},
"agent": {
"id": "AGT-1049",
"fullName": "Naledi Mokoena",
"email": "naledi.mokoena@example.co.za",
"phone": "+27 82 555 0142",
"location": "Cape Town, Atlantic Seaboard",
"photoUrl": "https://cdn.example.com/agents/agt-1049.jpg",
"ffcNumber": "FFC-2026-114829"
},
"type": "sale",
"status": "active",
"propertyType": "apartment",
"title": "Sea-facing apartment in Green Point",
"description": "Renovated two-bedroom apartment with open-plan living areas and sea views.",
"price": 4250000,
"address": "14 Ocean View Drive, Green Point, Cape Town, Western Cape",
"suburb": "Green Point",
"city": "Cape Town",
"province": "Western Cape",
"beds": 2,
"baths": 2,
"garage": 1,
"parking": 1,
"unitSize": 98,
"photoUrls": [
"https://cdn.example.com/listing/1842/image1.jpg",
"https://cdn.example.com/listing/1842/image2.jpg",
"https://cdn.example.com/listing/1842/image3.jpg"
],
"features": [
"Pool",
"Security Estate"
]
}
A type: "rent" listing additionally carries availableNow (boolean) and availableFrom (YYYY-MM-DD).
Agency & agent objects
agencyandagentare required on every property and identify themselves withid. The same agent or agency repeats on each of its properties; we de-duplicate byid.- Never send
providerId: it is set automatically from the feed. The agent and agency are stored with the property they arrive on. sourceUpdatedAt,statusand removals are optional and normally left out: timestamps are set when a record is stored or changes, everything inpropertiesis active, and a listing missing frompropertiesis removed automatically.- Field definitions: Agency, Agent.
Type & value conventions
- Numbers (
price,beds,baths,garage,parking,erfSize,unitSize) are JSON numbers, not strings. - Booleans (
availableNow) are JSONtrue/false. - Dates are
YYYY-MM-DD; timestamps (sourceUpdatedAt,deletedAt) are full ISO-8601 with a timezone. - Arrays (
photoUrls,features) preserve order. Omit an emptyfeaturesarray rather than sending[]. - Omit optional fields you have no value for instead of sending
null/"".
Versioning
- New optional field, or a new allowed enum value →
MINORbump (2.0→2.1). - Renaming / removing a field, or changing its type or requiredness →
MAJORbump (2.0→3.0). The importer rejects an unrecognised major version.
Full example
See examples/sample-feed.json for a complete, valid document.