BETA

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…" }
}
KeyWhat it is
clientRefWhich 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
paIdThe property identifier in our API. It is your handle for everything that comes next
savedIt was stored
savedAtWhen
statusactive, or deleted if you deleted it
imagesThe 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.statusWhat it meansWhat you do
processingWe are downloading themWait and check again
readyAll on our serversMove on
partialSome could not be downloadedLook 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:

ReasonWhat 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ónYour server refused the connection
HTTP 403Your CDN blocked us, or the file is not there — see below
HTTP 404The image is not at that URL
el origen no respondió en 15sToo slow
el certificado del origen está vencidoAn 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

Modifying

  • PUT /properties/{paId} — full replacement: you send the whole object and we return a diff of what changed, with the cleared fields listed separately

Errors

CodeWhen
400canonical is missing from the body, customer is missing from the object, or the body is not valid JSON
401Token missing, expired or invalid
404That paId does not exist
409The property is processing its images — see below
501PUT: 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

  1. You build the object — the fields
  2. You test it with /properties/verify until no blockers are left
  3. POST /properties → store the paId
  4. You check the GET until images.status is no longer processing
  5. If it ended up partial, you fix the URLs and call /assets