# Functions and flows

An **AI Function** is ONE tool the Met can call (scopes `functions:read` / `functions:write`). When the Met decides to use it, it collects the arguments according to your `parameters` and fires the handler you defined. That is how you connect your own backend to the conversation.

## Parameters: the tool's schema

Each `FunctionParameter` goes into the JSON schema the LLM sees. Only `name` is required:

```ts
const parameters = [
  { name: 'sku', description: 'Product code', required: true },
  { name: 'currency', description: 'Price currency', enum_values: ['COP', 'USD'] },
];
```

Available fields: `name`, `description`, `required`, `type` (`'string' | 'number' | 'boolean'`), `enum_values` (closed list; `null`/`[]` = free) and `save_to` (the contact's `field_key` where the collected value should persist, or `null` to not store it).

A Function without a handler is **inert** (not callable). You have two ways to give it one.

## A) Direct HTTP handler

The shortest path to hitting your endpoint: pass `http`. The arguments the Met collects travel to your URL as a JSON body.

```ts
const fn = await met.functions.create({
  name: 'check_price',
  description: 'Looks up the current price of a SKU in our backend',
  prompt: 'Use it when the customer asks about a product price.',
  parameters,
  http: {
    url: 'https://api.your-company.com/prices',
    method: 'POST',
    auth_workspace_variable: 'api_token',
    sign_secret_variable: 'price_signature',
    timeout_ms: 15000,
  },
});
```

> Secrets do NOT go in the raw. `auth_workspace_variable` (Bearer token, sent as `Authorization`) and `sign_secret_variable` (signing secret) are referenced by **workspace variable name**. Create them first with `met.variables.set('api_token', 'sk_live_…', { encrypted: true })` — see [Workspace variables](variables.html).

Meteor signs every call with `X-Met-Signature` (the same HMAC as the webhooks). Verify it in your endpoint without writing HMAC by hand:

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

Static, NON-sensitive headers go in `headers`. `timeout_ms` is optional (default 30s, ceiling 60s).

## B) Flow (`flow_id`)

When you need multi-step logic, branches or several nodes, point the Function at a Meteor Flow (scopes `flows:write` / `automations:execute`). The pattern is `create` → `update` with the canvas → `publish`:

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

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

await met.flows.publish(flow.id);   // the Met only invokes the published version

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

The `http.request` node can sign the request with `X-Met-Signature` just like the direct handler. Test the flow before publishing with `met.flows.run(flow.id, { use_draft: true })`.

## Link it to the Met

Creating the Function does not activate it on any Met: it has to be linked.

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

> To confirm your Function ended up available as a tool of a Met, use `met.agents.tools(agentId)`: it returns each tool with its `source` (`'function'` for yours) and whether it is `disabled`. See [Mets and their tools](mets-and-tools.html).

## End-to-end

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

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

// 1. Secret by variable (not in the raw)
await met.variables.set('api_token', process.env.API_TOKEN, { encrypted: true });

// 2. Function with an HTTP handler
const fn = await met.functions.create({
  name: 'check_price',
  description: 'Looks up the current price of a SKU',
  parameters: [{ name: 'sku', description: 'Product code', required: true }],
  http: { url: 'https://api.your-company.com/prices', method: 'POST', auth_workspace_variable: 'api_token' },
});

// 3. Link it to the Met and try it with a run that uses it
await met.functions.link(agentId, fn.id);

const run = await met.runs.create({ met: 'Valeria', input: 'How much is SKU AB-12?' });
console.log(run.output);   // the Met called your endpoint and answered with the price
```

See [Run Mets (Runs)](run-mets.html) for the details of `runs.create`.
