Toda la API está en {BASE_URL}/studio/... (privada, requiere STUDIO_KEY) o {BASE_URL}/public/artefacto/{id}/... (pública, requiere api_key del artefacto).
Convenciones
| Concepto | Valor |
|---|
| BASE_URL | https://puente-backend-721178029791.southamerica-west1.run.app/ |
| Auth privada | X-API-Key: puente_studio_xxxxxxxxxxxx |
| Auth pública | X-API-Key: puente_artifact_xxxxxxxxxxxx |
| Content-Type | application/json en POST/PUT |
Endpoints privados — Artefactos
Requieren X-API-Key: {STUDIO_KEY}.
Listar artefactos
GET {BASE_URL}/studio/artefactos
Retorna los artefactos del equipo asociado a tu STUDIO_KEY.
Obtener un artefacto
GET {BASE_URL}/studio/artefactos/{id}
Retorna el código fuente completo en formato JSON (o HTML si es legacy).
Crear artefacto
POST {BASE_URL}/studio/artefactos
Content-Type: application/json
{
"titulo": "Nombre de la app",
"descripcion": "Descripción opcional",
"equipo_id": null,
"app_content": {
"index.tsx": { "content": "// código aquí", "type": "tsx" }
}
}
Si equipo_id es null, se asigna automáticamente el equipo de la STUDIO_KEY.
Respuesta:
{
"message": "Artefacto insertado correctamente",
"request_id": "330482e2-...",
"empresa_id": 1,
"artefacto_insertado": {
"id": 545,
"titulo": "Mi Primera App",
"equipo_id": 2,
"fecha_creacion": "2026-04-30T20:21:51.588362+00:00"
},
"api_key": "puente_artifact_xxxxx"
}
Guarda id, public_id (obtenlo con /meta) y api_key inmediatamente. La api_key solo se muestra una vez.
Actualizar artefacto
PUT {BASE_URL}/studio/artefactos/{id}
Content-Type: application/json
{
"titulo": "Nuevo nombre",
"descripcion": "Nueva descripción",
"app_content": { ... TODOS los archivos ... }
}
El PUT reemplaza todo el app_content. Si envías solo los archivos modificados, los demás se eliminan permanentemente.Flujo seguro: GET → modificar en memoria → PUT con todo el contenido.
GET {BASE_URL}/studio/artefactos/{id}/meta
Respuesta:
{
"request_id": "8789ad9e-...",
"artefacto": {
"id": 541,
"titulo": "App Base",
"public_id": "788517f2-a8e1-44dc-9667-d3a48eff6972",
"equipo_id": 2,
"empresa_id": 1,
"fecha_creacion": "2026-04-30T17:59:02.631483+00:00"
}
}
La URL pública final de la app es https://app.puente.xyz/public/{public_id}/.
Endpoints privados — Tablas
Listar tablas
GET {BASE_URL}/studio/tablas
Crear tabla
POST {BASE_URL}/studio/tablas
Content-Type: application/json
{
"nombre": "pedidos",
"descripcion": "Pedidos del CRM",
"equipo_id": null,
"columnas": [
{ "key": "cliente", "label": "Cliente", "tipo": "text", "requerido": true },
{ "key": "monto", "label": "Monto", "tipo": "number", "requerido": true },
{ "key": "fecha", "label": "Fecha", "tipo": "date", "requerido": false },
{ "key": "activo", "label": "Activo", "tipo": "boolean", "requerido": false },
{ "key": "estado", "label": "Estado", "tipo": "select", "requerido": true,
"opciones": ["pendiente", "pagado", "enviado"] }
]
}
Tipos de columna:
| Tipo | Formato |
|---|
text | string |
number | number (decimal soportado) |
date | "YYYY-MM-DD" |
boolean | true / false (no strings) |
select | string de las opciones |
Actualizar tabla
PUT {BASE_URL}/studio/tablas/{tabla_id}
Content-Type: application/json
{ "nombre": "nuevo_nombre", "descripcion": "nueva descripción" }
Leer filas
GET {BASE_URL}/studio/tablas/{tabla_id}/datos?limit=500&offset=0
Máximo limit=5000. Para datasets grandes, pagina con offset.
Insertar fila
POST {BASE_URL}/studio/tablas/{tabla_id}/datos
Content-Type: application/json
{
"datos": {
"cliente": "Ana",
"monto": 1000,
"fecha": "2026-04-29",
"estado": "pendiente"
}
}
Insertar en masa (bulk)
POST {BASE_URL}/studio/tablas/{tabla_id}/datos/bulk
Content-Type: application/json
{
"filas": [
{ "cliente": "Ana", "monto": 1000 },
{ "cliente": "Luis", "monto": 2500 }
]
}
Máximo 10,000 filas por request. Atómico: si una fila falla, ninguna se inserta.
API Keys de artefactos
Cada artefacto tiene su propia api_key (puente_artifact_xxx) que le permite conectarse a tablas desde el frontend sin JWT.
Obtener configuración (sin la key en texto plano)
GET {BASE_URL}/studio/artefactos/{id}/api-key
Respuesta:
{
"id": 456,
"artefacto_id": 123,
"tipo": "artifact",
"rate_limit_config": {
"requests_per_minute": 60,
"requests_per_hour": 1000,
"requests_per_day": 10000
},
"revoked": false,
"uso_count": 12543,
"last_used_at": "2026-04-29T14:23:45Z"
}
Actualizar rate limit
PUT {BASE_URL}/studio/artefactos/{id}/api-key
Content-Type: application/json
{
"rate_limit_config": {
"requests_per_minute": 120,
"requests_per_hour": 5000,
"requests_per_day": 50000
}
}
Regenerar API Key
OPERACIÓN DESTRUCTIVA. La key anterior queda invalidada inmediatamente. Cualquier app que la use deja de funcionar hasta actualizar el código.
POST {BASE_URL}/studio/artefactos/{id}/api-key/regenerate
Respuesta:
{
"api_key": "puente_artifact_NEW_KEY_HERE",
"message": "API key regenerada exitosamente. Guárdala de forma segura, no se podrá recuperar."
}
Acceso de artefactos a tablas
Un artefacto no puede leer/escribir una tabla por default. Hay que conceder acceso explícito.
Listar tablas accesibles
GET {BASE_URL}/studio/artefactos/{id}/tablas-acceso
Conceder acceso
POST {BASE_URL}/studio/artefactos/{id}/tablas-acceso
Content-Type: application/json
{ "tabla_id": "uuid-tabla", "permisos": ["read", "write"] }
Permisos disponibles:
["read"] // Solo lectura
["read", "write"] // Lectura + insertar/actualizar
["read", "write", "delete"] // Control total
Aplica principio de mínimo privilegio: un dashboard de consulta solo necesita ["read"].
Actualizar permisos
PUT {BASE_URL}/studio/artefactos/{id}/tablas-acceso/{tabla_id}
Content-Type: application/json
{ "permisos": ["read"] }
Revocar acceso
DELETE {BASE_URL}/studio/artefactos/{id}/tablas-acceso/{tabla_id}
La tabla debe pertenecer al mismo equipo que el artefacto.
Endpoints públicos — Datos del artefacto
Los usa la app publicada desde el frontend. Se autentican con la api_key del artefacto.
Base URL: {BASE_URL}/public/artefacto/{artefacto_id}
Auth: X-API-Key: puente_artifact_xxxxxxxxxxxx
GET /public/artefacto/{artefacto_id}/tablas/{tabla_id}
Permiso requerido: read.
Listar filas (con filtros)
GET /public/artefacto/{artefacto_id}/tablas/{tabla_id}/datos?limit=50&offset=0
Permiso requerido: read.
Filtros con where
?where=(campo,operador,valor)~and(campo2,operador2,valor2)
Operadores:
| Operador | Descripción |
|---|
eq / neq | Igual / no igual |
gt / gte / lt / lte | Comparaciones numéricas |
like / nlike | Contiene / no contiene (case-insensitive) |
starts / ends | Empieza / termina con |
is / isnot | Es null, notnull, true, false |
in / notin | En lista / no en lista |
empty / notempty | Nulo o vacío / no |
Operadores lógicos: ~and, ~or.
Ejemplos:
?where=(plan,eq,Pro)~and(activo,is,true)
?where=(monto,gte,1000)~and(monto,lte,5000)
?where=(activo,is,true)&limit=20&offset=40
Si el valor contiene espacios o caracteres especiales, URL-encodea el parámetro completo.
Respuesta:
[
{
"id": "uuid-fila",
"tabla_id": "uuid-tabla",
"fila_data": {
"fecha": "2026-04-29",
"sucursal": "Santiago",
"monto": 1250000
},
"created_at": "2026-04-29T12:00:00Z"
}
]
El campo de datos se llama fila_data, no datos. Es un gotcha frecuente.
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1709988123
Insertar fila
POST /public/artefacto/{artefacto_id}/tablas/{tabla_id}/dato
Content-Type: application/json
{ "datos": { "campo": "valor", "monto": 1000 } }
Permiso requerido: write.
Actualizar fila
PUT /public/artefacto/{artefacto_id}/tablas/{tabla_id}/dato/{fila_id}
Content-Type: application/json
{ "datos": { "monto": 2000 } }
Permiso requerido: write.
Eliminar fila
DELETE /public/artefacto/{artefacto_id}/tablas/{tabla_id}/dato/{fila_id}
Permiso requerido: delete.
Límites del sistema
| Concepto | Límite |
|---|
| API keys activas por artefacto | 1 |
| Tablas accesibles por artefacto | Ilimitado |
| Filas retornadas por request (pública) | 500 max |
| Filas retornadas por request (privada) | 5,000 max |
| Bulk insert | 10,000 filas por request |
| Rate limit default / minuto | 60 requests |
| Rate limit default / hora | 1,000 requests |
| Rate limit default / día | 10,000 requests |
Códigos de error
Con STUDIO_KEY (gestión)
| Código | Significado | Qué hacer |
|---|
401 | STUDIO_KEY inválida o revocada | Generar nueva en app.puente.xyz → Configuración |
403 | Sin créditos | Contactar admin de la cuenta |
404 | Recurso no existe en tu equipo | Verificar ID con endpoint de listado |
422 | Campo con formato incorrecto | Leer mensaje — indica qué campo falló |
Con API Key de artefacto (públicos)
| Código | Causa | Solución |
|---|
400 Datos inválidos | Tipos incorrectos o requerido faltante | Revisar schema de columnas |
401 API Key requerida | Falta header | Incluir X-API-Key: puente_artifact_xxx |
401 API Key inválida | Key incorrecta, revocada o tipo incorrecto | Verificar o regenerar |
401 API Key no autorizada | Key pertenece a otro artefacto | Usar la key correcta |
403 Acceso no configurado | El artefacto no tiene acceso a esa tabla | POST .../tablas-acceso |
403 Permiso insuficiente | Permisos no incluyen la acción | PUT .../tablas-acceso/{tabla_id} |
429 Rate limit excedido | Demasiados requests | Esperar Retry-After segundos |
Ejemplo en JavaScript (dentro de una app publicada)
const API_KEY = 'puente_artifact_xxxxxxxxxxxx';
const ARTEFACTO_ID = 123;
const TABLA_ID = 'uuid-de-la-tabla';
const BASE = `https://puente-backend-721178029791.southamerica-west1.run.app/public/artefacto/${ARTEFACTO_ID}`;
// Leer datos con filtro
const res = await fetch(
`${BASE}/tablas/${TABLA_ID}/datos?where=(activo,is,true)&limit=50`,
{ headers: { 'X-API-Key': API_KEY } }
);
const filas = await res.json();
// Insertar fila
await fetch(`${BASE}/tablas/${TABLA_ID}/dato`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({ datos: { nombre: 'Ana', monto: 100, fecha: '2026-04-29' } })
});
// Manejar rate limit
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After') || 60;
console.warn(`Rate limit excedido. Reintentar en ${retryAfter}s`);
}
La api_key del artefacto es visible en el código fuente de la app publicada (el bundle se sirve al browser del usuario). Usa siempre el mínimo privilegio (["read"] si solo lees) para limitar el riesgo si la key se filtra.