# Contact fields

A contact field is a custom per-contact value (a column in Contactos, a filter in Audiencias, a row in SQL). Its values live in the workspace's own database. It is for data you keep about a contact; one campaign's values go in the audience CSV plus the broadcast's `fill` (`get_skill('enviar-campana')`). Workspaces without their own database answer `contact_fields_not_ready` on every field tool; tell the operator and do not retry.

## Create or refresh

1. Explore with `bi_query` until a SELECT returns ONE row per contact with a `contact_key` (or `phone`) column and a `value` column. Use `DISTINCT` or `GROUP BY` (`BOOL_OR`, `MAX`).
2. Call `bi_materialize_field` with `key` (or `label`), `label`, `type` (`string`, `number`, `boolean`, `date`, `enum` with `options`) and `sql`, without `confirmation_token`. Nothing is written: the answer says how many contacts get a value and shows 10 samples.
3. Show the counts to the operator. Only after they agree, call again with the same arguments plus `confirmation_token`.

`bi_materialize_field` with only `key` refreshes the field from its stored SQL. A refresh replaces the values of every contact its SELECT returns, manual values included, and removes the values of contacts it no longer returns (manual values of other contacts stay).

## Change, set by hand, delete

- `bi_update_contact_field`: label, description, enum options (add, rename, remove an unused one). The type cannot change once the field has values: delete it and create it again.
- `bi_set_contact_field_values`: one value (or `clear=true`) for up to 500 contacts given as phones or contact keys.
- `bi_delete_contact_field`: the field disappears and its values are deleted (kept in history).

Every write is two calls: the first is a dry run, the second carries `confirmation_token`. Writes need the Editor avanzado profile or higher.

## Read

- `bi_list_contact_fields`: key, label, type, options, how many contacts have a value.
- SQL: `SELECT field_key, contact_key, value FROM public_marts.contact_fields_v1 WHERE field_key = 'interes_aves'`. The catalog with counts is `public_marts.contact_field_catalog_v1`.
- Audiences: filter `field.<key>` (e.g. `field.interes_aves = sí`).

## Errors

| `error_code` | Meaning |
| --- | --- |
| `contact_fields_not_ready` | No own database with contact fields. Do not retry. |
| `field_sql_not_select` | Only one SELECT (or WITH … SELECT) is accepted. |
| `field_sql_shape` | The SELECT lacks `contact_key` (or `phone`) or `value`. |
| `field_duplicate_contact` | Two different values for one contact: one row per contact. |
| `field_too_many_rows` | More than 200,000 rows: filter more. |
| `field_value_not_in_options` | An enum value outside the field's options. |
| `field_type_immutable` | Delete the field and create it again with the new type. |
| `field_option_in_use` | Rename the option, or clear its values first. |
