homlity / sdk-wasi-php8
SDK de Homlity en PHP para el CRM inmobiliario Wasi: crear, actualizar, buscar y publicar inmuebles con la API v1 de api.wasi.co
Requires
- php: >=8.0
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- php-curl-class/php-curl-class: ^11.0
Requires (Dev)
- phpunit/phpunit: ^9.6.33
README
Homlity SDK para Wasi
SDK oficial de Homlity en PHP para crear, actualizar, buscar y publicar inmuebles en el CRM inmobiliario Wasi (API v1).
homlity.com · Portal de desarrolladores · GitHub · Packagist
📚 Documentación
| Documento | Contenido |
|---|---|
| Este README | Instalación, referencia del SDK, ejemplos y troubleshooting |
| docs/wasi-api-reference.md | Referencia completa de la API de Wasi: endpoints, campos modificables, filtros y formas de respuesta |
| docs/laravel.md | Integración en Laravel: contenedor, repositorio, jobs, comando artisan, cache de catálogos, testing y logging seguro |
| docs/desarrolladores.html | Página web para desarrolladores con la marca Homlity — abrir en el navegador |
| examples/ | 8 scripts ejecutables, del hola mundo a la sincronización completa |
Tabla de contenidos
- ¿Para qué sirve este SDK?
- Arquitectura en 30 segundos
- Requisitos
- Instalación
- Credenciales:
id_companyywasi_token - Quick start
- Referencia del SDK
- Campos de un inmueble
- Filtros de búsqueda
- Manejo de respuestas y errores
- Integración con Laravel
- Receta completa: sincronizar un inmueble de tu CRM a Wasi
- Catálogos de Wasi (IDs que necesitas)
- Limitaciones conocidas y buenas prácticas
- Troubleshooting
- Ejemplos ejecutables y tests
- Extender el SDK
- Soporte y contribución
1. ¿Para qué sirve este SDK?
Wasi es el CRM que usan miles de inmobiliarias en LatAm para administrar su
inventario de inmuebles, sus asesores y sus clientes. Su API v1 (https://api.wasi.co/v1/)
permite leer y escribir ese inventario por HTTP en lugar de cargarlo a mano desde el panel web.
Este paquete es la capa PHP que Homlity —y cualquier portal, lonja, landing o CRM que se integre con Homlity— usa para hablar con esa API sin escribir cURL a mano.
Qué te resuelve
| Problema | Qué hace el SDK |
|---|---|
| Construir URLs con credenciales en cada llamada | ConfigSDK guarda id_company + wasi_token y RequestsFather::buildUrl() los inyecta en todas las peticiones |
| Recordar las rutas de la API | Cada operación es un método del facade: propertyStore(), propertyUpdate(), searchPropertys(), mediaStore(), getAllUsers() |
| Serializar payloads y subir imágenes | Codifica el JSON del inmueble y arma el multipart/form-data con CURLFile para las fotos |
| Leer la respuesta cruda de cURL | Objetos Response con isOk(), getBody(), getBodyArray(), getBodyRaw() y helpers como getTotal() / getUsers() |
| Acoplarte a un framework | Cero dependencias de framework: solo php-curl-class. Funciona en Laravel, Symfony, WordPress o PHP plano |
| Distinguir «llegó la petición» de «funcionó la operación» | isOk() valida el transporte y el status del cuerpo; getErrorMessage() te da el motivo |
| Peticiones colgadas | Timeouts por defecto (30 s / 10 s de conexión) y reintentos configurables en ConfigSDK |
| Booleanos y arrays en la query string | Serializa true/false y features[]=2&features[]=4 como espera Wasi |
Qué NO hace (por diseño, hoy)
- No cubre toda la API de Wasi. Implementa 5 operaciones (ver §7). Los catálogos (países, ciudades, tipos de inmueble, monedas) se consultan aparte — ver §13.
- No valida el contenido del payload: comprueba que sea serializable, pero si mandas un
id_cityinexistente el error lo devuelve Wasi. - No cachea catálogos ni resultados de búsqueda.
- No gestiona colas ni jobs — eso lo pone tu aplicación.
Si necesitas alguna de estas capas, mira §17 Extender el SDK.
2. Arquitectura en 30 segundos
El SDK sigue una separación App / Infrastructure muy simple:
Tu aplicación
│
▼
┌──────────────────────────────────────────────────────────┐
│ App\WasiSDKFacade ← único punto de entrada │
│ App\ConfigSDK ← id_company + wasi_token │
└──────────────────────────────────────────────────────────┘
│ crea, configura y ejecuta
▼
┌──────────────────────────────────────────────────────────┐
│ InfraStructure\Requets\RequestsFather (abstracta) │
│ · setConfig(ConfigSDK) │
│ · getEndpoint(): string ← la implementa cada hija │
│ · buildUrl() → https://api.wasi.co/{endpoint} │
│ ?id_company=…&wasi_token=… │
│ │
│ ├─ PropertyStoreRequest POST v1/property/add │
│ ├─ PropertyUpdateRequest POST v1/property/update/{id}
│ ├─ SearchPropertysRequests GET v1/property/search │
│ ├─ MediaStoreRequest POST v1/property/upload-image/{id}
│ └─ UserGetAllRequest GET v1/user/all-users │
└──────────────────────────────────────────────────────────┘
│ devuelve
▼
┌──────────────────────────────────────────────────────────┐
│ InfraStructure\Responses\ResponseFather │
│ · isOk() ← transporte Y status del cuerpo │
│ · isTransportOk() · getStatus() · getErrorCode() │
│ · getErrorMessage() · getHttpStatusCode() │
│ · getBody() · getBodyRaw() · getBodyArray() · getRows() │
│ ├─ PropertyStoreResponse (+ getIdProperty()) │
│ ├─ PropertyUpdateResponse │
│ ├─ SearchPropertysResponse (+ getTotal(), getProperties())
│ ├─ MediaStoreResponse (+ getIdImage()) │
│ └─ UserGetAllResponse (+ getUsers()) │
└──────────────────────────────────────────────────────────┘
Excepciones · Homlity\WASI\SDK\Exceptions
WasiException ← base, captúrala para atrapar todo
├─ ConfigException ← faltan credenciales o config
└─ RequestException ← payload o archivo inválidos
Consecuencia práctica: añadir un endpoint nuevo = una clase Request (con su getEndpoint()
y su run()), una clase Response, y un método en el facade. Ver §17.
Mapa de archivos
| Archivo | Rol |
|---|---|
| src/App/WasiSDKFacade.php | Fachada pública. Es lo único que tu código necesita conocer |
| src/App/ConfigSDK.php | Contenedor de credenciales |
| src/InfraStructure/Requets/RequestsFather.php | Base abstracta: config + construcción de URL |
| src/InfraStructure/Requets/PropertyStoreRequest.php | Crear inmueble |
| src/InfraStructure/Requets/PropertyUpdateRequest.php | Actualizar inmueble |
| src/InfraStructure/Requets/SearchPropertysRequests.php | Buscar inmuebles |
| src/InfraStructure/Requets/MediaStoreRequest.php | Subir imagen |
| src/InfraStructure/Requets/UserGetAllRequest.php | Listar asesores |
| src/InfraStructure/Responses/ResponseFather.php | Base de todas las respuestas |
| src/Exceptions/ | WasiException, ConfigException, RequestException |
| tests/ | Suite PHPUnit — composer test |
Nota sobre el namespace: la carpeta se llama
Requets(sin las), y así aparece en el namespaceHomlity\WASI\SDK\InfraStructure\Requets. No es un typo de esta documentación: es el nombre real del paquete publicado. No lo cambies sin publicar una versión mayor.
3. Requisitos
| Requisito | Versión | Por qué |
|---|---|---|
| PHP | >= 8.0 |
Tipos de propiedad, promoción de constructor y argumentos con nombre |
ext-curl |
* | Todas las peticiones HTTP |
ext-json |
* | Serialización de payloads y parseo de respuestas |
ext-mbstring |
* | Manejo de textos con tildes/ñ en títulos y observaciones |
php-curl-class/php-curl-class |
^11.0 |
Cliente HTTP usado internamente |
| Plan Pro de Wasi | — | La API solo está disponible en plan Pro |
Comprueba tus extensiones:
php -m | grep -E 'curl|json|mbstring'
4. Instalación
composer require homlity/sdk-wasi-php8
Si tu proyecto aún no tiene minimum-stability configurado y Composer se queja, fija la versión:
composer require homlity/sdk-wasi-php8:^1.0
Luego, en tu código:
require __DIR__ . '/vendor/autoload.php'; use Homlity\WASI\SDK\App\ConfigSDK; use Homlity\WASI\SDK\App\WasiSDKFacade;
Actualizar desde la v1
La v2 corrige varios comportamientos que causaban bugs silenciosos. La migración es corta:
| Si en la v1 escribías | En la v2 |
|---|---|
if ($res->isOk()) para saber si funcionó |
Sigue igual — y ahora sí detecta los errores de Wasi |
$body = $res->getBodyArray(); if ($body['status'] === 'success') |
if ($res->isOk()) |
$body['id_property'] |
$res->getIdProperty() |
$res->getTotal() devolvía false al fallar |
Devuelve ?int — usa ?? 0 si lo sumas |
array_filter($body, 'is_numeric', ARRAY_FILTER_USE_KEY) |
$res->getProperties() o $res->getRows() |
'for_sale' => 'true' (cadena) |
'for_sale' => true (booleano) — la cadena sigue funcionando |
['filters' => ['id_city' => 61]] |
Sigue igual, o directamente ['id_city' => 61] |
Comprobar is_readable() antes de mediaStore() |
El SDK valida y lanza RequestException |
| PHP 7.4 | PHP 8.0 o superior |
Lo único que puede romperse: si tu código dependía de que isOk() devolviera true ante un
{"status":"error"} —normalmente sin saberlo—, ahora devolverá false. Ese es precisamente el
bug que la v2 arregla. Si necesitas el comportamiento antiguo en algún punto concreto, usa
isTransportOk().
composer require homlity/sdk-wasi-php8:^2.0
Instalar desde el repositorio Git (sin pasar por Packagist)
{
"repositories": [
{ "type": "vcs", "url": "https://github.com/homlity/sdk-wasi-php8" }
],
"require": {
"homlity/sdk-wasi-php8": "dev-main"
}
}
5. Credenciales: id_company y wasi_token
La API de Wasi se autentica con dos valores que viajan en cada petición:
| Valor | Qué es |
|---|---|
id_company |
Identificador numérico de tu empresa en Wasi |
wasi_token |
Token de acceso de la empresa |
Cómo obtenerlos
- Entra a tu cuenta de Wasi (requiere plan Pro).
- Ve a Configuración → Ajustes generales.
- Busca la sección «API de Wasi» y genera el token.
- Wasi te muestra
id_companyywasi_token. Cópialos.
Cómo guardarlos
Nunca los pongas en el código. Usa variables de entorno:
# .env WASI_ID_COMPANY=1234567890 WASI_TOKEN=ABCD_EFGH_IJKL_MNOP
$config = new ConfigSDK(); $config->setCompanyId(getenv('WASI_ID_COMPANY')); $config->setToken(getenv('WASI_TOKEN'));
⚠️ Seguridad: este SDK envía las credenciales en la query string (
?id_company=…&wasi_token=…), que es como Wasi documenta el acceso vía GET. Eso significa que el token puede acabar en logs de proxies, access logs de tu servidor, APM o trazas de error. Nunca vuelques la URL construida (buildUrl()) en un log y asegúrate de que tu APM redacta el parámetrowasi_token. La propia documentación de Wasi recomienda enviar las credenciales por POST cuando sea posible.
6. Quick start
<?php require __DIR__ . '/vendor/autoload.php'; use Homlity\WASI\SDK\App\ConfigSDK; use Homlity\WASI\SDK\App\WasiSDKFacade; // 1. Credenciales $config = new ConfigSDK(getenv('WASI_ID_COMPANY'), getenv('WASI_TOKEN')); // 2. Fachada — es el único objeto que necesitas $wasi = new WasiSDKFacade($config); // 3. Buscar los 10 inmuebles en venta más recientes $result = $wasi->searchPropertys([ 'for_sale' => true, 'short' => true, // omite galerías: respuesta mucho menor 'take' => 10, 'order_by' => 'created_at', 'order' => 'desc', ]); // isOk() comprueba el transporte Y el `status` del cuerpo if (!$result->isOk()) { throw new RuntimeException('Wasi respondió con error: ' . $result->getErrorMessage()); } echo 'Total de inmuebles que coinciden: ' . $result->getTotal() . PHP_EOL; foreach ($result->getProperties() as $property) { printf( "#%s · %s · %s · %s\n", $property['id_property'], $property['title'] ?? 's/título', $property['city_label'] ?? '-', $property['sale_price_label'] ?? '-' ); }
7. Referencia del SDK
App\ConfigSDK
Credenciales y opciones de transporte.
| Método | Firma | Descripción |
|---|---|---|
__construct |
__construct($idCompany = null, $token = null) |
Ambos opcionales |
setCompanyId |
setCompanyId($id): self |
Acepta string o int; recorta espacios |
setToken |
setToken(?string $token): self |
Recorta espacios |
setHost |
setHost(string $host): self |
URL base — útil para apuntar a un mock |
setTimeout |
setTimeout(int $s): self |
Petición completa. Por defecto 30 s |
setConnectTimeout |
setConnectTimeout(int $s): self |
Establecer conexión. Por defecto 10 s |
setRetries |
setRetries(int $n): self |
Reintentos ante fallo de transporte. Por defecto 0 |
assertUsable |
assertUsable(): void |
Lanza ConfigException si faltan credenciales |
Los getters correspondientes (getIdCompany(), getToken(), getHost(), getTimeout(),
getConnectTimeout(), getRetries()) devuelven lo esperado.
$config = new ConfigSDK(getenv('WASI_ID_COMPANY'), getenv('WASI_TOKEN')); // O con setters, que encadenan $config = (new ConfigSDK()) ->setCompanyId(getenv('WASI_ID_COMPANY')) ->setToken(getenv('WASI_TOKEN')) ->setTimeout(60) ->setRetries(2);
Los setters recortan espacios y saltos de línea: el fallo más común es copiar el token del
.envcon un\ncolado. Si faltan credenciales, el SDK lanzaConfigExceptionantes de salir a la red, en lugar de dejar que Wasi respondaid_company or wasi_token is wrong.
App\WasiSDKFacade
$wasi = new WasiSDKFacade($config);
| Método | Endpoint Wasi | Verbo | Devuelve |
|---|---|---|---|
propertyStore(array $data) |
v1/property/add |
POST | PropertyStoreResponse |
propertyUpdate(string $idProp, array $data) |
v1/property/update/{id} |
POST | PropertyUpdateResponse |
searchPropertys(array $filters = []) |
v1/property/search |
GET | SearchPropertysResponse |
mediaStore($path, $idProperty, ?$description, ?$position) |
v1/property/upload-image/{id} |
POST | MediaStoreResponse |
getAllUsers() |
v1/user/all-users |
GET | UserGetAllResponse |
propertyStore(array $data): PropertyStoreResponse
Crea un inmueble. El array se envía como cuerpo JSON.
$response = $wasi->propertyStore([ 'id_user' => 45678, // asesor responsable (ver getAllUsers) 'id_property_type' => 2, // 2 = Apartamento 'id_country' => 1, // 1 = Colombia 'id_region' => 5, 'id_city' => 61, 'id_currency' => 12, 'for_sale' => true, 'for_rent' => false, 'sale_price' => 450000000, 'title' => 'Apartamento con vista al parque', 'address' => 'Cra 45 #23-10', 'area' => 78, 'built_area' => 72, 'bedrooms' => 3, 'bathrooms' => 2, 'garages' => 1, 'floor' => 7, 'stratum' => 4, 'observations' => 'Remodelado en 2024, cocina abierta, balcón.', 'latitude' => '6.244203', 'longitude' => '-75.581212', 'id_status_on_page' => 1, // visible en la web 'reference' => 'HOM-2026-0451', // tu referencia interna 'features' => [2, 4], // arrays → features[]=2&features[]=4 ]); // isOk() comprueba el transporte Y el `status` del cuerpo if (!$response->isOk()) { throw new RuntimeException('Wasi rechazó el inmueble: ' . $response->getErrorMessage()); } $idProperty = $response->getIdProperty(); // ← guárdalo en tu base de datos
Respuesta de Wasi:
{ "id_property": 123456, "status": "success" }
💡 Guarda siempre el
id_propertydevuelto junto al registro de tu propio sistema. Es la única forma de volver a actualizar ese inmueble o de subirle fotos.
💡 Usa el campo
referencepara guardar tu identificador interno dentro de Wasi. Así puedes reconciliar en ambos sentidos.
propertyUpdate(string $idProp, array $data): PropertyUpdateResponse
Actualiza un inmueble existente. Envía solo los campos que cambian.
$response = $wasi->propertyUpdate(123456, [ 'sale_price' => 425000000, 'observations' => 'Precio actualizado — negociable.', 'id_status_on_page' => 1, ]); if (!$response->isOk()) { error_log('No se actualizó: ' . $response->getErrorMessage()); }
El identificador acepta string o int. Un identificador vacío lanza RequestException en
lugar de construir una URL rota.
Respuesta de Wasi:
{ "status": "success" }
⚠️ Solo puedes enviar campos marcados como modificables en Wasi. Ver §8. Campos calculados (
sale_price_label,city_label,galleries, …) se ignoran o provocan error.
💡 Los campos array (
features,portals) se separan solos y viajan en la query string, que es donde Wasi los espera. Tú los pasas como un array normal.
searchPropertys(array $filters = []): SearchPropertysResponse
Busca inmuebles. Acepta las dos formas de pasar los filtros:
// Anidada — más legible cuando mezclas filtros con otras claves $wasi->searchPropertys([ 'filters' => [ 'id_city' => 61, 'for_sale' => true, 'min_price' => 200000000, 'max_price' => 600000000, 'take' => 25, ], ]); // En plano — equivalente $wasi->searchPropertys([ 'id_city' => 61, 'for_sale' => true, ]);
En la v1 solo funcionaba la forma anidada, y la plana se descartaba en silencio. Desde la v2 ambas son válidas y equivalentes.
Helpers de la respuesta:
$result = $wasi->searchPropertys(['for_rent' => true]); $result->isOk(); // bool — transporte + status del cuerpo $result->getTotal(); // ?int — total de coincidencias (no de la página) $result->getProperties(); // array<int, array> — solo los inmuebles $result->getBodyArray(); // array completo, con "total" y "status"
Forma de la respuesta de Wasi:
{
"0": { "id_property": "123456", "title": "Apartamento…", "sale_price": "450000000", "…": "…" },
"1": { "id_property": "123457", "…": "…" },
"total": 348,
"status": "success"
}
getProperties() te devuelve solo los registros, ya reindexados desde 0, sin que tengas que
filtrar las claves numéricas a mano.
💡 Pasa
'short' => truepara omitir galerías y características y aligerar mucho la respuesta cuando solo necesitas listar.
Ver todos los filtros en §9.
mediaStore($path, $idProperty, ?$description, ?$position): MediaStoreResponse
Sube una imagen a la galería de un inmueble. $path es una ruta local del sistema de archivos
(no una URL): el SDK la envuelve en un CURLFile y la manda como multipart/form-data.
use Homlity\WASI\SDK\Exceptions\RequestException; try { $response = $wasi->mediaStore( '/var/www/storage/fotos/fachada.jpg', 123456, description: 'Fachada principal', position: 1 ); } catch (RequestException $e) { // El archivo no existe, no es legible, está vacío, o le pasaste una URL error_log($e->getMessage()); }
El SDK valida el archivo antes de enviarlo y lanza RequestException con un mensaje claro si:
| Situación | Mensaje |
|---|---|
| Ruta vacía | Falta la ruta de la imagen… |
| Le pasas una URL | mediaStore() espera una ruta local, no una URL… |
| El archivo no existe | La imagen no existe: … |
| Existe pero PHP no puede leerlo | La imagen existe pero no es legible por el usuario de PHP: … |
| Pesa 0 bytes | La imagen está vacía (0 bytes): … |
En la v1 estos casos salían como una petición con adjunto vacío y un error opaco de Wasi.
Para subir varias fotos, itera — el endpoint acepta una imagen por petición:
$fotos = [ '/var/www/storage/fotos/01-fachada.jpg', '/var/www/storage/fotos/02-sala.jpg', '/var/www/storage/fotos/03-cocina.jpg', ]; foreach ($fotos as $i => $foto) { try { $res = $wasi->mediaStore($foto, $idProperty, position: $i + 1); } catch (RequestException $e) { error_log($e->getMessage()); continue; } if (!$res->isOk()) { error_log("Falló la subida de {$foto}: " . $res->getErrorMessage()); } // Sé amable con la API: no dispares 40 uploads en paralelo usleep(300_000); }
💡 La primera imagen que subas suele quedar como principal. Sube la fachada primero, o usa
positionpara fijar el orden explícitamente.
getAllUsers(): UserGetAllResponse
Lista los usuarios (asesores) de la empresa. Lo necesitas para obtener el id_user que va en
propertyStore().
$response = $wasi->getAllUsers(); if (!$response->isOk()) { throw new RuntimeException('No se pudieron leer los asesores: ' . $response->getErrorMessage()); } foreach ($response->getUsers() as $user) { printf( "%s · %s %s · %s · %s\n", $user['id_user'], $user['first_name'] ?? '', $user['last_name'] ?? '', $user['email'] ?? '-', $user['user_type'] ?? '-' // ADMIN, REALTOR, … ); }
getUsers() devuelve una lista limpia: sin la clave status y reindexada desde 0, así que
puedes iterarla directamente.
Objetos de respuesta
Todos heredan de InfraStructure\Responses\ResponseFather:
| Método | Devuelve | Descripción |
|---|---|---|
isOk() |
bool |
Lo que quieres el 95 % del tiempo. true solo si cURL no falló y el cuerpo trae status: success |
isTransportOk() |
bool |
Solo si la petición HTTP se completó. Es lo que hacía isOk() en la v1 |
getStatus() |
?string |
success, error, o null si el endpoint no devuelve status |
getErrorCode() |
?string |
Código de error de Wasi |
getErrorMessage() |
?string |
Motivo legible del fallo, o null si todo fue bien |
getHttpStatusCode() |
int |
Código HTTP de la respuesta |
getBodyArray() |
?array |
Cuerpo como array. null si no era JSON válido. Se cachea |
getRows() |
array |
Solo los registros, sin status ni total, reindexados desde 0 |
getBody() |
mixed |
Respuesta ya decodificada por php-curl-class |
getBodyRaw() |
?string |
Cuerpo crudo |
Extras por clase:
| Clase | Extra |
|---|---|
PropertyStoreResponse |
getIdProperty(): ?int |
SearchPropertysResponse |
getTotal(): ?int · getProperties(): array |
MediaStoreResponse |
getIdImage(): ?int |
UserGetAllResponse |
getUsers(): array |
Excepciones
| Excepción | Cuándo |
|---|---|
Exceptions\WasiException |
Base de todas. Captúrala para atrapar cualquier fallo del SDK |
Exceptions\ConfigException |
Faltan credenciales o no llamaste a setConfig() |
Exceptions\RequestException |
El payload no es serializable, falta el id_property, o el archivo de imagen no sirve |
Las excepciones cubren errores de programación o de entorno, detectables antes de salir a la
red. Los errores de la API (credenciales incorrectas, campo rechazado, inmueble inexistente)
no lanzan: llegan en la respuesta y se leen con isOk() y getErrorMessage().
8. Campos de un inmueble
Estos son los campos modificables de la API de Wasi, es decir, los que puedes mandar en
propertyStore() y propertyUpdate().
Obligatorios en la práctica
| Campo | Tipo | Notas |
|---|---|---|
id_user |
número | Asesor responsable. Sácalo de getAllUsers() |
id_property_type |
número | Casa, apartamento, lote… (v1/property-type/all) |
id_country / id_region / id_city |
número | Jerarquía de ubicación |
for_sale / for_rent / for_transfer |
booleano | Al menos uno debe ser true |
sale_price y/o rent_price |
número | Coherente con los flags anteriores |
id_currency |
número | Moneda del precio |
title |
texto | Título público del anuncio |
Ubicación
| Campo | Tipo |
|---|---|
id_country, id_region, id_city, id_location, id_zone |
número |
zone |
texto (nombre libre de la zona) |
address |
texto |
zip_code |
texto |
latitude, longitude |
texto |
id_publish_on_map |
número (cómo se publica en el mapa) |
Precio y moneda
| Campo | Tipo |
|---|---|
sale_price |
número |
rent_price |
número |
maintenance_fee |
número (administración) |
id_currency |
número |
id_rents_type |
número (tipo de arriendo) |
Áreas
| Campo | Tipo |
|---|---|
area + id_unit_area |
número + número |
built_area + id_unit_built_area |
número + número |
private_area + id_unit_private_area |
número + número |
Cada área lleva su unidad aparte (m², ha, ft²…). Si mandas
areasinid_unit_area, Wasi asume la unidad por defecto de la empresa.
Características físicas
| Campo | Tipo | Rango |
|---|---|---|
bedrooms |
número | 0–29 |
bathrooms |
número | 0–9 |
half_bathrooms |
número | — |
garages |
número | 0–29 |
floor |
número | 1–60 |
stratum |
número | Estrato socioeconómico (Colombia) |
building_date |
número | Año de construcción |
id_property_condition |
número | Nuevo / usado / sobre planos |
Publicación y contenido
| Campo | Tipo | Notas |
|---|---|---|
observations |
texto | Descripción larga del anuncio |
comment |
texto | Nota interna (no pública) |
video |
URL | Video del inmueble |
id_status_on_page |
número | Estado en la web (activo, inactivo, vendido…) |
id_availability |
número | Disponibilidad |
reference |
texto | Tu referencia interna — úsala para reconciliar |
label |
texto | Etiqueta |
network_share |
booleano | Compartir en la red Wasi |
tv_share |
booleano | Compartir en Wasi TV |
features |
array | IDs de características (piscina, ascensor…) |
portals |
array | IDs de portales donde publicar |
✅
featuresyportalsson arrays y el SDK ya los maneja. Wasi los espera en la query string (features[]=2&features[]=4), no en el cuerpo JSON; el SDK los separa solo. Tú los pasas como un array normal:'features' => [2, 4].
La tabla completa y actualizada está en la referencia de campos de Wasi.
9. Filtros de búsqueda
Todos opcionales. Van dentro de ['filters' => [...]].
Coincidencia y texto
| Filtro | Tipo | Descripción |
|---|---|---|
match |
texto | Búsqueda por palabra clave |
title |
texto | Filtra por título |
id_property |
número | Un inmueble concreto |
Ubicación
| Filtro | Tipo | Descripción |
|---|---|---|
id_country |
número | País |
id_region |
número | Departamento / provincia |
id_city |
número | Ciudad |
id_zone |
texto | Acepta varias zonas separadas por coma: '12,15,20' |
Tipo y negocio
| Filtro | Tipo | Descripción |
|---|---|---|
id_property_type |
número | Tipo de inmueble |
for_sale |
booleano | En venta |
for_rent |
booleano | En arriendo |
for_transfer |
booleano | En traspaso |
id_status_on_page |
número | Estado de publicación |
id_user |
número | Inmuebles de un asesor |
Rangos
| Filtro | Tipo |
|---|---|
min_price / max_price |
número |
min_area / max_area |
número |
min_bedrooms / max_bedrooms |
número |
building_date |
número (año mínimo de construcción) |
Paginación y orden
| Parámetro | Tipo | Por defecto | Notas |
|---|---|---|---|
skip |
número | 0 |
Offset |
take |
número | 10 |
Máximo 100 |
order |
texto | desc |
asc | desc |
order_by |
texto | id_property |
id_property, title, id_country, id_region, id_city, id_property_type, sale_price, rent_price, visits, created_at |
short |
booleano | false |
true omite galerías y características |
Ejemplo: paginar el catálogo completo
$skip = 0; $take = 100; // el máximo permitido $todos = []; do { $res = $wasi->searchPropertys([ 'id_status_on_page' => 1, 'short' => true, // sin galerías: mucho más rápido 'take' => $take, 'skip' => $skip, 'order_by' => 'id_property', 'order' => 'asc', // orden estable al paginar ]); if (!$res->isOk()) { throw new RuntimeException('Wasi: ' . $res->getErrorMessage()); } $pagina = $res->getProperties(); $todos = array_merge($todos, $pagina); $skip += $take; } while (count($pagina) === $take); echo count($todos) . " inmuebles descargados\n";
✅ Booleanos: pásalos como
true/falsede PHP. El SDK los serializa atrue/falseen la query string, que es lo que Wasi lee. (http_build_query()convertiríafalseen cadena vacía y el filtro dejaría de aplicarse; por eso el SDK no lo usa.)
10. Manejo de respuestas y errores
Wasi devuelve HTTP 200 incluso cuando la operación falla. El resultado real está en el campo
status del cuerpo:
{ "status": "error", "code": "0", "message": "id_company or wasi_token is wrong." }
isOk() cubre los dos casos
Desde la v2, isOk() devuelve true solo si cURL no falló y el cuerpo trae
status: success. Es lo único que necesitas comprobar:
$response = $wasi->propertyStore($payload); if (!$response->isOk()) { error_log('Wasi: ' . $response->getErrorMessage()); return; } $idProperty = $response->getIdProperty();
getErrorMessage() elige el mensaje más útil disponible, en este orden:
- El
messageque devuelve Wasi. - El error de cURL, si el fallo fue de transporte.
Wasi devolvió una respuesta que no es JSON válido, si el cuerpo no se pudo decodificar.
Y devuelve null cuando la operación fue correcta, así que también sirve como aserción.
Distinguir un fallo de red de un rechazo de Wasi
Útil para decidir si reintentar: un timeout se reintenta, un campo rechazado no.
if (!$response->isOk()) { if (!$response->isTransportOk()) { throw new NetworkException($response->getErrorMessage()); // reintentable } throw new ValidationException($response->getErrorMessage()); // no reintentable }
| Método | Qué comprueba |
|---|---|
isOk() |
Transporte y status del cuerpo |
isTransportOk() |
Solo que la petición HTTP se completara |
getStatus() |
El valor literal de status |
getErrorCode() |
El code que devuelve Wasi |
getHttpStatusCode() |
El código HTTP real |
Prefieres excepciones
El SDK no lanza ante errores de la API, para que decidas tú. Si te va mejor un flujo con excepciones, envuélvelo una vez:
use Homlity\WASI\SDK\InfraStructure\Responses\ResponseFather; /** @throws RuntimeException */ function wasiOk(ResponseFather $res): array { if (!$res->isOk()) { throw new RuntimeException(sprintf( 'Wasi [%s]: %s', $res->getErrorCode() ?? 'sin código', $res->getErrorMessage() ?? 'error desconocido' )); } return $res->getBodyArray() ?? []; }
Lo tienes listo en examples/bootstrap.php.
Errores frecuentes de Wasi
| Mensaje | Causa habitual | Solución |
|---|---|---|
id_company or wasi_token is wrong. |
Credenciales mal copiadas, token regenerado, o plan no-Pro | Regenera el token en Configuración → Ajustes generales. Los setters ya recortan espacios |
| Campo no modificable | Enviaste un campo calculado (*_label, galleries…) |
Envía solo campos modificables (§8) |
| Inmueble no encontrado | id_property de otra empresa o borrado |
Verifica con searchPropertys(['id_property' => $id]) |
| Respuesta vacía / no-JSON | Timeout de red o mantenimiento de Wasi | getBodyArray() devuelve null e isOk() es false. Reintenta con setRetries() |
Timeouts y reintentos
Los valores por defecto son 30 s de petición y 10 s de conexión, y no hay reintentos.
Se ajustan en ConfigSDK:
$config ->setTimeout(60) // lotes grandes o redes lentas ->setConnectTimeout(5) // falla rápido si Wasi no responde ->setRetries(2); // reintenta ante fallo de transporte o 5xx
Los reintentos los aplica
php-curl-classa nivel de transporte. Un{"status":"error"}de Wasi no se reintenta —ni debe—: es una respuesta válida a una petición inválida.
Cache de Wasi
La API de Wasi cachea algunas peticiones clave. Tras crear o actualizar un inmueble, puede
tardar algunos minutos en aparecer con los datos nuevos en searchPropertys().
No lo interpretes como un fallo de escritura: si el add/update devolvió success, se guardó.
11. Integración con Laravel
El SDK no trae service provider. Regístralo tú — son 15 líneas.
config/wasi.php
<?php return [ 'id_company' => env('WASI_ID_COMPANY'), 'token' => env('WASI_TOKEN'), ];
app/Providers/WasiServiceProvider.php
<?php namespace App\Providers; use Homlity\WASI\SDK\App\ConfigSDK; use Homlity\WASI\SDK\App\WasiSDKFacade; use Illuminate\Support\ServiceProvider; class WasiServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton(ConfigSDK::class, function ($app) { return (new ConfigSDK( $app['config']->get('wasi.id_company'), $app['config']->get('wasi.token'), )) ->setTimeout(30) ->setRetries(2); }); $this->app->singleton(WasiSDKFacade::class, fn ($app) => new WasiSDKFacade($app->make(ConfigSDK::class))); } }
Regístralo en bootstrap/providers.php (Laravel 11+) o config/app.php (Laravel ≤10), y ya
puedes inyectarlo:
use Homlity\WASI\SDK\App\WasiSDKFacade; class PropertyController extends Controller { public function index(WasiSDKFacade $wasi) { $result = $wasi->searchPropertys(['for_sale' => true, 'short' => true, 'take' => 24]); abort_if(!$result->isOk(), 502, $result->getErrorMessage()); return view('propiedades.index', [ 'propiedades' => $result->getProperties(), 'total' => $result->getTotal(), ]); } }
Job para publicar en segundo plano (recomendado: las subidas de imagen son lentas):
<?php namespace App\Jobs; use App\Models\Inmueble; use Homlity\WASI\SDK\App\WasiSDKFacade; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class PublicarInmuebleEnWasi implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public int $tries = 3; public int $backoff = 30; public function __construct(private readonly int $inmuebleId) {} public function handle(WasiSDKFacade $wasi): void { $inmueble = Inmueble::findOrFail($this->inmuebleId); $res = $wasi->propertyStore($inmueble->toWasiPayload()); if (!$res->isOk()) { throw new \RuntimeException('Wasi rechazó el inmueble: ' . $res->getErrorMessage()); } $inmueble->update(['wasi_id' => $res->getIdProperty()]); foreach ($inmueble->fotos as $i => $foto) { $wasi->mediaStore( storage_path('app/' . $foto->path), $inmueble->wasi_id, position: $i + 1 ); } } }
⚠️ Cuidado con el log de Laravel. Si atrapas una excepción de cURL y la vuelcas entera, el
wasi_tokenpuede acabar enstorage/logs/laravel.log. Loguea solo el mensaje de negocio.
12. Receta completa: sincronizar un inmueble de tu CRM a Wasi
El flujo de extremo a extremo que usa Homlity:
1. getAllUsers() → elegir el id_user del asesor
2. (catálogos) → resolver id_city, id_property_type, id_currency
3. propertyStore() → crear el inmueble → guardar id_property
4. mediaStore() × N → subir las fotos
5. propertyUpdate() → activar la publicación / ajustar precio
6. searchPropertys() → verificar (recuerda el cache de Wasi)
<?php require __DIR__ . '/vendor/autoload.php'; use Homlity\WASI\SDK\App\ConfigSDK; use Homlity\WASI\SDK\App\WasiSDKFacade; use Homlity\WASI\SDK\Exceptions\RequestException; $config = new ConfigSDK(getenv('WASI_ID_COMPANY'), getenv('WASI_TOKEN')); $wasi = new WasiSDKFacade($config); // ── 1. Asesor responsable ──────────────────────────────────────────────────── $usersRes = $wasi->getAllUsers(); if (!$usersRes->isOk()) { exit('No se pudieron leer los asesores: ' . $usersRes->getErrorMessage() . PHP_EOL); } $usuarios = $usersRes->getUsers(); $asesor = null; foreach ($usuarios as $u) { if (($u['email'] ?? null) === 'asesor@miinmobiliaria.com') { $asesor = $u; break; } } $idUser = $asesor['id_user'] ?? $usuarios[0]['id_user']; // ── 2. Crear el inmueble ───────────────────────────────────────────────────── $payload = [ 'id_user' => $idUser, 'id_property_type' => 2, 'id_country' => 1, 'id_region' => 5, 'id_city' => 61, 'id_currency' => 12, 'for_sale' => true, 'sale_price' => 450000000, 'title' => 'Apartamento con vista al parque', 'address' => 'Cra 45 #23-10', 'area' => 78, 'built_area' => 72, 'bedrooms' => 3, 'bathrooms' => 2, 'garages' => 1, 'floor' => 7, 'stratum' => 4, 'observations' => 'Remodelado en 2024. Cocina abierta, balcón con vista al parque.', 'latitude' => '6.244203', 'longitude' => '-75.581212', 'reference' => 'HOM-2026-0451', 'id_status_on_page' => 1, ]; $createRes = $wasi->propertyStore($payload); if (!$createRes->isOk()) { exit('Error creando el inmueble: ' . $createRes->getErrorMessage() . PHP_EOL); } $idProperty = $createRes->getIdProperty(); echo "Inmueble creado en Wasi con id {$idProperty}\n"; // 👉 Aquí: guarda $idProperty en tu base de datos junto al registro local. // ── 3. Subir las fotos ─────────────────────────────────────────────────────── $fotos = glob(__DIR__ . '/fotos/HOM-2026-0451/*.jpg') ?: []; sort($fotos); // la primera será la principal $subidas = 0; foreach ($fotos as $i => $foto) { try { // El SDK valida el archivo; si no sirve, lanza RequestException $res = $wasi->mediaStore($foto, $idProperty, position: $i + 1); } catch (RequestException $e) { echo ' ✗ ' . basename($foto) . ': ' . $e->getMessage() . "\n"; continue; } if ($res->isOk()) { $subidas++; echo ' ✓ ' . basename($foto) . "\n"; } else { echo ' ✗ ' . basename($foto) . ': ' . $res->getErrorMessage() . "\n"; } usleep(300_000); } echo "{$subidas}/" . count($fotos) . " fotos subidas\n"; // ── 4. Ajuste posterior ────────────────────────────────────────────────────── $wasi->propertyUpdate($idProperty, [ 'id_status_on_page' => 1, 'comment' => 'Sincronizado desde Homlity el ' . date('Y-m-d H:i'), ]); // ── 5. Verificar (el cache de Wasi puede tardar minutos) ───────────────────── $check = $wasi->searchPropertys(['id_property' => $idProperty]); echo 'Coincidencias en búsqueda: ' . ($check->getTotal() ?? 0) . PHP_EOL;
13. Catálogos de Wasi (IDs que necesitas)
Los payloads de inmueble se construyen con IDs (id_city, id_property_type, id_currency…).
Este SDK todavía no expone métodos para leer esos catálogos, pero son peticiones GET
sencillas con las mismas credenciales:
| Catálogo | Endpoint |
|---|---|
| Países | GET v1/location/all-countries |
| Un país | GET v1/location/country/{id_country} |
| Ciudades de una región | GET v1/location/cities-from-region/{id_region} |
| Una ciudad | GET v1/location/city/{id_city} |
| Ciudades con inmuebles | GET v1/location/cities-with-property |
| Tipos de inmueble | GET v1/property-type/all |
| Un inmueble concreto | GET v1/property/get/{id_property} |
| Un usuario | GET v1/user/get/{id_user} |
| Eliminar imagen | POST v1/property/remove-image/{id_property}?id={id_image} |
| Editar datos de imagen | POST v1/gallery/image/update-data/{id_image} |
Helper para consultarlos mientras no estén en el SDK:
function wasiGet(string $endpoint, ConfigSDK $config, array $query = []): array { $url = 'https://api.wasi.co/' . ltrim($endpoint, '/') . '?' . http_build_query(array_merge([ 'id_company' => $config->getIdCompany(), 'wasi_token' => $config->getToken(), ], $query)); $curl = new \Curl\Curl(); $curl->get($url); return json_decode((string) $curl->rawResponse, true) ?: []; } // Uso $tipos = wasiGet('v1/property-type/all', $config); foreach ($tipos as $k => $t) { if (is_numeric($k)) { echo $t['id_property_type'] . ' → ' . $t['nombre'] . PHP_EOL; } }
Ejemplo de respuesta de tipos de inmueble:
{
"0": { "id_property_type": "1", "nombre": "Casa" },
"1": { "id_property_type": "2", "nombre": "Apartamento" }
}
💡 Los catálogos cambian poquísimo. Cachéalos (Redis, archivo, tabla) en lugar de consultarlos en cada sincronización.
Documentación oficial completa: https://api.wasi.co/docs/guide/
14. Limitaciones conocidas y buenas prácticas
Limitaciones actuales
| Limitación | Impacto | Workaround |
|---|---|---|
| Solo 5 endpoints cubiertos | Catálogos, property/get y borrado quedan fuera |
Helper wasiGet() de §13 |
| No valida el contenido del payload | Un id_city inexistente lo rechaza Wasi, no el SDK |
Cachea los catálogos y valida contra ellos |
| Sin cache propia | Cada llamada sale a la red | Cachea tú los catálogos (§13) |
| Credenciales en query string | Es como Wasi documenta el acceso GET; el token puede filtrarse a logs | Nunca loguees URLs; redacta wasi_token en tu APM |
| Sin cliente HTTP inyectable | No puedes mockear cURL desde fuera | Envuelve el SDK en un repositorio tuyo y mockea ese (docs/laravel.md) |
Arreglado en la v2
| Antes (v1) | Ahora (v2) |
|---|---|
isOk() solo miraba si cURL falló |
Comprueba transporte y status del cuerpo |
Filtros fuera de ['filters' => …] se ignoraban en silencio |
Ambas formas funcionan |
false en un filtro se serializaba como cadena vacía |
Se serializa como false |
features/portals no llegaban a Wasi |
Se separan solos y viajan en la query string |
mediaStore() con ruta inválida mandaba un adjunto vacío |
Lanza RequestException antes de salir a la red |
| Sin timeouts: una petición colgada bloqueaba el request | 30 s / 10 s por defecto, configurables |
| Sin reintentos | setRetries() |
| Credenciales ausentes → error opaco de Wasi | ConfigException con un mensaje claro |
| Sin tests | Suite PHPUnit — composer test |
Buenas prácticas
- Guarda el
id_propertyque devuelvepropertyStore()junto a tu registro local. Sin él no puedes actualizar ni subir fotos. - Usa
referencepara escribir tu ID interno dentro de Wasi: reconciliación en ambos sentidos. - Busca antes de crear por tu
reference: hace tus jobs idempotentes ante reintentos. - Cachea los catálogos (ciudades, tipos, monedas). Casi nunca cambian.
- Usa
'short' => trueen búsquedas de listado: la respuesta se reduce drásticamente. - Pagina con
take= 100 (el máximo) para minimizar peticiones. - Sube las fotos en background. Son la parte lenta del flujo y hacen fallar peticiones HTTP síncronas.
- Espacia las subidas (
usleep) en lotes grandes. - No confíes en leer justo después de escribir: el cache de Wasi puede tardar minutos.
- Castea los números: Wasi devuelve muchos IDs como cadenas (
"id_property": "123456").
15. Troubleshooting
id_company or wasi_token is wrong.
- Confirma que tu cuenta de Wasi está en plan Pro — la API no existe en planes inferiores.
- Los espacios y saltos de línea al copiar del
.envya los recorta el SDK. - Regenera el token en Configuración → Ajustes generales → API de Wasi. Regenerarlo invalida el anterior: actualízalo en todos tus entornos.
- Si olvidas una credencial, el SDK lanza
ConfigExceptionantes de salir a la red; si ves este mensaje de Wasi es que las dos llegaron, pero alguna es incorrecta.
`searchPropertys()` ignora mis filtros
En la v1 había que anidarlos bajo filters o se descartaban en silencio. En la v2 ambas
formas funcionan, así que si sigues viendo el problema, actualiza:
composer update homlity/sdk-wasi-php8
Si ya estás en v2, revisa los nombres de los filtros contra §9: un filtro con un nombre que Wasi no conoce simplemente no filtra.
`getTotal()` devuelve `null`
El cuerpo no traía la clave total, lo que casi siempre indica error. Comprueba primero
isOk() y lee getErrorMessage().
En la v1 este método devolvía
false; ahora devuelve?int, así que$res->getTotal() ?? 0es la forma segura de usarlo en una suma o unprintf.
La imagen se sube pero no aparece en el inmueble
- Si el archivo no sirve, el SDK lanza
RequestExceptioncon el motivo exacto. Si no lanza, el archivo llegó bien. - Revisa
upload_max_filesizeypost_max_sizeen tuphp.ini. - Recuerda el cache de Wasi: puede tardar minutos en reflejarse en la web pública.
`Class "Curl\Curl" not found`
Falta la dependencia o el autoload. Ejecuta composer install y asegúrate de requerir
vendor/autoload.php antes de instanciar nada del SDK.
Timeouts en producción
Ajusta ConfigSDK:
$config->setTimeout(60)->setConnectTimeout(5)->setRetries(2);
Para lotes grandes, mueve además las llamadas a una cola en lugar de ejecutarlas dentro de un request HTTP.
Los acentos y la ñ salen mal
Confirma que ext-mbstring está cargado y que tus textos llegan en UTF-8. Si no lo están, el
SDK lanza RequestException mencionando UTF-8 en lugar de enviar un payload roto. Sanea con
mb_convert_encoding($texto, 'UTF-8', 'UTF-8') si vienen de fuentes dudosas.
Los acentos y la ñ se envían sin escapar (JSON_UNESCAPED_UNICODE), tal cual los guarda Wasi.
16. Ejemplos ejecutables y tests
En examples/ encontrarás scripts listos para correr:
| Script | Qué hace |
|---|---|
| examples/bootstrap.php | Configuración compartida y el helper wasiOk() |
| examples/list_users.php | Lista los asesores y sus id_user |
| examples/search_properties.php | Búsqueda con filtros y paginación completa |
| examples/create_property.php | Crea un inmueble y muestra el id_property |
| examples/update_property.php | Cambia precio y estado de publicación |
| examples/upload_images.php | Sube todas las fotos de una carpeta |
| examples/catalogs.php | Consulta países, ciudades y tipos de inmueble |
| examples/full_sync.php | Flujo completo: crear → fotos → publicar → verificar |
export WASI_ID_COMPANY=1234567890 export WASI_TOKEN=ABCD_EFGH_IJKL_MNOP php examples/list_users.php php examples/search_properties.php
Ejecutar los tests
La suite es de unidad: no toca la red, así que corre en cualquier sitio sin credenciales.
composer install
composer test
Cubre la lógica que rodea a la API: validación del status, normalización de filtros,
construcción de la query string (booleanos y arrays), validación de archivos y de credenciales.
Las respuestas se simulan con tests/Support/FakeCurl.php, un doble
de \Curl\Curl con las mismas propiedades públicas.
17. Extender el SDK
Añadir un endpoint son tres pasos. Ejemplo: obtener un inmueble (v1/property/get/{id}).
1. La petición — src/InfraStructure/Requets/PropertyGetRequest.php
<?php namespace Homlity\WASI\SDK\InfraStructure\Requets; use Curl\Curl; use Homlity\WASI\SDK\InfraStructure\Responses\PropertyGetResponse; class PropertyGetRequest extends RequestsFather { private $idProp; public function setId(string $idProp) { $this->idProp = $idProp; } public function getEndpoint(): string { return "v1/property/get/" . $this->idProp; } public function run() { $curl = new Curl(); $curl->get($this->buildUrl()); return new PropertyGetResponse($curl); } }
2. La respuesta — src/InfraStructure/Responses/PropertyGetResponse.php
<?php namespace Homlity\WASI\SDK\InfraStructure\Responses; class PropertyGetResponse extends ResponseFather { public function getGalleries() { $body = $this->getBodyArray(); return $body['galleries'] ?? []; } }
3. El método del facade — en src/App/WasiSDKFacade.php
public function propertyGet(string $idProp) { $request = new PropertyGetRequest(); $request->setConfig($this->config); $request->setId($idProp); return $request->run(); }
newCurl() ya aplica los timeouts y reintentos de ConfigSDK, y buildUrl() acepta parámetros
extra, así que tu petición nueva hereda ese comportamiento sin hacer nada.
Otras extensiones útiles
- Logging: inyecta un
Psr\Log\LoggerInterfaceenRequestsFathery loguea endpoint +getHttpStatusCode()— nunca la URL completa. - Un endpoint que devuelva algo con forma propia: añade el helper a tu clase
Response, como hacengetIdProperty()ogetProperties(). - Tests de tu extensión: hereda el patrón de tests/Unit/ —
FakeCurlte permite probar la respuesta sin red.
18. Soporte y contribución
| Recurso | Enlace |
|---|---|
| 🏠 Homlity | https://homlity.com/ |
| 👩💻 Portal de desarrolladores | https://homlity.com/desarrolladores/ |
| 📦 Packagist | https://packagist.org/packages/homlity/sdk-wasi-php8 |
| 🐙 GitHub | https://github.com/homlity/sdk-wasi-php8 |
| 📘 API oficial de Wasi | https://api.wasi.co/docs/guide/ |
| 📗 Referencia de la API (este repo) | docs/wasi-api-reference.md |
| 🅻 Guía de Laravel (este repo) | docs/laravel.md |
| 🧾 Campos de inmueble (Wasi) | https://api.wasi.co/docs/guide/fields/property.html |
Contribuir
- Haz fork y crea una rama desde
main. - Mantén el estilo existente: una clase
Request+ unaResponse+ un método de facade. - No cambies namespaces públicos (incluido
Requets) sin una versión mayor. - Abre el PR describiendo el endpoint de Wasi que cubre y enlazando su documentación.
Otros SDK de Homlity
| Paquete | Para qué sirve |
|---|---|
homlity/sdk-wasi-php8 |
CRM Wasi — este paquete |
homlity/sdk-proppit |
Portal Proppit (LIFULL Connect) — publicación de anuncios en PHP/Laravel |
Hecho por Homlity · ¿Construyendo sobre nuestros SDK? Escríbenos desde el portal de desarrolladores.