ByDomuDevelopers

Tu página web con clave publicable

Lista propiedades con filtros, recibe formularios y visitas, agenda citas y arma un buscador guiado.

GuíaClave publicableSin aprobación

Crea una clave publicable

En ByDomu, Configuración › API Keys › Nueva clave › Publicable. Sirve con cualquier plan pagado, sin solicitud, y solo alcanza lo público: portal:read, leads:write, booking:write, finder:write, events:write.

Puede ir en el navegador (la API responde con CORS abierto), pero todos tus visitantes comparten su límite: 300 peticiones y 60 escrituras por minuto. Para el inventario, pide desde tu servidor y guarda en caché; deja en el navegador solo los formularios. Una clave secreta en el navegador responde 403 SECRET_KEY_IN_BROWSER.

Para quéEndpoint
Listado con filtrosGET /properties
FichaGET /properties/{id}
DestacadasGET /properties/featured
Opciones de los filtrosGET /filters
AsesoresGET /agents
Configuración del portalGET /portal
Formulario de contactoPOST /leads
Solicitud de visitaPOST /leads/schedule
Agenda en líneaPOST /booking/{slug}
Buscador guiadoPOST /finder
Analítica del portalPOST /events

Lista propiedades con filtros

Solo salen las propiedades activas y publicadas en el portal, nunca las notas internas ni los campos personalizados. Si la ubicación es aproximada, address y zipCode llegan en null y las coordenadas redondeadas.

// Clave publicable: puede ir en el navegador. Mejor aún, en tu servidor con caché.
const API = "https://api.bydomu.com/v1";
const KEY = "domu_pk_live_XXXXXXXX";

const params = new URLSearchParams({
  transactionType: "RENT",   // SALE | RENT | SALE_OR_RENT
  type: "APARTMENT",
  city: "Medellín",          // coincidencia parcial
  priceMax: "4000000",
  bedroomsMin: "2",
  hasPetFriendly: "1",       // los has* se mandan solo cuando aplican
  sortBy: "price",           // price | createdAt | area
  sortOrder: "asc",
  pageSize: "12",            // máximo 48
});
const res = await fetch(`${API}/properties?${params}`, { headers: { "X-API-Key": KEY } });
const { data, meta } = await res.json(); // meta: { total, page, pageSize, totalPages }
Ojo con los booleanos. En este endpoint los has* e includeTotal cuentan como verdaderos con cualquier valor no vacío, incluso «false»: para no filtrar, no los mandes. Para recorrer todo sin contar el total, pagina con cursor (el id de la última propiedad).

Recibe el formulario de contacto

Crea o actualiza el contacto con su propiedad y sus UTM, y suma la analítica del portal. Un 400 trae en palabras qué campo falló: muéstralo en el formulario.

JavaScript
await fetch("https://api.bydomu.com/v1/leads", {
  method: "POST",
  headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    firstName: "Laura",                 // obligatorio (2-100)
    phone: "+573000000001",             // obligatorio (7-20)
    email: "[email protected]",
    message: "¿Sigue disponible?",
    propertyId: "clx1prop001",          // en la ficha de una propiedad
    interestedIn: "RENT",               // SALE | RENT | SALE_OR_RENT
    utmSource: new URLSearchParams(location.search).get("utm_source") ?? undefined,
  }),
});
// 201 → { "data": { "success": true } }

Si escuchas webhooks: el formulario dispara lead.submitted, pero hoy ese evento solo trae la respuesta de la API; los datos de la persona llegan en contact.created.

Visitas y citas

POST /leads/schedule registra una solicitud de visita con fecha y franja preferidas; no crea la cita: la inmobiliaria la confirma después.

JavaScript
await fetch("https://api.bydomu.com/v1/leads/schedule", {
  method: "POST",
  headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({
    firstName: "Laura",
    phone: "+573000000001",
    propertyId: "clx1prop001",          // obligatorio
    preferredDate: "2026-10-04",
    preferredTime: "morning",           // morning | afternoon | evening
  }),
});

Para agendar de verdad, usa la agenda en línea de cada asesor: sus horas libres y la cita en un espacio libre (queda CONFIRMED si la página confirma sola; si no, SCHEDULED). Estas tres respuestas vienen doblemente envueltas: { data: { data: … } }.

JavaScript
// 1. Asesores con agenda en línea (cada uno con su slug)
const agents = await get("/booking/agents");
// 2. Horas libres de un mes. OJO: month va de 0 (enero) a 11 (diciembre)
const { data: { data: availability } } = await get("/booking/carlos-rodriguez/availability?month=9&year=2026");
// availability = { configured: true, availableDates: [{ date: "2026-10-05", slots: ["09:00", "10:00"] }] }
// 3. Agendar en un espacio libre
const res = await post("/booking/carlos-rodriguez", {
  firstName: "Laura",
  phone: "+573000000001",
  date: "2026-10-05",
  startTime: "09:00",
  type: "SHOWING",
  propertyId: "clx1prop001",
});
// 201 → { "data": { "data": { "appointmentId": "…", "status": "CONFIRMED" | "SCHEDULED", "ownerNotified": … } } }

Arma un buscador guiado

Pregunta paso a paso qué busca la persona y manda todo junto al final: queda como contacto y la respuesta trae hasta 12 propiedades que encajan, de mejor a peor, con el puntaje (0-100) y el detalle del cálculo.

JavaScript
const res = await post("/finder", {
  transactionType: "SALE",            // SALE | RENT
  propertyType: "APARTMENT",
  city: "Bogotá",
  neighborhood: "Chapinero",
  budgetMin: 250000000,
  budgetMax: 450000000,
  minBedrooms: 2,
  amenities: ["parking", "gym"],
  firstName: "Laura",
  phone: "+573000000001",
  subscribedToAlerts: true,
});
// 201 → { "data": { "contact": { "id": "…" }, "total": 7,
//   "properties": [{ "id": "…", "title": "…", "price": 450000000, "matchScore": 87,
//                    "breakdown": [{ "criterio": "presupuesto", "puntos": 30, "estado": "match" }], … }] } }

Cuenta visitas y clics

Con events:write, tu web alimenta la analítica del portal en ByDomu. La respuesta es inmediata y no confirma que el evento se haya guardado.

JavaScript
await post("/events", { type: "PROPERTY_VIEW", propertyId: "clx1prop001", referrer: document.referrer });
// PAGE_VIEW | PROPERTY_VIEW | SEARCH | LEAD_FORM | WHATSAPP_CLICK | CALL_CLICK
// SCHEDULE_CLICK | SHARE_CLICK | FINDER_SUBMIT  → 200 { "data": { "success": true } }

Si algo falla

  • 400Un campo no cumple el esquema (nombre muy corto, teléfono inválido, pageSize mayor a 48, month fuera de 0-11).
  • 403Usaste una publicable en un endpoint privado (el mensaje dice cuáles puede usar) o una secreta desde el navegador.
  • 404La propiedad o el slug de agenda no es de tu inmobiliaria.
  • 429Tus visitantes agotaron el límite de la clave: sirve el inventario desde una caché.