# Operación del workspace

Cinco dominios que se consultan **juntos**, y siempre por la misma razón: algo ya está corriendo en producción y necesitas saber cuánto está gastando, quién lo está atendiendo o dónde quedó un archivo. No son cosas que se leen aprendiendo; son las que se buscan a las once de la noche.

## Cuánta Energía estás gastando

La Energía se debita por ejecución. Si tu integración dispara Mets, esto es lo que te dice cuánto cuesta antes de que te sorprenda la factura:

```ts
const uso = await met.billing.consumption({ from: '2026-07-01', to: '2026-07-31' });
const ejec = await met.billing.executions({ limit: 50 });
```

`consumption` es el agregado del período; `executions` es el detalle, una fila por ejecución con su costo. Cuando el total no cuadra con lo que esperabas, la respuesta está en el detalle: casi siempre es un Met que se llama a sí mismo en un bucle, o un flujo que corre más veces de las que creías.

```ts
const saldo = await met.billing.subscription();
const recarga = await met.billing.autoRecharge();
```

`autoRecharge` importa más de lo que parece si tu integración es desatendida: sin recarga automática, quedarse sin Energía **detiene las ejecuciones**, y eso se ve como "la API dejó de responder" cuando en realidad es saldo. Consúltala antes de culpar al código.

El resto es lectura de la relación comercial: `invoices()`, `transactions()`, `plans()`, `addons()` y `myAddons()`. Y `access()`, que responde qué tiene habilitado el plan actual — útil para no llamar a un endpoint que va a devolver 403 por plan y no por permisos.

## Archivos del workspace

La Mediateca son los archivos del workspace, con carpetas propias:

```ts
const imagenes = await met.assets.list({ mime_prefix: 'image/', limit: 50 });
const subido = await met.assets.upload(blob, {
  filename: 'propuesta.pdf',
  contentType: 'application/pdf',
  folder_id: carpeta.id,
});
```

`list` devuelve un array (no una página con cursor) y pagina con `limit`/`offset`. Además de `mime_prefix` filtra por `folder_id`, `search` y `source` — y ese último es el que sirve para auditar: distingue lo que subió una persona (`upload`) de lo que salió de un ítem, de lo que generó un Met (`generated`) o un flujo (`flow_run`).

En `upload`, `filename` es **obligatorio**: es lo que determina la extensión del objeto en storage, y sin ella el archivo queda sin tipo reconocible.

Dos cosas que ahorran tiempo:

**Las miniaturas no las generas tú.** Storage reescala en el borde, así que para mostrar un archivo en chico se pide ya dimensionado en vez de bajar el original. Está en [Imágenes](imagenes.html), y la diferencia medida es de 870 KB a 15 KB.

**`cleanupProvisional`** existe porque una subida que empieza y no termina deja el archivo huérfano. Corre en simulación por defecto:

```ts
const previo = await met.assets.cleanupProvisional();       // simula
await met.assets.cleanupProvisional(true);                  // aplica
```

Mira el resultado antes de pasar `true`. No es reversible.

## Conversaciones

Una conversación es el hilo con un contacto. La distinción que hay que tener clara:

```ts
const principal = await met.conversations.main();
const todas = await met.conversations.list();
```

`main()` devuelve la conversación **principal** del workspace — la del panel, donde el equipo habla con el Met. `list()` devuelve todas, incluidas las de cada contacto. Si buscabas el historial de un contacto, el camino corto es [`met.contacts.messages(id)`](contactos.html#leer-conversaciones), no recorrer `list()`.

`threadMessages(threadId)` baja los mensajes de un hilo derivado, que es lo que se arma cuando alguien responde dentro de un mensaje en vez de al final.

## Recordatorios

Un recordatorio agenda **un mensaje al contacto** para una fecha futura. Eso es lo primero que hay que tener claro: **siempre se envía**. No existe un modo "nota interna" — `note` es un campo para el equipo, no un interruptor que evite el envío.

```ts
await met.reminders.create({
  contact_id: 42,
  scheduled_for: '2026-08-05T14:00:00Z',
  message: 'Te escribo para confirmar si firmaste la propuesta',
  note: 'Seguimiento de la propuesta de julio',   // interno, no viaja
});
```

A la hora agendada, un despachador se lo manda al contacto por su canal. El canal sale del contacto, así que no hay que elegirlo.

`scheduled_for` va en ISO 8601 y tiene que ser futuro. **Con offset** (`2026-08-05T09:00:00-05:00` o el `Z` del ejemplo) es un instante exacto; **sin offset** se interpreta en la zona horaria del workspace. Manda el offset si lo calculas en tu servidor: es la diferencia entre las 9 de la mañana del cliente y las 9 de la mañana de tu proceso.

Va con plantilla de WhatsApp y no con texto libre por lo mismo que las difusiones: un recordatorio se dispara días después del último mensaje de la persona, o sea fuera de la ventana de 24 horas, donde solo pasan plantillas aprobadas. Por eso el workspace necesita tener una **plantilla de recordatorio por defecto** configurada (Ajustes): sin ella, `create` responde 400. Tu `message` va como el cuerpo de esa plantilla.

Si quieres una plantilla distinta de la de por defecto, pásala explícita — y entonces `language` es obligatorio:

```ts
await met.reminders.create({
  contact_id: 42,
  scheduled_for: '2026-08-05T14:00:00Z',
  template_name: 'seguimiento_propuesta',
  language: 'es',
  variables: { '1': 'Ana' },                      // los huecos de la plantilla
});
```

`list({ contactId, status })`, `update` y `cancel` completan el CRUD. `update` mueve la fecha o cambia el texto, y **solo funciona mientras está pendiente**; `cancel` lo detiene antes de que se despache. Cancela en cuanto el motivo deja de existir: si no, al contacto le llega un mensaje fuera de contexto.

## Autopiloto: cuándo contesta el Met y cuándo un humano

Por defecto el Met atiende. El autopiloto es el interruptor:

```ts
await met.contacts.setAutopilot(42, false);      // que atienda un humano
await met.contacts.pauseAutopilot(42, 30);       // 30 minutos, y vuelve solo
await met.contacts.setAutopilot(42, true);       // devolvérselo al Met
```

**Usa `pause` y no `setAutopilot(false)` para una intervención puntual.** Es la decisión que más se equivoca: apagar el autopiloto es permanente hasta que alguien lo prenda, y lo que sigue es un contacto que quedó sin atención automática durante semanas porque nadie se acordó. `pause` con minutos se reanuda solo; `pause(id, 0)` reanuda ya.

`markRead(id)` marca la conversación como leída, para que tu integración no deje el panel del equipo lleno de no-leídos que ya procesaste.

## Grupos de agentes

Son grupos de **personas** —los agentes humanos del chat—, no de Mets. Sirven para enrutar y asignar conversaciones a un equipo en vez de a un individuo:

```ts
const g = await met.agentGroups.create({ name: 'Soporte técnico' });
await met.agentGroups.addMember(g.id, userId);
const miembros = await met.agentGroups.members(g.id);
```

`addMember` recibe un **`user_id`**, y ahí está la confusión que conviene evitar: si buscabas agrupar Mets, eso no existe como grupo — un Met se acota con sus [habilidades y herramientas](mets-y-herramientas.html).

Gestionarlos pide rol de supervisor o admin, así que una key con scope pero de un usuario sin ese rol recibe 403. Es permiso, no plan.

> Todo lo de esta guía opera sobre el workspace de la key. Una key de **partner** tiene su propia superficie: mira la [API de Partners](../partners.html).
