Documentación de la API de generación de planos de planta#
URL base:
https://api.ideal.house
Versión: v1
Actualizado: 2026-08-09
📖 Descripción general#
API de generación de planos de planta crea un plano de planta conceptual residencial, en blanco y negro, visto desde arriba y con estilo CAD, generado por IA, a partir de requisitos estructurados de habitaciones y un prompt personalizado o imagen de referencia opcionales.
El resultado está destinado a la exploración temprana de la distribución. No es un dibujo de construcción, y las dimensiones generadas, la geometría, la colocación de accesorios y el cumplimiento normativo deben ser revisados por un profesional cualificado.
El flujo de trabajo es asíncrono:
- Crear una tarea: envía los parámetros del plano de planta y recibe un
taskId. - Consultar resultados: consulta el punto de conexión de resultados con el
taskIdhasta que la tarea alcance un estado terminal.
🔐 Autenticación#
Todas las solicitudes de API públicas deben incluir una clave API.
| Cabecera | Obligatorio | Valor |
|---|---|---|
APIKEY | ✅ Sí | Tu clave API |
Content-Type | ✅ Sí para POST | application/json |
[!WARNING] Mantén tu clave API segura. No la expongas en código del lado del cliente ni en repositorios públicos.
💰 Deducción de créditos#
Los créditos se descuentan después de que una tarea de generación se haya creado correctamente. Si la tarea finalmente falla, los créditos descontados se reembolsan automáticamente. La insuficiencia de créditos genera el código de error 9051.
Modelo (modelType) | Tamaño de salida | Créditos |
|---|---|---|
Base | 1536 × 1024 | 10 |
Pro | 2496 × 1664 | 20 |
Flash no es compatible con Floor Plan API.
Consulta Referencia de deducción de créditos para conocer el comportamiento habitual de facturación.
📌 Endpoints de la API#
1. Crear tarea de plano de planta#
Crea una tarea de generación de plano de planta y devuelve un ID de tarea único.
Endpoint
POST /api/v1/floorPlan/generate
Cabeceras de solicitud
| Encabezado | Obligatorio | Descripción |
|---|---|---|
APIKEY | ✅ Sí | Clave de autenticación API |
Content-Type | ✅ Sí | Debe ser application/json |
Cuerpo de la solicitud#
| Campo | Tipo | Obligatorio | Descripción | Predeterminado |
|---|---|---|---|---|
bedrooms | integer | ❌ No | Cantidad de dormitorios de 0 a 5 | 2 |
bathrooms | number | ❌ No | Cantidad total de baños de 0.5 a 4, en incrementos de 0.5 | 1.5 |
totalArea | string | ✅ Sí | Área total objetivo positiva con unidad m² o ft², como 220 m² o 1386 ft² | — |
bedroomAreaRanges | array<object> | ❌ No | Orientación opcional del tamaño de los dormitorios. Ver rangos de área de dormitorios | Se deriva de totalArea cuando se omite |
bathroomDetails | object | ❌ No | Preferencias solo para baños completos. Ver detalles del baño | — |
kitchenDetails | object | ❌ No | Configuración opcional de la cocina. Ver detalles de la cocina | — |
keyRooms | array<string> | ❌ No | Habitaciones o espacios adicionales. Ver habitaciones clave | [] |
prompt | string | ❌ No | Prioridades de diseño adicionales. No puede anular los conteos estructurados ni las restricciones visuales fijas | "" |
refImageUrl | string | ❌ No | URL de la imagen de referencia accesible públicamente | "" |
modelType | string | ❌ No | Enum: Base, Pro | Base |
[!IMPORTANT] La API pública actual valida
bedroomscomo0–5ybathroomscomo0.5–4. Los valores disponibles en otra interfaz de cliente no amplían estos límites del lado del servidor.
Reglas generales de solicitud#
- Todos los valores enum son sensibles a mayúsculas y deben usar los valores en inglés que se muestran en este documento.
totalAreaes un área total objetivo utilizada para guiar la escala y las proporciones; no se trata como una dimensión de construcción exacta.- El prompt personalizado efectivo está limitado a los primeros 800 caracteres cuando se ensambla el prompt de imagen estructurado.
- Los campos estructurados tienen prioridad sobre las instrucciones contradictorias en
prompt. - Una tarea exitosa genera exactamente una imagen.
📐 Área total#
totalArea contiene un valor numérico positivo seguido de una unidad de área.
| Unidad | Ejemplo |
|---|---|
m² | 220 m² |
ft² | 1386 ft² |
Se recomienda un espacio en blanco antes de la unidad. Se aceptan valores decimales cuando son positivos.
Ejemplos válidos:
{
"totalArea": "200 m²"
}
{
"totalArea": "1850 ft²"
}
🛏️ Rangos de área de dormitorios#
bedroomAreaRanges proporciona orientación relativa del tamaño del dormitorio. No solicita etiquetas de área numéricas en la imagen generada.
Cada elemento tiene la siguiente estructura:
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | ❌ No | Identidad del dormitorio, por ejemplo Room 1 (Master) o Room 2 |
minArea | string | ❌ No | Área mínima positiva |
maxArea | string | ❌ No | Área máxima positiva; no puede ser menor que minArea |
unit | string | ❌ No | Enum: m², ft²; usa la misma unidad que totalArea |
Ejemplo de rango explícito
{
"bedroomAreaRanges": [
{
"name": "Room 1 (Master)",
"minArea": "30",
"maxArea": "40",
"unit": "m²"
},
{
"name": "Room 2",
"minArea": "20",
"maxArea": "30",
"unit": "m²"
}
]
}
Reglas cuando se proporciona una matriz no vacía:
- Su longitud debe ser igual a
bedrooms. - Cada
minAreaymaxAreasuministrado debe ser una cadena numérica positiva. - Cuando ambos valores se suministran,
minArea <= maxArea. unit, cuando se suministra, debe serm²oft².- Los nombres se conservan. Los elementos vacíos o nulos no proporcionan orientación de tamaño.
Rangos automáticos cuando se omite#
El campo puede omitirse o enviarse como una matriz vacía. Cuando ningún elemento contiene un minArea o maxArea efectivo, la vía de generación estructurada deriva rangos internos de dormitorio a partir de totalArea y bedrooms:
- El presupuesto de área del dormitorio comienza en 20% del área total para un dormitorio.
- El presupuesto aumenta en 7.5 puntos porcentuales por cada dormitorio adicional, con un tope en 50%.
- El primer dormitorio recibe un peso de tamaño de
1.3; cada otro dormitorio recibe un peso de1.0. - Cada objetivo se convierte en un rango aproximado de
±10%, redondeado a unidades de área enteras. - La unidad se hereda de
totalArea. - Los nombres existentes no vacíos de las habitaciones se conservan; de lo contrario, el servidor usa
Room 1,Room 2, y así sucesivamente.
Para 200 m² y 4 dormitorios, la orientación derivada actual es aproximadamente:
[
{ "name": "Room 1", "minArea": "23", "maxArea": "28", "unit": "m²" },
{ "name": "Room 2", "minArea": "18", "maxArea": "22", "unit": "m²" },
{ "name": "Room 3", "minArea": "18", "maxArea": "22", "unit": "m²" },
{ "name": "Room 4", "minArea": "18", "maxArea": "22", "unit": "m²" }
]
Estos valores son orientación proporcional interna, no áreas finales garantizadas de la habitación. Los rangos válidos explícitos siempre tienen prioridad sobre los rangos automáticos.
Cuando bedrooms es 0, omite bedroomAreaRanges o envía [].
🛁 Detalles del baño#
bathrooms representa el recuento total de baños:
- Su parte entera es el número de baños completos.
- Una fracción
.5añade un medio baño. - Se solicita que cada baño completo incluya un inodoro, mueble de lavabo/lavabo, y ducha o zona húmeda.
- Un medio baño contiene solo un inodoro y mueble de lavabo/lavabo, sin ducha ni bañera.
bathroomDetails solo configura baños completos:
{
"bathroomDetails": {
"fullBathroomOptions": [
{
"name": "Bathroom 1",
"wetDrySeparation": "yes",
"bathtub": "required"
},
{
"name": "Bathroom 2",
"wetDrySeparation": "no",
"bathtub": "optional"
}
]
}
}
| Campo | Tipo | Valores permitidos | Descripción |
|---|---|---|---|
name | string | Bathroom 1, Bathroom 2, etc. | Identidad visual opcional |
wetDrySeparation | string / null | yes, no, null | Si mostrar una zona húmeda separada |
bathtub | string / null | no, optional, required, null | Preferencia de bañera |
Reglas:
fullBathroomOptions.lengthno puede excederfloor(bathrooms).- La matriz solo puede contener los baños completos para los que se hayan seleccionado preferencias.
- Un valor de
nullsignifica no especificado. - Una bañera requerida es adicional a los accesorios estándar del baño completo; no reemplaza el inodoro ni la ducha.
- La separación húmeda/seca es una partición interna dentro de un baño contado, no un baño adicional.
🍳 Detalles de la cocina#
Todos los campos secundarios de kitchenDetails son opcionales. Omite todo el objeto cuando no se seleccione preferencia de cocina.
{
"kitchenDetails": {
"type": "open",
"size": "standard",
"layout": "U",
"islandType": "preparation",
"storage": "maximum",
"features": ["breakfast nook", "pantry"]
}
}
| Campo | Tipo | Valores permitidos |
|---|---|---|
type | string | open, semi-open, closed |
size | string | small, standard, large, extra large |
layout | string | I, L, U, gallery |
islandType | string | no, preparation, cooking, entertainment |
storage | string | minimal, standard, maximum |
features | array<string> | eating bar, breakfast nook, pantry |
La configuración parcial es válida. Por ejemplo:
{
"kitchenDetails": {
"type": "semi-open"
}
}
🚪 Habitaciones clave#
keyRooms acepta una matriz de estos valores exactos:
| Valor | Descripción |
|---|---|
walk-in closet | Vestidor independiente conectado a una zona de dormitorio |
laundry room | Espacio dedicado para lavandería |
storage room | Habitación de almacenamiento general |
utility room | Habitación mecánica o de servicio |
home office | Oficina o estudio dedicado |
garage | Garaje con apertura exterior para vehículos y acceso interno al hogar |
pantry | Despensa adyacente a la cocina |
combined living-dining | Una zona compartida de estar y comedor |
balcony | Balcón exterior conectado al espacio de estar o al dormitorio principal |
El valor heredado de Web balcon también se acepta y se normaliza a balcony.
Reglas:
- Los valores vacíos se ignoran y los valores duplicados se eliminan.
- Las habitaciones clave seleccionadas se solicitan una sola vez.
- Los espacios opcionales no seleccionados se excluyen del distribución de habitaciones generada.
- Si
pantryaparece tanto enkitchenDetails.featurescomo enkeyRooms, solo se solicita una despensa.
Ejemplo:
{
"keyRooms": [
"garage",
"home office",
"combined living-dining"
]
}
🖼️ Imagen de referencia#
refImageUrl es opcional y debe ser accesible directamente por el servidor de API.
Requisitos:
- Formato: JPG/JPEG, PNG o WebP.
- Tamaño máximo de archivo: 20 MB.
- Dimensiones mínimas: 128 × 128 px.
- Dimensiones máximas: 6,000 × 6,000 px. Las imágenes más grandes se escalan proporcionalmente antes del procesamiento.
La imagen de referencia guía la distribución, la adyacencia, las proporciones o el estilo visual. No anula los conteos estructurados de habitaciones ni otras restricciones fijas.
🤖 Tipos de modelo#
| Valor | Descripción |
|---|---|
Base | Predeterminado. Calidad de generación equilibrada, salida de 1536 × 1024 |
Pro | Salida de mayor resolución de 2496 × 1664 con un tiempo de generación esperado más largo |
Solo se admiten Base y Pro.
Campos que no forman parte de la especificación funcional pública#
Los siguientes campos no deben ser utilizados por los clientes de API públicos:
| Campo | Notas |
|---|---|
imageNumbers | El generador actual siempre devuelve una imagen; este campo no es necesario |
extData | Metadatos de seguimiento de grupo de tareas de Web internas; los clientes públicos deben omitirlo |
isApiCall | Determinado por el punto de conexión de API, no por el cuerpo de la solicitud |
genByMember | Metadatos de generación interna, no un campo de solicitud de plano de planta |
Campos heredados eliminados que no deben enviarse:
floorplanSetting
roomCounts
grossArea
totalAreaValue
totalAreaUnit
totalAreaType
fullBathrooms
halfBathrooms
halfBathroomRequirement
kitchenType
diningRooms
livingRooms
extras
referenceImage
hasDetailOptions
📥 Ejemplos de creación de tarea#
Solicitud mínima con rangos automáticos de dormitorios#
curl -X POST "https://api.ideal.house/api/v1/floorPlan/generate" \
-H "APIKEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro",
"prompt": "Upper floor of a two-story Saudi Arabian villa with a master bedroom, family living area, staircase landing, and balcony"
}'
Solicitud completa#
cURL
curl -X POST "https://api.ideal.house/api/v1/floorPlan/generate" \
-H "APIKEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"bedrooms": 3,
"bathrooms": 2.5,
"totalArea": "220 m²",
"bedroomAreaRanges": [
{"name": "Room 1 (Master)", "minArea": "30", "maxArea": "40", "unit": "m²"},
{"name": "Room 2", "minArea": "20", "maxArea": "30", "unit": "m²"},
{"name": "Room 3", "minArea": "20", "maxArea": "30", "unit": "m²"}
],
"bathroomDetails": {
"fullBathroomOptions": [
{"name": "Bathroom 1", "wetDrySeparation": "yes", "bathtub": "required"},
{"name": "Bathroom 2", "wetDrySeparation": "no", "bathtub": "optional"}
]
},
"kitchenDetails": {
"type": "open",
"size": "standard",
"layout": "U",
"islandType": "preparation",
"storage": "maximum",
"features": ["breakfast nook", "pantry"]
},
"keyRooms": ["garage", "home office", "combined living-dining"],
"prompt": "Bright modern home with good natural lighting",
"refImageUrl": "https://example.com/reference-plan.png",
"modelType": "Pro"
}'
Java (OkHttp)
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class FloorPlanApiExample {
private static final String BASE_URL = "https://api.ideal.house";
private static final String API_KEY = "your_api_key_here";
public static void main(String[] args) throws Exception {
OkHttpClient client = new OkHttpClient();
String json = """
{
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro"
}
""";
Request request = new Request.Builder()
.url(BASE_URL + "/api/v1/floorPlan/generate")
.addHeader("APIKEY", API_KEY)
.addHeader("Content-Type", "application/json")
.post(RequestBody.create(json, MediaType.parse("application/json")))
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
}
}
Python (requests)
import requests
BASE_URL = "https://api.ideal.house"
API_KEY = "your_api_key_here"
payload = {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro",
}
response = requests.post(
f"{BASE_URL}/api/v1/floorPlan/generate",
headers={"APIKEY": API_KEY, "Content-Type": "application/json"},
json=payload,
)
response.raise_for_status()
print("Task ID:", response.json()["data"])
Node.js (axios)
const axios = require('axios');
const BASE_URL = 'https://api.ideal.house';
const API_KEY = 'your_api_key_here';
async function createFloorPlanTask() {
const response = await axios.post(
`${BASE_URL}/api/v1/floorPlan/generate`,
{
bedrooms: 4,
bathrooms: 2,
totalArea: '200 m²',
keyRooms: ['walk-in closet', 'balcony'],
prompt: 'Upper floor with a master bedroom and family living area',
modelType: 'Pro'
},
{
headers: {
APIKEY: API_KEY,
'Content-Type': 'application/json'
}
}
);
console.log('Task ID:', response.data.data);
return response.data.data;
}
createFloorPlanTask();
Respuesta de éxito de creación de tarea#
{
"code": 0,
"message": "success",
"data": 1234567890123456789
}
| Campo | Tipo | Descripción |
|---|---|---|
code | integer | 0 indica que la tarea se creó correctamente |
message | string | Mensaje de la respuesta |
data | long | ID de tarea utilizado para consultar el punto de conexión de resultados |
2. Obtener resultado de la tarea#
Devuelve el progreso de la tarea y la imagen generada cuando esté disponible.
Endpoint
GET /api/v1/floorPlan/result?taskId={taskId}
Cabeceras de solicitud
| Encabezado | Obligatorio | Descripción |
|---|---|---|
APIKEY | ✅ Sí | Clave de autenticación API |
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
taskId | long | ✅ Sí | ID de la tarea devuelto por el endpoint de creación |
Ejemplos de solicitud de resultado#
cURL
curl -X GET "https://api.ideal.house/api/v1/floorPlan/result?taskId=1234567890123456789" \
-H "APIKEY: your_api_key_here"
Sondeo con Python
import time
import requests
BASE_URL = "https://api.ideal.house"
API_KEY = "your_api_key_here"
task_id = 1234567890123456789
while True:
response = requests.get(
f"{BASE_URL}/api/v1/floorPlan/result",
headers={"APIKEY": API_KEY},
params={"taskId": task_id},
)
response.raise_for_status()
task = response.json()["data"]
print(task["status"], task["percentage"], task["waitNumber"])
if task["status"] in ("Success", "Failed", "Termination"):
break
time.sleep(3)
if task["status"] == "Success":
print("Result URL:", task["output"]["resultUrl"])
Sondeo con Node.js
const axios = require('axios');
const BASE_URL = 'https://api.ideal.house';
const API_KEY = 'your_api_key_here';
async function pollFloorPlanResult(taskId) {
while (true) {
const response = await axios.get(
`${BASE_URL}/api/v1/floorPlan/result`,
{
headers: { APIKEY: API_KEY },
params: { taskId }
}
);
const task = response.data.data;
console.log(task.status, task.percentage, task.waitNumber);
if (['Success', 'Failed', 'Termination'].includes(task.status)) {
if (task.status === 'Success') {
console.log('Result URL:', task.output.resultUrl);
}
return task;
}
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
pollFloorPlanResult('1234567890123456789');
Respuesta de tarea completada#
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Success",
"waitNumber": 0,
"percentage": 100,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"bedroomAreaRanges": [
{"name": "Room 1", "minArea": "23", "maxArea": "28", "unit": "m²"},
{"name": "Room 2", "minArea": "18", "maxArea": "22", "unit": "m²"},
{"name": "Room 3", "minArea": "18", "maxArea": "22", "unit": "m²"},
{"name": "Room 4", "minArea": "18", "maxArea": "22", "unit": "m²"}
],
"keyRooms": ["walk-in closet", "balcony"],
"prompt": "Upper floor with a master bedroom and family living area",
"modelType": "Pro"
},
"output": {
"resultUrl": "https://cdn.ideal.house/output/floor-plan.jpg",
"width": 2496,
"height": 1664
}
}
}
Respuesta de procesamiento#
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Processing",
"waitNumber": 1,
"percentage": 45,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro"
},
"output": null
}
}
Respuesta de tarea fallida#
{
"code": 0,
"message": "success",
"data": {
"id": 1234567890123456789,
"status": "Failed",
"waitNumber": 0,
"percentage": 0,
"input": {
"bedrooms": 4,
"bathrooms": 2,
"totalArea": "200 m²",
"modelType": "Pro"
},
"output": null
}
}
Campos del resultado#
| Campo | Tipo | Descripción |
|---|---|---|
id | long | ID de tarea |
status | string | Estado actual de la tarea |
waitNumber | integer | Número de tareas por delante en la cola; 0 significa que no hay tareas por delante |
percentage | integer | Porcentaje aproximado de finalización de 0 a 100 |
input | object | Entrada de tarea normalizada, incluidos los rangos de dormitorios derivados automáticamente cuando corresponda |
output | object / null | Salida generada cuando la tarea tiene éxito; de lo contrario, generalmente null |
output.resultUrl | string | URL firmada de la imagen del plano de planta generado |
output.width | integer | Ancho de salida en píxeles |
output.height | integer | Alto de salida en píxeles |
📊 Estado de la tarea#
| Estado | Descripción |
|---|---|
Unprocessed | La tarea se ha creado pero aún no ha comenzado |
Processing | La tarea se está procesando |
Success | La tarea se completó y output.resultUrl está disponible |
Failed | La tarea falló |
Termination | La tarea fue interrumpida o terminada |
Consulta cada 3–5 segundos. Consulta Límite de tareas de API.
❌ Respuestas de error#
Todas las respuestas de error utilizan la estructura de respuesta común:
{
"code": 1011,
"message": "bedroomAreaRanges size must match bedrooms",
"data": null
}
| Código | Nombre | Descripción | Acción sugerida |
|---|---|---|---|
1001 | FAILED | Falla genérica de la solicitud | Comprueba el campo message |
1003 | INTERNAL_ERROR | Error interno del servidor | Reintenta más tarde; contacta con soporte si persiste |
1011 | PARAM_ERROR | Parámetro de solicitud no válido | Verifica conteos, unidades, valores enum y matrices anidadas |
5002 | API_KEY_INVALID | Clave API no válida o faltante | Verifica la cabecera APIKEY |
9010 | SCAN_TEXT_ERROR | El prompt falló en la revisión de contenido | Revisa el prompt |
9038 | PROHIBITED_CONTENT | La salida generada contiene contenido prohibido | Ajusta las entradas y reintenta |
9051 | COINS_NOT_ENOUGH | Créditos insuficientes | Añade créditos y reintenta |
Consulta Referencia de códigos de error para la lista completa de errores comunes.
🔄 Notas de integración Web#
La aplicación Web autenticada y la API pública utilizan puntos de conexión y métodos de autenticación diferentes:
| Cliente | Punto de conexión | Autenticación |
|---|---|---|
| Aplicación Web | POST /floorPlan/generate | Cabecera de inicio de sesión token |
| API pública | POST /api/v1/floorPlan/generate | Cabecera APIKEY |
Las estructuras de los campos funcionales están alineadas, pero los clientes de API pública deben seguir los límites del lado del servidor y el contrato público de este documento. En particular:
- Los clientes Web pueden incluir
imageNumbersyextDatainternos; los clientes públicos no los necesitan. - La API pública determina los metadatos de llamada de API a partir del punto de conexión y las credenciales. Los campos de solicitud como
isApiCallygenByMemberno son necesarios. balconse acepta por compatibilidad y se normaliza abalcony; las nuevas integraciones deben enviarbalcony.- Los límites actuales del servidor público siguen siendo
0–5dormitorios y0.5–4baños, incluso si otra UI presenta selectores más amplios de manera temporal.