POST /properties/verify
You send a property object and we answer three things:
- Whether the object is valid according to Mapaprop's contract, and what it is missing if it is not.
- What we understood of what you sent — how many fields we rebuilt from your
attributesarray. - Whether each portal could publish it, and when it could not, exactly what is missing and whose problem it is.
This endpoint does not create, modify or publish anything. It leaves no trace. It is there for you to adjust your object before integrating, as many times as you want.
It is in beta. The shape of the response may change while this stage lasts. Changes are announced on this page beforehand.
What it is for
Integrating with several portals has a problem that shows up late: you send an object that looks complete to you, it gets published, and only then do you see that the listing came out without a price, without a pool or with the wrong type — because each portal asks for different things and stays quiet about what it does not understand.
This endpoint lets you see that before publishing, without creating anything and without needing the portal accounts connected.
| Resource information | |
|---|---|
| Authentication | Required (JWT) — scope property-api-verify |
| HTTP Method | POST |
| Response | JSON |
| Version | 1 |
Resource URL
https://property-api.mapaprop.com/property-v1/properties/verify
Authentication
An OAuth2 token and the property-api-verify scope are required.
POST /property-v1/properties/verify
Host: property-api.mapaprop.com
Content-Type: application/json
Authorization: Bearer {access_token}
The token is obtained like any other: POST /api/action/oauth2-v1/authorize.
Scopes travel signed inside the token. If we add a scope after you generated yours, it has to be generated again to take effect — and generating a new one invalidates the previous one. Ask up front for every scope you are going to need.
The request body
Verify works WITHOUT having declared your client. It is a tester: it validates the token and
describes the object, it does not upload anything. If you have not declared it yet, it fills the client
details with a placeholder, and tells you so in customerSource — so you can try your object on
day one, with no prior step and without mistaking filler for your own data.
⚠️ Where it IS required is the upload: POST /property-v1/properties responds 400 with
rule: account_required if the account has no client declared. You declare it ONCE with
POST /property-v1/customers. The properties you upload afterwards inherit it; the ones already
uploaded keep the contact they were created with.
⚠️ One detail about the placeholder: country is not filled in. A filler value there would be
indistinguishable from a real answer, and it is the one field you should not guess.
{
"canonical": { …el objeto de la propiedad… },
"portals": ["argenprop", "zonaprop", "cabaprop"]
}
| Key | Required | What it is |
|---|---|---|
canonical | yes | The property object. property is accepted as an alternative name |
portals | no | Which portals to evaluate. If you omit it, all of them are evaluated |
The object that goes in canonical is the usual one:
The property JSON object.
If you send a portal that does not exist, the request fails with a 400 instead of ignoring it silently.
Which portals it reports back
If in publication.api you declare… | The response brings… |
|---|---|
| One or several portals | only those |
Nothing (you do not send publication) | all of them |
The idea is that you do not receive problems about a portal you did not configure. If you already
declared Argenprop and nothing else, Zonaprop does not show up in the response nor count in the
summary.
And if you have not configured any yet, you see all of them: that is the case of someone evaluating the integration who wants to see how their object converts on each portal before deciding.
If you want to narrow the response without declaring configuration, use portals in the
request body. They are two different things: portals says what you want to look at, publication
says what you have configured.
publication — the publishing configuration, inside the object
Inside canonical you can send a publication key with what each portal needs from your account and
from your publishing choices. It is not a field of the property: it is how you publish it.
{
"publication": {
"api": {
"argenprop": { "advertiserId": 99999, "visible": true },
"zonaprop": {
"plan": { "plan": "SUPERDESTACADO" },
"branch": { "custId": 1, "email": "contacto@ejemplo.com", "name": "Inmobiliaria", "phone1": "1144445555" }
},
"cabaprop": { "branchOfficeId": 42 }
}
}
}
Zonaprop's branch and MercadoLibre's sellerContact are autofilled from the client you declared:
you do not need to send them. They are in the example because if you do send them, they win —
autofilling has the lowest precedence of all.
| Portal | Fields |
|---|---|
argenprop | advertiserId (number) · visible (boolean) |
zonaprop | plan (object) · branch (object) |
cabaprop | branchOfficeId (number) |
mercadolibre | listingTypeId (string) · condition (string) · sellerContact (object) |
Each portal also accepts a token (string), which is validated for shape and never for validity:
this endpoint does not verify accounts. Its value does not appear anywhere in the response.
publication does not travel to the portals as one more field: it is consumed here and discarded.
The full reference —the two levels, what happens if you send something that does not belong, and the
detail about the token— is on its own page:
The publication key.
The response
The response carries the clientRef, the same one you sent in the envelope. We return it on all six
paths (verify, create, read, list, update and delete) so you can match it against your own database
without having to remember which clientRef you asked with.
{
"mode": "client",
"object": {
"valid": true,
"contract": [],
"mapapropZone": "1-2-189-973",
"conflicts": [],
"conflicts": []
},
"portals": {
"argenprop": {
"status": "fields_complete",
"connection": "not_verified",
"zone": {
"sent": {
"code": "1-2-189-973",
"level": 3
},
"publishingTo": "Mar del Plata",
"status": "mapped"
},
"blockers": [],
"pendingAtPublishTime": []
},
"zonaprop": {
"status": "fields_complete",
"connection": "not_verified",
"zone": {
"sent": {
"code": "1-2-189-973",
"level": 3
},
"publishingTo": "General Pueyrredón",
"status": "mapped"
},
"blockers": [],
"pendingAtPublishTime": []
},
"cabaprop": {
"status": "fields_complete",
"connection": "not_verified",
"zone": {
"sent": {
"code": "1-2-189-973",
"level": 3
},
"status": "not_mapped",
"message": "Todavía no tenemos mapeada esta zona para cabaprop. Escribinos con el código de zona y la publicamos: el mapeo queda hecho para siempre."
},
"blockers": [],
"pendingAtPublishTime": []
},
"mercadolibre": {
"status": "coming_soon",
"message": "MercadoLibre: disponible pronto. La integración todavía no está habilitada en este endpoint."
}
},
"summary": {
"complete": 3,
"of": 4
}
}
object — what happened to your object
| Field | What it says |
|---|---|
valid | Whether the object complies with Mapaprop's contract |
contract | What does not comply: field, rule and the detail. Empty if valid is true |
mapapropZone | The zone we resolved, as zone0-zone1-zone2-zone3 |
conflicts | Fields where your attributes contradicts the individual field. attributes wins |
conflicts deserves attention: if you send "type": 23 and your attributes says "Departamento",
it is published as Departamento. It is not an error, it is the rule — but if that was not what
you wanted, you see it here instead of in the listing.
portals — what each portal says
| Field | What it says |
|---|---|
status | The verdict. Values below |
connection | connected · not_connected · not_verified |
zone | Which zone you sent and which one we are going to publish in. Below |
blockers | What is missing in the object in order to publish |
pendingAtPublishTime | What is missing in order to publish, but is not part of the object (plan, branch, mapped zone) |
status values:
| Value | Means |
|---|---|
fields_complete | The object has everything that portal needs |
missing_data | A piece of data is missing. Look at blockers — there is something you can fix |
not_supported | The portal does not support something about this property. You cannot fix it by sending more data |
coming_soon | The integration is not enabled on this endpoint yet |
Some portals cannot build the listing until they have their configuration: Zonaprop needs the
plan and the branch details to build the listing. In those cases the portal shows up with its
status and its pendingAtPublishTime, but without the mapping — not because your object is wrong,
but because the account data it is built with is still missing.
Blockers coming from publication also carry a concrete example of how to fix them, attached
to the message: se espera number · ejemplo: "publication": { "api": { "argenprop": { "advertiserId": 99999 } } }. See The publication key.
Each blocker carries:
{ "field": "price",
"kind": "missing",
"message": "`price` es obligatorio en el objeto de Mapaprop y no vino…",
"youCanFixIt": true }
youCanFixIt answers the only question that matters about a blocker: do I fix this by sending
more data, or is it a limitation of the portal? If it is true, send the data. If it is false,
that portal does not admit what you are asking of it and no JSON will solve it.
blockers and pendingAtPublishTime are different things on purpose. A perfect object that is
only missing the choice of plan is not an incomplete object: it is a listing that has not been
configured. That is why pendingAtPublishTime does not affect the status.
zone — which zone we are going to publish in
Every portal has its own zone map, and we maintain the translation between your Mapaprop zone and
that portal's. zone shows you both ends:
{ "sent": { "code": "1-2-189-973", "level": 3 },
"publishingTo": "Mar del Plata",
"status": "mapped" }
| Field | What it says |
|---|---|
sent.code | The zone you sent, exactly as we build it from your zone0…zone3 |
sent.level | How deep that code goes: 2 is the whole district, 3 goes down to the locality |
sent.name | The name of your zone, if you sent the description for that level |
publishingTo | The name of the portal zone where your listing is going to appear |
status | mapped · not_mapped · not_sent · not_verified |
The states:
| Value | Means |
|---|---|
mapped | We resolved the zone. publishingTo tells you where it is going to appear |
not_mapped | Your zone is valid, but we do not have it mapped to that portal yet. It comes with a message explaining what to do |
not_sent | You did not send the zone in the object |
not_verified | We did not get to look up the mapping, so we are not claiming anything |
Check publishingTo even when the status is mapped. It is the public name of the portal
zone: if you sent one zone and a different one shows up there, the listing is going to appear in
that other place. Write to us and we will fix it — you are the only one who can notice, because
you are the one who knows the property.
A 0 at the end of the code does not mean a level is missing: 1-2-201-0 is the whole
district, and it is a valid publishing destination on its own. That is why level counts up to
the last non-zero number.
summary
{ "summary": { "complete": 3, "of": 4 } }
How many portals have their required fields complete, out of the total evaluated.
Mapaprop's required fields override the portal's
This is the most important thing to understand about the endpoint.
Mapaprop has its own contract of required fields, and it does not depend on what each portal
asks for. If one of those fields is missing, no portal stays in fields_complete — even if
that particular portal does not require it.
Example: Argenprop does not require the price to build its listing. Even so, if you do not send
price:
{
"object": {
"valid": false,
"contract": [ { "field": "price", "rule": "required", "detail": "" } ]
},
"portals": {
"argenprop": {
"status": "missing_data",
"blockers": [
{ "field": "price",
"kind": "missing",
"message": "`price` es obligatorio en el objeto de Mapaprop y no vino. Sin él no se publica en ningún portal, lo exija o no este portal en particular.",
"youCanFixIt": true }
]
}
},
"summary": { "complete": 0, "of": 4 }
}
The full list of required fields is in The property JSON object.
Only the absence of a required field blocks. A field that is present but with an incorrect
format shows up in object.contract and informs without blocking — so that you can keep
correcting without the endpoint becoming unusable. Always check object.contract, not just the
summary.
You do not need the portal connected
The connection field tells you whether that portal's account is connected, but it does not
condition anything: the object is mapped the same way and the blockers are computed the same way.
| Value | Means |
|---|---|
connected | The account is connected |
not_connected | There is no connected account for that portal |
not_verified | The connection status could not be verified |
It is a piece of data on the side, and it still matters: a flawless object is not published if the account is not connected. But to perfect your object you do not need any.
Errors
| Code | When |
|---|---|
400 | The body is missing, it is not valid JSON, canonical/property is missing, or you asked for a portal that does not exist. ⚠️ Verify does NOT return 400 for having no client declared — that belongs to the upload; here it is simulated with a placeholder |
403 | The token is missing the property-api-verify scope |
{ "error": "Portales desconocidos: idealista. Válidos: argenprop, zonaprop, cabaprop, mercadolibre, mapaprop" }
MercadoLibre
It always comes out as coming_soon. The integration exists but is not enabled on this endpoint yet,
and we prefer not to show you an unverified mapping: you would take it for good.
How to use it
- Send your object exactly as you have it today.
- Look at
object.validandobject.contractfirst. If a required field is missing, fix that — it is blocking every portal at once. - Look at
object.conflicts. If something shows up, yourattributesand your individual fields are contradicting each other, andattributeswins. - Portal by portal, resolve the
blockerswithyouCanFixIt: true. - The
youCanFixIt: falseones you cannot resolve: they are limits of the portal. - Repeat. Nothing is created, so try as many times as you need.
Do you need help? Contact Mapaprop technical support