POST /properties
It is the same object and the same response as
POST /properties/verify, with two differences: here
the property is stored, and we return an identifier so you can operate on it later.
If verify says a portal can publish your property, create says the same. There are no surprises between the two.
It does not publish to any portal. Create stores the property and gets everything ready; publishing is a separate step.
What it is for
To upload the property once and keep an identifier you use to look it up, refresh its images or delete it.
You send the images as URLs and we download them: from the moment you create the property they live on our servers.
Resource URL
POST https://property-api.mapaprop.com/property-v1/properties
Authentication
Same as the rest of the API: Authorization: Bearer <your token>.
curl -X POST https://property-api.mapaprop.com/property-v1/properties \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @propiedad.json
🔑 clientRef — WHICH client this property belongs to
It travels with the token, in any of these three ways:
x-client-ref: cliente-42 ← header (recommended)
?clientRef=cliente-42 ← query
{ "clientRef": "cliente-42", … } ← body
It is the id of the CLIENT, not of the property. The same clientRef for every property of that
client of yours.
It is what groups the inventory: GET /properties returns the ones for a given clientRef, and
it is what keeps one of your clients apart from another. If you send the id of each property, each
one ends up in its own group and the list returns a single one.
You choose the value and it is opaque to us: your internal id, a UUID, whatever you use — as long as
it identifies the client. It is the same clientRef you use to register
its portal connections.
The request body
The client is declared ONCE, not on every property. Before your first upload, declare it with
POST /property-v1/customers: it returns the customerId Mapaprop assigns to it, and from then on
every property of yours inherits its name, phone, email, address and branch. Without it the upload
responds 400 with rule: account_required.
Changing a detail later is a PUT to the same route. ⚠️ It reaches new properties: the ones you already
uploaded keep the contact they were created with, which is the record of how they were published.
⚠️ If you send customer inside the object it is ignored (like propertyId, customerId and
branchId).
Exactly the same as verify: the object goes in canonical.
{
"canonical": { "…the property object…" }
}
The fields, their rules and which ones are required are in
The property JSON object. The per-portal publishing
configuration is in The publication key.
Test your object with /properties/verify before
creating it. It creates nothing and tells you the same thing create will.
The response
201 Created. It is the verify response plus these five keys:
{
"paId": "01M3PTZ8YBMM5DSNW03WP8VD9E",
"saved": true,
"savedAt": "2026-09-29T15:01:46.059Z",
"status": "active",
"images": {
"status": "processing",
"total": 4,
"processed": 0,
"message": "Procesando 4 imágenes…"
},
"object": { "…same as verify…" },
"portals": { "…same as verify…" },
"summary": { "…same as verify…" }
}
| Key | What it is |
|---|---|
clientRef | Which client it belongs to. The same one you sent in the envelope — we return it so you can match it against your own database without remembering what you asked with |
paId | The property identifier in our API. It is your handle for everything that comes next |
saved | It was stored |
savedAt | When |
status | active, or deleted if you deleted it |
images | The status of your images — see below |
The object, portals and summary blocks are the same ones from verify and are explained
there.
🔑 Store the paId
It is the only identifier for that property on our side. Store it in your system next to your own id. Without it there is no way to look the property up, refresh its images or delete it.
You do not generate it and you cannot choose it. If you post the same object again, a new property
is created with a different paId: create does not deduplicate.
Images: what happens after the 201
As soon as we answer you we start downloading your images. It is the only part of create that is not immediate.
201 → images.status: "processing" …downloading your images…
images.status: "ready" done
o "partial" some could not be downloaded
While they are processing, the property cannot be modified, deleted or published. Any call
returns 409 telling you how many are done.
You check the status with GET /property-v1/properties/{paId}. It usually
takes a few seconds.
Wait 2-3 seconds between requests. With normal volumes 3 or 4 are enough: measured, 40 images took 9 seconds. Polling in a tight loop will not get you the answer any sooner.
What to do with each status
images.status | What it means | What you do |
|---|---|---|
processing | We are downloading them | Wait and check again |
ready | All on our servers | Move on |
partial | Some could not be downloaded | Look at assets and retry |
When something fails: assets
The GET returns one entry per image, with the URL that failed and the reason:
"assets": [
{ "n": 1, "slot": "image", "source": "https://tu-cdn.com/frente.jpg",
"url": "https://…/image-1.jpg", "ok": true },
{ "n": 2, "slot": "image", "source": "https://tu-cdn.com/living.jpg",
"error": "el host del origen no existe (DNS no resuelve) [ENOTFOUND]", "ok": false }
]
The most common reasons:
| Reason | What to check |
|---|---|
el host del origen no existe (DNS no resuelve) | The URL is misspelled, or the domain no longer exists |
el origen rechazó la conexión | Your server refused the connection |
HTTP 403 | Your CDN blocked us, or the file is not there — see below |
HTTP 404 | The image is not at that URL |
el origen no respondió en 15s | Too slow |
el certificado del origen está vencido | An HTTPS problem on your side |
An HTTP 403 from your own S3 bucket almost always means the file IS NOT THERE. If the bucket
does not expose its listing — the usual setup — S3 answers 403 instead of 404 so as not to reveal
which keys exist. So before checking permissions, make sure the URL points at a file that is still
there.
If you get partial, the property was still stored, with the images we did manage to download.
Fix the URLs that failed and retry; there is no need to create it again.
Retrying the images
POST https://property-api.mapaprop.com/property-v1/properties/{paId}/assets
It retries only the missing ones: images already on our servers are neither touched nor downloaded again.
It responds 202 with the new status.
Use it when the GET returned partial and you have already fixed the problem on your side.
Looking up and deleting
GET /properties— one property by itspaId, or the listDELETE /properties/{paId}— delete. It does not take down your listings on the portals
Modifying
PUT /properties/{paId}— full replacement: you send the whole object and we return adiffof what changed, with the cleared fields listed separately
Errors
| Code | When |
|---|---|
400 | canonical is missing from the body, customer is missing from the object, or the body is not valid JSON |
401 | Token missing, expired or invalid |
404 | That paId does not exist |
409 | The property is processing its images — see below |
501 | PUT: not available yet |
The 409
{
"status": 409,
"error": "La propiedad está procesando sus imágenes",
"detail": "Procesando 2 de 4 imágenes. No se puede modificar ni publicar hasta que terminen."
}
It is not an error in your request: it means we have not finished yet. Check the GET again and retry
once it is ready or partial.
If a download gets stuck, the block clears on its own after a while, and then you can retry the images or delete the property.
How to use it
- You build the object — the fields
- You test it with
/properties/verifyuntil noblockersare left POST /properties→ store thepaId- You check the
GETuntilimages.statusis no longerprocessing - If it ended up
partial, you fix the URLs and call/assets