En esta sección
Guías / Sitios estáticos

Sitios estáticos

Publica un sitio ya construido —HTML, CSS, JS e imágenes— en tu subdominio de metsite.co, con versiones y vuelta atrás en un paso.

Actualizada el Ver .md

Si ya tienes el sitio hecho —a mano, con un generador como Astro, Vite o Hugo, o con un asistente—, lo subes tal cual y Meteor lo aloja en https://<slug>.metsite.co. Cada subida es una versión que no cambia; volver atrás es poner en línea una versión anterior, sin subir nada otra vez.

Son tres pasos: crear el sitio una vez → subir una versión cada vez que cambie → activarla (por defecto pasa sola al subirla).

Antes de empezar

ScopePara quéCon una key de test
sites:writeCrear el sitio, subir versiones y activarlas.No: responde 403 test_mode_restricted, porque publica en internet.
sites:readListar las versiones.Sí

Las rutas son planas (/sites/…): operan sobre el workspace de la key, y un sitio de otro workspace responde 404.

1. Crear el sitio

const sitio = await met.sites.createStatic({ name: 'Neuroliderazgo', slug: 'neuroliderazgo' });
// { site_id: 812, slug: 'neuroliderazgo', url: 'https://neuroliderazgo.metsite.co' }
sitio = met.sites.create_static(name="Neuroliderazgo", slug="neuroliderazgo")
curl -X POST https://api.met.meteor.com.co/api/v1/sites/static \
  -H "Authorization: Bearer $METEOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Neuroliderazgo","slug":"neuroliderazgo"}'

2. Subir una versión

Desde un zip, la forma más simple cuando ya tienes la carpeta construida (hasta 50 MB):

import { readFile } from 'node:fs/promises';

const zip = await readFile('sitio.zip');
const version = await met.sites.deployZip(sitio.site_id, zip);
// { id: '3f6c1e0a-…', status: 'ready', source: 'zip', file_count: 42, total_bytes: 5242880, is_active: true, url: 'https://neuroliderazgo.metsite.co' }
with open("sitio.zip", "rb") as f:
    version = met.sites.deploy_zip(sitio["site_id"], f.read())
cd dist && zip -r ../sitio.zip . && cd ..
curl -X POST https://api.met.meteor.com.co/api/v1/sites/812/deploys \
  -H "Authorization: Bearer $METEOR_API_KEY" \
  -F file=@sitio.zip

O archivo por archivo, en JSON: content en utf8 para el texto y en base64 para lo binario. El cuerpo entero tiene un tope de 10 MB; un sitio más grande va por zip.

import { readdir, readFile } from 'node:fs/promises';
import { join, relative, extname } from 'node:path';

const TEXTO = new Set(['.html', '.css', '.js', '.mjs', '.json', '.svg', '.txt', '.xml', '.webmanifest', '.map']);

async function archivosDe(carpeta: string, raiz = carpeta) {
  const out: { path: string; content: string; encoding: 'utf8' | 'base64' }[] = [];
  for (const entrada of await readdir(carpeta, { withFileTypes: true })) {
    const ruta = join(carpeta, entrada.name);
    if (entrada.isDirectory()) out.push(...(await archivosDe(ruta, raiz)));
    else {
      const texto = TEXTO.has(extname(entrada.name));
      const bytes = await readFile(ruta);
      out.push({
        path: relative(raiz, ruta).split('\\').join('/'),
        content: texto ? bytes.toString('utf8') : bytes.toString('base64'),
        encoding: texto ? 'utf8' : 'base64',
      });
    }
  }
  return out;
}

const version = await met.sites.deployFiles(812, { files: await archivosDe('dist') });
import base64, pathlib

TEXTO = {".html", ".css", ".js", ".mjs", ".json", ".svg", ".txt", ".xml", ".webmanifest", ".map"}
raiz = pathlib.Path("dist")
files = []
for p in raiz.rglob("*"):
    if p.is_file():
        es_texto = p.suffix in TEXTO
        files.append({
            "path": p.relative_to(raiz).as_posix(),
            "content": p.read_text("utf-8") if es_texto else base64.b64encode(p.read_bytes()).decode(),
            "encoding": "utf8" if es_texto else "base64",
        })

version = met.sites.deploy_files(812, files=files)

Cada llamada es una versión completa: no se suman archivos a la anterior. Con activate: false (activate=False en Python) la versión se sube sin ponerla en línea, para revisarla o publicarla después.

Qué se acepta

ReglaTope
Zip50 MB
Archivos por versión2000
Un archivo, descomprimido25 MB
Toda la versión, descomprimida200 MB
JSON de deployFiles10 MB
Largo de una ruta512 caracteres

Cómo se sirven las rutas

Versiones y vuelta atrás

const versiones = await met.sites.listDeploys(812, { limit: 10 });
// de la más reciente a la más vieja; is_active: true en la que está en línea

const anterior = versiones.find((v) => !v.is_active && v.status === 'ready');
if (anterior) await met.sites.activate(812, anterior.id);
versiones = met.sites.list_deploys(812, limit=10)
anterior = next((v for v in versiones if not v["is_active"] and v["status"] == "ready"), None)
if anterior:
    met.sites.activate(812, anterior["id"])
curl -X POST https://api.met.meteor.com.co/api/v1/sites/812/deploys/3f6c1e0a-5b7d-4c2e-9a1f-0d8b7e6a5c4b/activate \
  -H "Authorization: Bearer $METEOR_API_KEY"
statusQué es
readySubida completa: se puede activar.
uploadingTodavía se está subiendo.
failedLa subida no terminó; error dice por qué. Sube otra versión.

listDeploys trae 20 por defecto (de 1 a 100). activate solo pone en línea una versión ready de ese sitio; la primera activación publica el sitio. El cambio es inmediato y no se sube nada otra vez.

Tu dominio

El sitio vive en https://<slug>.metsite.co. Conectar un dominio propio (www.tunegocio.com) no se hace por la API.

Desde un asistente (MCP)

Estas operaciones se alcanzan con la herramienta genérica del servidor MCP (met_call_operation), en su forma por archivos: el zip no viaja por MCP, porque un asistente no adjunta binarios en la conversación.

Errores comunes

CódigoQué pasó
400 validation_errorEl slug no cumple el formato, o está reservado.
400 deploy_files_rejectedAlguna ruta o extensión no se acepta; mira errors. No se subió nada.
400 deploy_zip_missingEl zip no llegó en el campo multipart file.
400 zip_invalidEl archivo no es un zip válido.
400 deploy_empty · deploy_too_many_files · deploy_too_large · deploy_zip_too_largeLa versión no trae archivos o pasa uno de los topes.
413 FILE_TOO_LARGEEl zip subido por multipart pasa de 50 MB.
403 test_mode_restrictedUna key met_test_ no publica sitios.
404 not_found · deploy_not_foundEl sitio no es de este workspace, o la versión no es de este sitio.
409 conflictEl slug ya está en uso.
409 site_not_staticEl sitio se arma por bloques en el panel: las versiones son solo para sitios creados con createStatic.
409 deploy_not_readySe intentó activar una versión que no está ready.
503 deploy_upload_failedLa subida falló de nuestro lado. La versión queda failed y la que estaba en línea sigue en línea: vuelve a subirla.