Ir a la documentación
Desarrolladores
Ver Markdown

Conectar con Homito

En esta página
  1. Conecta un sistema o un agente
  2. Acceso de empresa
  3. Usa el origen de API configurado
  4. curl
  5. JavaScript — servidor, Node.js 20 o posterior
  6. Python — servidor, biblioteca estándar
  7. Interpreta la primera consulta autenticada
  8. Operaciones REST y permisos de las claves
  9. Continúa las consultas de forma segura
  10. Identidad del solicitante y claves de integración

Elige permisos y completa tu primera lectura autenticada desde el servidor.

Conecta un sistema o un agente

Usa la API HTTPS de Homito, limitada a tu empresa, desde tu propio servidor o agente. Cuando un administrador te haya proporcionado una clave, el trabajo habitual de integración puede realizarse sin el portal de Homito. La referencia de la API y el documento OpenAPI describen las mismas operaciones y esquemas.

Acceso de empresa

Un administrador de una empresa de Homito existente y verificada crea una clave en Configuración → API y agentes. Necesita una sesión de usuario de Homito y el permiso settings.edit. El secreto se muestra una sola vez: guárdalo en el gestor de secretos de tu servidor. La clave determina la empresa; enviar un ID de empresa distinto no amplía su acceso. El registro externo de empresas por autoservicio no está disponible. La gestión de claves utiliza el origen de la aplicación web de Homito y su sesión de usuario, no este origen de API externa.

Usa el origen de API configurado

Pide a un administrador de la empresa una clave con read:properties. Configura HOMITO_API_BASE_URL con el origen HTTPS de la API, sin ruta ni parámetros de consulta. Inyecta HOMITO_API_KEY desde el gestor de secretos de tu servidor; nunca la incluyas en código de navegador, una URL o una conversación con un agente.

Estos ejemplos de servidor, sin SDK, realizan la misma primera consulta. curl y JavaScript tienen un plazo total de 10 segundos. Python utiliza un tiempo de espera de red bloqueante de 10 segundos, no un plazo para la respuesta completa. JavaScript y Python rechazan respuestas de más de 2 MiB y solo muestran count/hasMore; curl descarta el cuerpo y muestra el estado HTTP. Guarda el ejemplo de JavaScript como first-read.mjs y ejecútalo con Node.js 20 o posterior.

curl

Ejemplo de código
curl --proto '=https' --fail --silent --show-error --connect-timeout 5 --max-time 10 \
  --output /dev/null --write-out 'HTTP %{http_code}\n' \
  "$HOMITO_API_BASE_URL/api/v1/landlord/inventory/properties?limit=1" \
  -H "Authorization: Bearer $HOMITO_API_KEY"

JavaScript — servidor, Node.js 20 o posterior

Ejemplo de código
const apiOrigin = process.env.HOMITO_API_BASE_URL;
const apiKey = process.env.HOMITO_API_KEY;
if (!apiOrigin || !apiKey) {
  throw new Error("Configure HOMITO_API_BASE_URL and HOMITO_API_KEY on the server.");
}
let base;
try {
  base = new URL(apiOrigin);
} catch {
  throw new Error("Use the configured HTTPS API origin without paths, ports, query or credentials.");
}
if (base.protocol !== "https:" || base.username || base.password || base.port ||
    !(apiOrigin === base.origin || apiOrigin === base.origin + "/")) {
  throw new Error("Use the configured HTTPS API origin without paths, ports, query or credentials.");
}
const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
let response;
try {
  response = await fetch(new URL("/api/v1/landlord/inventory/properties?limit=1", base.origin), {
    headers: { Authorization: "Bearer " + apiKey },
    signal: AbortSignal.timeout(10000),
    redirect: "error",
  });
} catch {
  throw new Error("Homito request failed (network or timeout).");
}
if (!response.ok) {
  throw new Error("Homito read failed (HTTP " + response.status + ").");
}
const declared = response.headers.get("content-length");
if (declared !== null && (!/^[0-9]+$/.test(declared) ||
    !Number.isSafeInteger(Number(declared)) || Number(declared) > MAX_RESPONSE_BYTES)) {
  throw new Error("Homito response exceeded the safe size limit.");
}
if (!response.body) throw new Error("Homito returned an empty response.");
const reader = response.body.getReader();
const chunks = [];
let byteLength = 0;
try {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    byteLength += value.byteLength;
    if (byteLength > MAX_RESPONSE_BYTES) {
      await reader.cancel();
      throw new Error("Response too large.");
    }
    chunks.push(value);
  }
} catch {
  throw new Error("Homito response could not be read within the configured bounds.");
} finally {
  reader.releaseLock();
}
const bytes = new Uint8Array(byteLength);
let offset = 0;
for (const chunk of chunks) {
  bytes.set(chunk, offset);
  offset += chunk.byteLength;
}
let data;
try {
  data = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
} catch {
  throw new Error("Homito returned invalid JSON.");
}
if (!data || !Array.isArray(data.properties) ||
    !Object.prototype.hasOwnProperty.call(data, "nextCursor") ||
    !(data.nextCursor === null || typeof data.nextCursor === "string")) {
  throw new Error("Unexpected Homito response shape.");
}
console.log({ count: data.properties.length, hasMore: data.nextCursor !== null });

Python — servidor, biblioteca estándar

Ejemplo de código
import json
import os
import urllib.error
import urllib.request
from urllib.parse import urlsplit

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

api_origin = os.environ.get("HOMITO_API_BASE_URL")
api_key = os.environ.get("HOMITO_API_KEY")
if not api_origin or not api_key:
    raise RuntimeError("Configure HOMITO_API_BASE_URL and HOMITO_API_KEY on the server.")
try:
    base = urlsplit(api_origin)
    hostname = base.hostname
    port = base.port
except ValueError:
    raise RuntimeError("Use the configured HTTPS API origin without paths, ports, query or credentials.") from None
if not hostname:
    raise RuntimeError("Use the configured HTTPS API origin without paths, ports, query or credentials.")
request_origin = "https://" + ("[" + hostname + "]" if ":" in hostname else hostname)
if (base.scheme != "https" or base.username is not None or base.password is not None
        or port is not None or any(character.isspace() for character in api_origin)
        or chr(92) in api_origin or api_origin not in (request_origin, request_origin + "/")):
    raise RuntimeError("Use the configured HTTPS API origin without paths, ports, query or credentials.")
MAX_RESPONSE_BYTES = 2 * 1024 * 1024
request = urllib.request.Request(
    request_origin + "/api/v1/landlord/inventory/properties?limit=1",
    headers={"Authorization": "Bearer " + api_key},
    method="GET",
)
opener = urllib.request.build_opener(NoRedirect())
try:
    with opener.open(request, timeout=10) as response:
        if not 200 <= response.status < 300:
            raise RuntimeError("Homito read failed (HTTP " + str(response.status) + ").")
        declared = response.headers.get("Content-Length")
        if declared is not None and (not declared.isdecimal() or int(declared) > MAX_RESPONSE_BYTES):
            raise RuntimeError("Homito response exceeded the safe size limit.")
        body = response.read(MAX_RESPONSE_BYTES + 1)
        if len(body) > MAX_RESPONSE_BYTES:
            raise RuntimeError("Homito response exceeded the safe size limit.")
        data = json.loads(body)
except urllib.error.HTTPError as error:
    raise RuntimeError("Homito read failed (HTTP " + str(error.code) + ").") from None
except (urllib.error.URLError, TimeoutError, OSError):
    raise RuntimeError("Homito request failed (network or timeout).") from None
except ValueError:
    raise RuntimeError("Homito returned invalid JSON.") from None
if (not isinstance(data, dict) or not isinstance(data.get("properties"), list)
        or "nextCursor" not in data
        or not (data["nextCursor"] is None or isinstance(data["nextCursor"], str))):
    raise RuntimeError("Unexpected Homito response shape.")
print(json.dumps({"count": len(data["properties"]), "hasMore": data["nextCursor"] is not None}))

Interpreta la primera consulta autenticada

Un resultado vacío correcto es {"properties":[],"nextCursor":null}: la conexión funcionó y esta empresa no tiene inmuebles que devolver. No es un error de autenticación. Si el resultado contiene datos, utiliza el id de un inmueble devuelto para consultar GET /api/v1/landlord/inventory/properties/{propertyId} o /{propertyId}/units. Revisa los esquemas de respuesta de la operación para conocer los campos y los valores que admiten null.

Operaciones REST y permisos de las claves

TareaPermisos de la claveEmpieza aquí
Consultar inmuebles y unidadesread:propertiesGET /api/v1/landlord/inventory/properties

Continúa las consultas de forma segura

Sigue nextCursor hasta que sea null, manteniendo la misma operación de listado y los mismos filtros. Utiliza los cursores solo con la misma empresa y operación de listado. Las previsualizaciones de importación, los trabajos y su historial pertenecen a la clave API que los creó, incluido el acceso mediante ID de trabajo. Una respuesta correcta del /health público, por sí sola, no verifica tu clave ni el acceso a la empresa.

Identidad del solicitante y claves de integración

Mantén las claves del cliente en el servidor. Una clave de empresa no sustituye la identidad del solicitante, su consentimiento, su clave de envío ni su clave de carga. Sigue los requisitos publicados del formulario y de eID de la operación antes de aceptar una solicitud.