# Funciones y flujos

Una **Función IA** es UNA herramienta que el Met puede llamar (scopes `functions:read` / `functions:write`). Cuando el Met decide usarla, recolecta los argumentos según tus `parameters` y dispara el handler que definiste. Así conectas tu propio backend a la conversación.

## Parámetros: el schema de la tool

Cada `FunctionParameter` entra al JSON schema que ve el LLM. Solo `name` es obligatorio:

```ts
const parameters = [
  { name: 'sku', description: 'Código de producto', required: true },
  { name: 'moneda', description: 'Moneda del precio', enum_values: ['COP', 'USD'] },
];
```

Campos disponibles: `name`, `description`, `required`, `type` (`'string' | 'number' | 'boolean'`), `enum_values` (lista cerrada; `null`/`[]` = libre) y `save_to` (el `field_key` del contacto donde persistir el valor recolectado, o `null` para no guardar).

Una Función sin handler queda **inerte** (no invocable). Tienes dos caminos para dárselo.

## A) Handler HTTP directo

El camino más corto para pegarle a tu endpoint: pasa `http`. Los argumentos que el Met recolecta viajan como body JSON a tu URL.

```ts
const fn = await met.functions.create({
  name: 'consultar_precio',
  description: 'Consulta el precio vigente de un SKU en nuestro backend',
  prompt: 'Úsala cuando el cliente pregunte por el precio de un producto.',
  parameters,
  http: {
    url: 'https://api.tu-empresa.com/precios',
    method: 'POST',
    auth_workspace_variable: 'api_token',
    sign_secret_variable: 'firma_precios',
    timeout_ms: 15000,
  },
});
```

> Los secretos NO van en crudo. `auth_workspace_variable` (Bearer token, se envía como `Authorization`) y `sign_secret_variable` (secreto de firma) se referencian por **nombre de variable del workspace**. Créalas antes con `met.variables.set('api_token', 'sk_live_…', { encrypted: true })` — ver [Variables del workspace](variables.html).

Meteor firma cada llamada con `X-Met-Signature` (mismo HMAC que los webhooks). Verifícala en tu endpoint sin escribir HMAC a mano:

```ts
const handle = met.tools.createHandler(process.env.FIRMA_PRECIOS);
app.post('/precios', (req, res) => {
  let call;
  try { call = handle(req.rawBody, req.headers['x-met-signature']); }
  catch { return res.status(400).end(); }
  res.json({ result: buscarPrecio(call.arguments) });
});
```

Los headers estáticos NO sensibles van en `headers`. `timeout_ms` es opcional (default 30s, tope 60s).

## B) Flujo (`flow_id`)

Cuando necesitas lógica multi-paso, ramas o varios nodos, apunta la Función a un Meteor Flow (scopes `flows:write` / `automations:execute`). El patrón es `create` → `update` con el lienzo → `publish`:

```ts
const flow = await met.flows.create({ name: 'llamar-mi-api' });

await met.flows.update(flow.id, {
  draft_definition: {
    nodes: [{ id: 'call', type: 'http.request', url: 'https://api.tu-empresa.com/precios' }],
    edges: [],
  },
});

await met.flows.publish(flow.id);   // el Met solo invoca la versión publicada

const fn = await met.functions.create({ name: 'consultar_precio', parameters, flow_id: flow.id });
```

El nodo `http.request` puede firmar la petición con `X-Met-Signature` igual que el handler directo. Prueba el flujo antes de publicar con `met.flows.run(flow.id, { use_draft: true })`.

## Vincular al Met

Crear la Función no la activa en ningún Met: hay que vincularla.

```ts
await met.functions.link(agentId, fn.id);        // idempotente
await met.functions.listForAgent(agentId);       // → [12, 34] (ids vinculados)
await met.functions.unlink(agentId, fn.id);      // desvincular
```

> Para confirmar que tu Función quedó disponible como tool de un Met, usa `met.agents.tools(agentId)`: devuelve cada herramienta con su `source` (`'function'` para las tuyas) y si está `disabled`. Ver [Mets y herramientas](mets-y-herramientas.html).

## End-to-end

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

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

// 1. Secreto por variable (no en crudo)
await met.variables.set('api_token', process.env.API_TOKEN, { encrypted: true });

// 2. Función con handler HTTP
const fn = await met.functions.create({
  name: 'consultar_precio',
  description: 'Consulta el precio vigente de un SKU',
  parameters: [{ name: 'sku', description: 'Código de producto', required: true }],
  http: { url: 'https://api.tu-empresa.com/precios', method: 'POST', auth_workspace_variable: 'api_token' },
});

// 3. Vincular al Met y probar con un run que la use
await met.functions.link(agentId, fn.id);

const run = await met.runs.create({ met: 'Valeria', input: '¿Cuánto vale el SKU AB-12?' });
console.log(run.output);   // el Met llamó a tu endpoint y respondió con el precio
```

Ver [Ejecutar Mets (Runs)](ejecutar-agentes.html) para el detalle de `runs.create`.
