POST /properties/verify
Mandás el objeto de una propiedad y te contestamos tres cosas:
- Si el objeto es válido según el contrato de Mapaprop, y qué le falta si no lo es.
- Qué entendimos de lo que mandaste — cuántos campos reconstruimos de tu arreglo
attributes. - Si cada portal lo podría publicar, y cuando no, exactamente qué le falta y de quién es el problema.
Este endpoint no crea, no modifica y no publica nada. No deja rastro. Es para que ajustes tu objeto antes de integrarte, tantas veces como quieras.
Está en beta. La forma de la respuesta puede cambiar mientras dure esta etapa. Los cambios se anuncian antes en esta página.
Para qué sirve
Integrarse a varios portales tiene un problema que aparece tarde: mandás un objeto que te parece completo, se publica, y recién ahí ves que el aviso salió sin precio, sin pileta o con el tipo equivocado — porque cada portal pide cosas distintas y se calla lo que no entiende.
Este endpoint te deja ver eso antes de publicar, sin crear nada y sin necesidad de tener las cuentas de los portales conectadas.
| Resource information | |
|---|---|
| Authentication | Requerida (JWT) — scope property-api-verify |
| HTTP Method | POST |
| Response | JSON |
| Version | 1 |
Resource URL
https://property-api.mapaprop.com/property-v1/properties/verify
Autenticación
Se pide un token OAuth2 y el scope property-api-verify.
POST /property-v1/properties/verify
Host: property-api.mapaprop.com
Content-Type: application/json
Authorization: Bearer {access_token}
El token se obtiene como cualquier otro: POST /api/action/oauth2-v1/authorize.
Los scopes viajan firmados dentro del token. Si te agregamos un scope después de que generaste el tuyo, hay que generarlo de nuevo para que tome efecto — y generar uno nuevo invalida el anterior. Pedí de entrada todos los scopes que vayas a necesitar.
El cuerpo de la petición
{
"canonical": { …el objeto de la propiedad… },
"portals": ["argenprop", "zonaprop", "cabaprop"]
}
| Clave | Obligatorio | Qué es |
|---|---|---|
canonical | sí | El objeto de la propiedad. Se acepta property como nombre alternativo |
portals | no | Qué portales evaluar. Si no lo mandás, se evalúan todos |
El objeto que va en canonical es el mismo de siempre:
El objeto JSON de la propiedad.
Si mandás un portal que no existe, la petición falla con un 400 en vez de ignorarlo en silencio.
Qué portales te reporta
Si en publication.api declarás… | La respuesta trae… |
|---|---|
| Uno o varios portales | sólo ésos |
Nada (no mandás publication) | todos |
La idea es que no te lleguen problemas de un portal que no configuraste. Si ya declaraste Argenprop
y nada más, Zonaprop no aparece en la respuesta ni cuenta en el summary.
Y si todavía no configuraste ninguno, los ves todos: es el caso de quien está evaluando la integración y quiere ver cómo se convierte su objeto en cada portal antes de decidir.
Si querés acotar la respuesta sin declarar la configuración, usá portals en el cuerpo de la
petición. Son dos cosas distintas: portals dice qué querés mirar, publication dice qué tenés
configurado.
publication — la configuración de publicación, dentro del objeto
Dentro de canonical podés mandar una clave publication con lo que cada portal necesita de tu
cuenta y de tus elecciones de publicación. No es un campo de la propiedad: es cómo la publicás.
{
"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 }
}
}
}
| Portal | Campos |
|---|---|
argenprop | advertiserId (number) · visible (boolean) |
zonaprop | plan (object) · branch (object) |
cabaprop | branchOfficeId (number) |
mercadolibre | listingTypeId (string) · condition (string) · sellerContact (object) |
Cada portal acepta además un token (string), que se valida de forma y nunca de validez: este
endpoint no verifica cuentas. Su valor no aparece en ninguna parte de la respuesta.
publication no viaja a los portales como un campo más: se consume acá y se descarta.
La referencia completa —los dos niveles, qué pasa si mandás algo que no corresponde, y el detalle del
token— está en su propia página: La clave publication.
La respuesta
{
"mode": "client",
"keyUsed": "canonical",
"object": {
"valid": true,
"contract": [],
"mapapropZone": "1-2-189-973",
"conflicts": [],
"rebuiltFromAttributes": {
"fields": 22,
"amenities": 36
},
"unresolved": []
},
"portals": {
"argenprop": {
"status": "fields_complete",
"requiredFieldsComplete": true,
"connection": "not_verified",
"zone": {
"sent": {
"code": "1-2-189-973",
"level": 3
},
"publishingTo": "Mar del Plata",
"status": "mapped"
},
"blockers": [],
"pendingAtPublishTime": [],
"completeness": {
"percentage": 100,
"informed": 3,
"supportedByThisType": 3
}
},
"zonaprop": {
"status": "fields_complete",
"requiredFieldsComplete": true,
"connection": "not_verified",
"zone": {
"sent": {
"code": "1-2-189-973",
"level": 3
},
"publishingTo": "General Pueyrredón",
"status": "mapped"
},
"blockers": [],
"pendingAtPublishTime": []
},
"cabaprop": {
"status": "fields_complete",
"requiredFieldsComplete": true,
"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.",
"requiredFieldsComplete": false
}
},
"summary": {
"complete": 3,
"of": 4
}
}
object — qué pasó con tu objeto
| Campo | Qué dice |
|---|---|
valid | Si el objeto cumple el contrato de Mapaprop |
contract | Lo que no cumple: field, rule y el detalle. Vacío si valid es true |
mapapropZone | La zona que resolvimos, como zone0-zone1-zone2-zone3 |
conflicts | Campos donde tu attributes contradice al campo suelto. Gana attributes |
rebuiltFromAttributes | Cuántos campos y amenidades reconstruimos de tu arreglo attributes |
unresolved | Ejes que no pudimos resolver (por ejemplo el tipo, si falta el catálogo del país) |
conflicts merece atención: si mandás "type": 23 y tu attributes dice "Departamento", se
publica como Departamento. No es un error, es la regla — pero si no era lo que querías, lo ves acá
en vez de en el aviso.
portals — qué dice cada portal
| Campo | Qué dice |
|---|---|
status | El veredicto. Valores abajo |
requiredFieldsComplete | Si los obligatorios del objeto están todos |
connection | connected · not_connected · not_verified |
zone | Qué zona mandaste y en cuál vamos a publicar. Abajo |
blockers | Lo que falta en el objeto para poder publicar |
pendingAtPublishTime | Lo que falta para publicar, pero no es del objeto (plan, sucursal, zona mapeada) |
completeness | Cuántos atributos informaste de los que ese portal soporta para ese tipo |
Valores de status:
| Valor | Significa |
|---|---|
fields_complete | El objeto tiene todo lo que ese portal necesita |
missing_data | Falta un dato. Mirá blockers — hay algo que vos podés arreglar |
not_supported | El portal no soporta algo de esta propiedad. No lo podés arreglar mandando más datos |
coming_soon | La integración todavía no está habilitada en este endpoint |
Algunos portales no producen payload hasta tener su configuración: Zonaprop necesita el plan y
los datos de la sucursal para armar el aviso. En esos casos el portal aparece con su estado y sus
pendingAtPublishTime, pero sin el mapeo — no porque tu objeto esté mal, sino porque todavía falta
el dato de cuenta con el que se construye.
Los blockers que vienen de publication traen además un ejemplo concreto de cómo se arreglan,
pegado al mensaje: se espera number · ejemplo: "publication": { "api": { "argenprop": { "advertiserId": 99999 } } }. Ver La clave publication.
Cada blocker trae:
{ "field": "price",
"kind": "missing",
"message": "`price` es obligatorio en el objeto de Mapaprop y no vino…",
"youCanFixIt": true }
youCanFixIt contesta la única pregunta que importa de un blocker: ¿esto lo arreglo yo mandando
más datos, o es una limitación del portal? Si es true, mandá el dato. Si es false, ese portal
no admite lo que le estás pidiendo y no hay JSON que lo resuelva.
blockers y pendingAtPublishTime son cosas distintas a propósito. Un objeto perfecto al que sólo
le falta elegir el plan no es un objeto incompleto: es una publicación sin configurar. Por eso
pendingAtPublishTime no afecta a requiredFieldsComplete ni al status.
completeness mide otra cosa que status: cuántos atributos opcionales informaste de los que ese
portal soporta para ese tipo de propiedad. Podés tener completeness: 100% y missing_data al
mismo tiempo — significa que informaste todos los atributos que el portal admite, pero te falta un
campo obligatorio.
zone — en qué zona vamos a publicar
Cada portal tiene su propio mapa de zonas, y nosotros mantenemos la traducción entre tu zona de
Mapaprop y la de ese portal. zone te muestra las dos puntas:
{ "sent": { "code": "1-2-189-973", "level": 3 },
"publishingTo": "Mar del Plata",
"status": "mapped" }
| Campo | Qué dice |
|---|---|
sent.code | La zona que mandaste, tal como la armamos con tus zone0…zone3 |
sent.level | Hasta qué nivel llega ese código: 2 es el partido entero, 3 baja a la localidad |
sent.name | El nombre de tu zona, si mandaste la descripción del nivel correspondiente |
publishingTo | El nombre de la zona del portal donde va a salir tu aviso |
status | mapped · not_mapped · not_sent · not_verified |
Los estados:
| Valor | Significa |
|---|---|
mapped | Resolvimos la zona. publishingTo te dice dónde va a salir |
not_mapped | Tu zona es válida, pero todavía no la tenemos mapeada a ese portal. Trae un message con qué hacer |
not_sent | No mandaste la zona en el objeto |
not_verified | No llegamos a consultar el mapeo, así que no afirmamos nada |
Mirá publishingTo aunque el estado sea mapped. Es el nombre público de la zona del portal:
si mandaste una zona y ahí aparece otra, el aviso va a salir en ese otro lugar. Escribinos y lo
corregimos — sos el único que puede darse cuenta, porque sos el que conoce la propiedad.
Un 0 al final del código no significa que falte un nivel: 1-2-201-0 es el partido entero, y
es un destino de publicación válido por sí mismo. Por eso level cuenta hasta el último número
distinto de cero.
summary
{ "summary": { "complete": 3, "of": 4 } }
Cuántos portales tienen los obligatorios completos, sobre el total evaluado.
Los campos obligatorios de Mapaprop mandan sobre los del portal
Esto es lo más importante de entender del endpoint.
Mapaprop tiene su propio contrato de campos obligatorios, y no depende de lo que pida cada
portal. Si falta uno de esos campos, ningún portal queda en fields_complete — aunque ese
portal en particular no lo exija.
Ejemplo: Argenprop no exige el precio para armar su payload. Igual, si no mandás price:
{
"object": {
"valid": false,
"contract": [ { "field": "price", "rule": "required", "detail": "" } ]
},
"portals": {
"argenprop": {
"status": "missing_data",
"requiredFieldsComplete": false,
"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 }
}
La lista completa de campos obligatorios está en El objeto JSON de la propiedad.
Sólo la ausencia de un obligatorio bloquea. Un campo presente pero con un formato incorrecto
aparece en object.contract e informa sin bloquear — para que puedas ir corrigiendo sin que
el endpoint se vuelva inusable. Revisá siempre object.contract, no sólo el summary.
No hace falta tener el portal conectado
El campo connection te dice si la cuenta de ese portal está conectada, pero no condiciona nada:
el objeto se mapea igual y los blockers se calculan igual.
| Valor | Significa |
|---|---|
connected | La cuenta está conectada |
not_connected | No hay cuenta conectada para ese portal |
not_verified | No se pudo verificar el estado de la conexión |
Es un dato al costado, y sigue importando: un objeto impecable no se publica si la cuenta no está conectada. Pero para perfeccionar tu objeto no necesitás tener ninguna.
Errores
| Código | Cuándo |
|---|---|
400 | Falta el body, no es JSON válido, falta canonical/property, o pediste un portal que no existe |
403 | Al token le falta el scope property-api-verify |
{ "error": "Portales desconocidos: idealista. Válidos: argenprop, zonaprop, cabaprop, mercadolibre, mapaprop" }
MercadoLibre
Sale siempre como coming_soon. La integración existe pero todavía no está habilitada en este
endpoint, y preferimos no mostrarte un mapeo sin verificar: lo tomarías por bueno.
Cómo usarlo
- Mandá tu objeto tal como lo tenés hoy.
- Mirá primero
object.validyobject.contract. Si hay un obligatorio faltando, arreglá eso — va a estar bloqueando todos los portales a la vez. - Mirá
object.conflicts. Si aparece algo, tuattributesy tus campos sueltos se están contradiciendo, y ganaattributes. - Portal por portal, resolvé los
blockersconyouCanFixIt: true. - Los
youCanFixIt: falseno los podés resolver: son límites del portal. - Repetí. No se crea nada, así que probá tantas veces como necesites.
¿Necesitás ayuda? Contactá al Soporte técnico de Mapaprop