# Cuánto cuesta cada llamada

Cada vez que un Met trabaja —responde un WhatsApp, corre un Run que lanzaste por API, ejecuta una automatización— se debita Energía de tu saldo. Esta guía muestra cómo saber cuánto costó **cada llamada** y cómo sumarlo por **Run**, por **Met** o por **línea de WhatsApp**.

Todo lo de esta guía es de solo lectura y pide el scope `billing:read` (el costo de un Run, `runs:read`). Los montos son la Energía que se te debitó, en USD: lo mismo que ves en el panel.

## Cada ejecución, con su origen

`GET /billing/executions` lista las ejecuciones de tu workspace, de la más reciente a la más vieja. Cada una dice cuánto costó y de dónde vino:

| Campo | Qué dice |
|---|---|
| `cost_final_usd` | La Energía que se debitó por esta ejecución, en USD. |
| `met_id`, `met_name` | El Met que trabajó. |
| `model_display` | El nivel de inteligencia que usó (`Ágil`, `Brillante`, `Genio`…). |
| `run_id` | El Run de la API que la causó (`run_…`), o `null` si no vino de un Run. |
| `channel` | Por dónde llegó el trabajo: `whatsapp`, `instagram`, `messenger`, `widget`, `panel`, `api`… |
| `whatsapp_line` | La línea de WhatsApp: `{ id, number_masked }`, o `null` (ver [qué lleva línea](#que-lleva-linea-y-desde-cuando)). |

`whatsapp_line.id` es el `phone_number_id` de Meta, el mismo valor que usas en `sender_line_id` y que devuelve `GET /channels/{channelId}/whatsapp/lines`. El número va enmascarado (`+57 300 ••• 4567`): alcanza para reconocer la línea sin copiar el número completo en cada fila.

```ts
import Met from '@meteor.ia/sdk';

const met = new Met(process.env.MET_API_KEY, { workspaceId: 7 });

const pagina = await met.billing.executions({ limit: 20 });
for (const e of pagina.rows) {
  console.log(e.executed_at, e.met_name, e.channel, e.whatsapp_line?.number_masked ?? '-', e.cost_final_usd);
}
console.log('Total del filtro:', pagina.totals?.cost_final_usd);
```
```python
import os
from meteor_ia import Met

met = Met(os.environ["MET_API_KEY"], workspace_id=7)

pagina = met.billing.executions(limit=20)
for e in pagina["rows"]:
    linea = (e.get("whatsapp_line") or {}).get("number_masked", "-")
    print(e["executed_at"], e["met_name"], e["channel"], linea, e["cost_final_usd"])
print("Total del filtro:", (pagina.get("totals") or {}).get("cost_final_usd"))
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/executions?limit=20" \
  -H "Authorization: Bearer $MET_API_KEY"
```

La respuesta es una página `{ rows, total, limit, offset, totals }`. El máximo es **100 por página**: un `limit` mayor se ajusta a 100 sin error, y el `limit` de la respuesta es el que se aplicó. `totals` suma **todas** las ejecuciones que cumplen el filtro, no solo las de la página.

Lo que nunca sale: el costo del proveedor, el margen ni el nombre del modelo. Tampoco el contacto ni la conversación: para saber cuánto se gastó por contacto está `GET /billing/consumption?by=contact`.

## Filtrar por Met, línea o Run

Tres filtros responden «¿cuánto me costó esto?»:

| Filtro | Ejemplo | Qué trae |
|---|---|---|
| `met_id` | `met_id=41207` | Solo lo que hizo ese Met. |
| `line_id` | `line_id=1210946622112871` | Solo lo que entró por esa línea de WhatsApp. |
| `run_id` | `run_id=run_01JB8ZQ4…` | Solo lo que causó ese Run. |

Se combinan entre sí y con `from`, `to`, `type`, `costMin` y `costMax`. `totals` respeta los mismos filtros, así que con una sola llamada tienes el total:

```ts
// ¿Cuánto costó la línea de ventas en octubre?
const octubre = await met.billing.executions({
  line_id: '1210946622112871',
  from: '2026-10-01T00:00:00Z',
  to: '2026-10-31T23:59:59Z',
  limit: 1,
});
console.log(octubre.total, 'ejecuciones ·', octubre.totals?.cost_final_usd, 'USD');
```
```python
octubre = met.billing.executions(
    line_id="1210946622112871",
    from_="2026-10-01T00:00:00Z",
    to="2026-10-31T23:59:59Z",
    limit=1,
)
print(octubre["total"], "ejecuciones ·", octubre["totals"]["cost_final_usd"], "USD")
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/executions?line_id=1210946622112871&from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z&limit=1" \
  -H "Authorization: Bearer $MET_API_KEY"
```

Un filtro mal escrito responde **400**, con el parámetro en `param` (`invalid_met_id`, `invalid_line_id`, `invalid_run_id`): nunca se ignora en silencio, porque devolver todas las ejecuciones te haría creer que esa línea gastó lo de toda la cuenta. `line_id` es el `phone_number_id` (solo dígitos), no el número de teléfono.

Para recorrer todas las ejecuciones de un filtro sin manejar el `offset`:

```ts
let suma = 0;
for await (const e of met.billing.iterateExecutions({ met_id: 41207, from: '2026-10-01T00:00:00Z' })) {
  suma += e.cost_final_usd;
}
```
```python
suma = sum(e["cost_final_usd"] for e in met.billing.iterate_executions(met_id=41207, from_="2026-10-01T00:00:00Z"))
```

## Lo que costó un Run

`GET /runs/{id}` trae `cost_final_usd`: lo que costó **ese** Run, sumando la Energía de todas las ejecuciones que causó (las mismas de `GET /billing/executions?run_id=…`).

```ts
const run = await met.runs.create({ input: 'Resume los leads de hoy' });
const detalle = await met.runs.retrieve(run.id);
console.log(detalle.status, detalle.cost_final_usd);
```
```python
run = met.runs.create("Resume los leads de hoy")
detalle = met.runs.retrieve(run["id"])
print(detalle["status"], detalle.get("cost_final_usd"))
```
```bash
curl https://api.met.meteor.com.co/api/v1/runs/run_01JB8ZQ4W8X3K7M2N5P6R9T0VA \
  -H "Authorization: Bearer $MET_API_KEY"
```

Tres cosas para leerlo bien:

- **Viene solo en el detalle.** La respuesta de `POST /runs` y el listado no lo traen: el costo se consolida después de que el Run responde.
- **Puede crecer unos minutos.** Lo que el Run dejó corriendo al responder (un resumen, una evaluación) se cobra un poco después y también es suyo. A los 10 minutos de `completed_at` queda cerrado.
- **`null` no es cero.** Sale `null` mientras el Run sigue en curso, y cuando no hay ejecuciones registradas con su id: un Run anterior a la versión 3.51.0 del contrato o uno que falló antes de llegar al Met. Un Run de test (`met_test_`) con ejecuciones da `0`.

`energy`, que ya venía en el detalle, suma la **conversación** del Run: si reusaste `conversation_id`, incluye otros Runs. Para el costo de uno solo, usa `cost_final_usd`.

## Consumo agrupado por línea

`GET /billing/consumption?by=line` reparte la Energía de un período por línea de WhatsApp, de la que más gastó a la que menos:

```ts
const porLinea = await met.billing.consumption({ by: 'line', from: '2026-10-01T00:00:00Z' });
for (const l of porLinea) {
  console.log(l.whatsapp_line?.number_masked, l.executions, l.energia_usd);
}
```
```python
por_linea = met.billing.consumption(by="line", **{"from": "2026-10-01T00:00:00Z"})
for l in por_linea:
    print(l["whatsapp_line"]["number_masked"], l["executions"], l["energia_usd"])
```
```bash
curl "https://api.met.meteor.com.co/api/v1/billing/consumption?by=line&from=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $MET_API_KEY"
```

Cada fila trae `energia_usd`, `executions`, `time_saved_minutes` y `whatsapp_line`. Usa `whatsapp_line.id` para bajar al detalle con `line_id`; `dim_key` es el id interno de la línea en Met (`null` si la línea ya no está conectada a tu cuenta). Si una línea se desconectó, su consumo sigue apareciendo, con `number_masked` en `null`.

El mismo endpoint agrupa por `met` (por defecto), `contact`, `actor` (miembro del equipo) y `conversation`.

## Qué lleva línea y desde cuándo

Met registra el Run y la línea de cada ejecución **desde la versión 3.51.0 del contrato**. Lo anterior no se reconstruye: esas ejecuciones traen `run_id` y `whatsapp_line` en `null` y no cuentan en `line_id`, `run_id` ni `by=line`. En ejecuciones viejas, `channel` puede decir `whatsapp` para cualquier canal. El costo (`cost_final_usd`) sí está completo desde siempre.

Llevan línea:

- la respuesta del Met a un mensaje que entró por WhatsApp, con todo lo que hizo para responderla;
- los seguimientos y los eventos sintéticos de esa conversación;
- los flujos que nacieron de un mensaje por WhatsApp (sus nodos de Met);
- la transcripción de las notas de voz que llegan por WhatsApp.

No llevan línea, aunque el gasto sea de WhatsApp:

- la reinvocación manual del Met sobre una conversación;
- los resúmenes que corren fuera de una respuesta;
- los procesos de fondo;
- todo lo anterior a la versión 3.51.0.

Por eso `whatsapp_line` puede ser `null` en un gasto de WhatsApp, y la suma de `by=line` puede ser menor que lo que gastó el canal entero. Para el total del canal, suma `cost_final_usd` de las ejecuciones con `channel` igual a `whatsapp`.
