# Workspace variables

`met.variables` stores two things belonging to the workspace: **value variables** (configuration and secrets, such as the token for an external API) and the **definitions of your CRM's custom fields**. The first needs the `variables:write` scope to write; the second lets you discover which keys a contact accepts.

```ts
const met = new Met(key, { workspaceId });
```

## Value variables

A variable is a `name` → `value` pair that lives in the workspace. `set()` upserts by name — creating or updating depending on whether it exists — which is the most convenient form.

```ts
await met.variables.set('my_api_token', 'sk_live_...', { encrypted: true });

const vars = await met.variables.list();   // never returns secrets in the clear
```

Mark `encrypted: true` for tokens and secrets: the value is stored encrypted and `list()` no longer returns it in the clear. If you prefer explicit operations by id, you have `create`, `update` and `delete`:

```ts
const v = await met.variables.create({
  name: 'greeting',
  value: 'Hello!',
  description: 'Default text',
});                                         // POST /variables (variables:write)

await met.variables.update(v.id, { value: 'Welcome!' });
await met.variables.delete(v.id);
```

> `set()` runs a `list()` to find the name; if you are going to write many variables in a row, go straight to `create`/`update` with the id you already have.

## Token → HTTP Function

The headline use case: an [HTTP Function](functions-and-flows.html) resolves its authentication token **by variable name**, not in the raw. You store the token once and the Function references it with `auth_workspace_variable`; Meteor sends it as `Authorization: Bearer <value>` when calling your endpoint.

```ts
// 1. Store the secret (encrypted)
await met.variables.set('my_api_token', 'sk_live_...', { encrypted: true });

// 2. The Function references it by name
await met.functions.create({
  name: 'check_price',
  description: 'Looks up a product price in the external API',
  http: {
    url: 'https://api.example.com/price',
    method: 'POST',
    auth_workspace_variable: 'my_api_token',
  },
});
```

That way the token is never written into the Function's definition. To rotate it, a single `set()` with the same name replaces it and every Function using it is up to date.

## CRM field definitions

A contact's custom fields live in its `data` under the **bare key** (`stage`, `amount`); system fields carry a `$` (`$name`, `$status`). To find out which keys exist and which options a `select` accepts, discover the catalog:

```ts
const defs = await met.variables.fieldDefinitions();   // variables:read
const stages = defs.find((d) => d.field_key === 'stage')?.options ?? [];

await met.contacts.update(42, { data: { stage: stages[0] } });
```

Each definition carries `field_key`, `label`, `type` (`text` · `number` · `select` · `currency` · `date` · …) and `options`. You can also manage them from code:

```ts
const field = await met.variables.createFieldDefinition({
  field_key: 'stage',
  label: 'Stage',
  type: 'select',
  options: ['New', 'Contacted', 'Closed'],
});

await met.variables.updateFieldDefinition(field.id, { options: [...field.options, 'Lost'] });
await met.variables.deleteFieldDefinition(field.id);
```

`field_key` and `type` cannot be edited: changing them would break the values already stored on your contacts. You only adjust `label`, `description` and `options`.

> To read and write those values on each contact, see [Contacts (CRM)](contacts.html).
