Your CRM can publish an agent's listings on Homezone.al and keep them up to date: create, update, take down and delete them. This page is for the CRM's developers.
There is one token per Homezone account. When the agent creates a new one or removes it, the old one stops working at once and your requests get 401. They get 401 too once the token expires, if the agent chose an end date. Keep it on your server like a password, never in a web page, an app or a link.
All requests go to https://apiv2.homezone.al over HTTPS. Bodies and answers are JSON.
| Header | Value |
|---|---|
Authorization | Bearer followed by the token |
X-Finger | Your CRM's name, for example bsp-crm. Never empty. |
Content-Type | application/json, except for uploads (multipart/form-data) |
| Endpoint | Does | Permission |
|---|---|---|
POST /media/upload | Uploads a photo, when your photo links aren't public. | Publish and update your listings |
POST /property/save | Creates or updates a listing, its status included. | Publish and update your listings |
GET /property/load | Reads the agent's listings. | Read your listings |
POST /property/delete | Deletes listings. | Delete your listings |
Each endpoint needs the permission next to it, the one the agent ticks under Profile → Connect your CRM. Every other endpoint answers 403. None of them moves a listing back to the top of the search: only a listing's first publication sets its date.
POST /property/save. Send the whole listing every time, with your own id in external_id. The first save creates the listing; every later save with the same external_id updates it.
curl -X POST https://apiv2.homezone.al/property/save \
-H "Authorization: Bearer $HOMEZONE_TOKEN" \
-H "X-Finger: bsp-crm" \
-H "Content-Type: application/json" \
-d '{
"external_id": "BSP-58412",
"status": "published",
"contract_type": "rent",
"category": "apartment",
"title": "Apartament 1+1 me qira, Komuna e Parisit",
"description": "70 m², i mobiluar, me ashensor.",
"price": 700,
"currency": "EUR",
"pay_period": "monthly",
"bedrooms": 1,
"bathrooms": 1,
"size": 70,
"county": "Tiranë",
"city": "Tiranë",
"area": "Komuna e Parisit",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"contact_phone": "+355691234567",
"assets_url": ["https://cdn.example.com/58412/1.jpg", "https://cdn.example.com/58412/2.jpg"]
}'The answer:
{ "error": "", "errorCode": null, "id": 185276, "url": "https://homezone.al/…", "duplicates": [] }"status": "published", a listing is published only if it has a category, county and city, a description, at least one photo, an address or area, and the contact name, email and phone. Otherwise it is saved as a draft, without an error.Send the photos in one of two ways. Either way, the list order is the display order and the first photo is the cover.
assets_url. Each link must open without a login. Homezone downloads the photos on every save, so a save can take several seconds: wait 60 seconds before you time out.assets: "assets": [{"id": 912345}, {"id": 912346}]. This is what the Homezone app does. Keep the ids and send the same ones on later saves: nothing is downloaded again, so saves stay fast.Send all the photos on every save. A photo left out of the list is removed from the listing, and its id can't be used again: upload it again if you need it. A save without any photos keeps the current ones but saves the listing as a draft.
POST /media/upload as multipart/form-data, with the same Authorization and X-Finger headers.
| Field | Value |
|---|---|
file | The image, with a file name that ends in its extension. |
fileSize | The whole file's size in bytes. |
tempFileName | Empty. Only pieces of a large file use it (below). |
curl -X POST https://apiv2.homezone.al/media/upload \ -H "Authorization: Bearer $HOMEZONE_TOKEN" \ -H "X-Finger: bsp-crm" \ -F "file=@58412-1.jpg" \ -F "fileSize=834512" \ -F "tempFileName="
The answer:
{ "status": 1, "message": "File uploaded", "id": 912345, "thumbUrl": "https://media.homezone.al/…", "path": "2026/11/07/…" }tempFileName, and fileSize is always the whole file's size. Each answer before the last says "Chunk uploaded" and gives a tempFileName for the next piece. The last answer has the id.UNSUPPORTED_MEDIA.| Field | Values | Notes |
|---|---|---|
external_id | text | Your id. A save with a known one updates instead of creating. |
external_url | link | The listing on your site. Optional. |
status | published, draft | Default draft. |
contract_type | sale, rent | |
category | apartment, house, vila, studio, penthouse, development, commercial, shop, store, bar, bar_restaurant, hotel, office, land, park, property | Needed to publish. |
title | text | At most 128 characters. |
description | text | Needed to publish. |
price | number | |
currency | EUR, USD, ALL | Default EUR. ALL is stored in EUR at 100 lek per euro. |
pay_period | daily, weekly, monthly, yearly | Rent only. Default monthly. |
bedrooms, bathrooms, floor | numbers | |
size, land_size | numbers | In m². |
build_year | year | Optional. |
facilities | cable_tv, cable_data, central_heating, dishwasher, dryer, washing_machine, garden, balcony, gas_heating, internet, alarm, parking, reception, wheelchair_access, microwave, serviced_property | A list. |
has_certificate, contact_whatsapp | 0, 1 | |
energy_class | A+, A … G, null | |
energy_indicator | 0 to 2000, or null | kWh per m² a year. |
country | AL | |
county, city | text | Needed to publish. |
area, address | text | One of the two is needed to publish. |
latitude, longitude | numbers | |
contract_status | available, rented, sold, withdrawn, sale_agreed | Default available. |
contact_name, contact_email, contact_phone | text | Needed for a new listing, and to publish. |
expiry_date | date | Default one year after the save. |
assets_url | list of links | Public photos. See Photos. |
assets | list of {"id": …} | Uploaded photos. See Photos. |
A number field that gets something else is saved as 0. Fields that Homezone sets itself (user_id, agency_id, publish_date, views and the like) are ignored.
"status": "draft". Saving it again with "published" puts it back with its old date."contract_status": "sold" or "rented".POST /property/delete with the Homezone ids, [185276, 185277]. The answer counts them: {"result": 2}. A later save with the same external_id brings the listing back.Your CRM's version wins. The agent's edits on Homezone are replaced at the next save, and a listing deleted on Homezone comes back at the next save with its external_id. To keep a listing off Homezone, unpublish or delete it from the CRM.
GET /property/load?externalId=BSP-58412 finds one listing by your id.GET /property/load?status=published&page=1&limit=100 lists them, at most 200 a page. The answer is {"properties": [...], "total": 120}.| Answer | Why | What to do |
|---|---|---|
401 | The token was removed, replaced or has expired, or the account was closed. | Ask the agent for a new token. Don't retry. |
403 domain is not authorized | No token, or not a token Homezone made: check that it was copied whole. | Send the token as <code>Authorization: Bearer</code>. |
403 Missing fingerprint | The X-Finger header is missing. | Add it. |
403 This token is not allowed to do this | The token doesn't have the permission this endpoint needs, or the endpoint isn't on this page. | Ask the agent for a token with that permission. |
400 UNSUPPORTED_MEDIA | An upload that isn't JPEG, PNG, WebP or GIF. | Convert it, or leave it out. |
400 INVALID_CONTACT | A new listing without contact name, email and phone. | Send all three. |
400 | GET where POST is needed. | Use POST. |
500 | A problem on our side. | Retry later. |
external_id for one property only, and never reuse it.id next to your own.