# Greet callers by name

## Requirement

"Greet callers by name when we know them."

## Pieces

- **Custom data**: a collection `customers` with `phone` declared as the
  identity field, so its `get_customers` tool looks a customer up by number.
- **The lookup as a pre hook**: `get_customers` on the agent's
  `pre_hook_tool_ids`, with its `phone` parameter fixed to
  `$ref:conversation.number`. The hook runs before the first word and its
  answer is injected as context; the number comes from the conversation, on
  either leg of a call.
- **Node instructions** that say to use the name when the lookup answered.
- A published agent.

## The shape to avoid

The customer list in the prompt. It goes stale the day it changes, it puts
every customer's name in front of the model on every call, and the personal
data escapes the retention window that a collection's `personal` fields
respect.

Also the in-conversation lookup: the tool on the node and instructions that
ask the caller for their number. It works, but it makes the caller say a
number the platform already knows, and the greeting cannot use the name
because the lookup has not happened yet.

## Steps

Ids from an earlier step are written as `{{step_N.field}}`; the test that runs
this page substitutes them.

1. The customers. Both fields are personal.

   `create_collection`

   ```json
   {
     "name": "Customers",
     "slug": "customers",
     "fields": [
       {"key": "phone", "label": "Phone", "type": "text", "required": true, "personal": true},
       {"key": "name", "label": "Name", "type": "text", "required": true, "personal": true}
     ]
   }
   ```

2. Declare the identity field. It is set once the fields exist, and it is
   what gives `get_customers` a `phone` parameter instead of a `record_id`. A
   phone number written to it is stored in E.164, so two spellings of one
   number are one customer.

   `update_collection` (`collection_id` = `{{step_1.id}}`)

   ```json
   {"identity_field_key": "phone"}
   ```

3. A customer.

   `create_record` (`collection_id` = `{{step_1.id}}`)

   ```json
   {"values": {"phone": "+34600000001", "name": "Marta"}}
   ```

4. Fix the lookup's `phone` to the number the conversation opened with.
   `get_tool_id` on the collection is the tool's id. A `fixed_parameters`
   value written `$ref:conversation.<key>` is resolved when the tool runs: an
   inbound call opens with the caller's number under `number` (and the
   dialled one under `called_number`), an outbound call with the contact's
   under the same `number`. The model no longer sees `phone` at all.

   `update_tool` (`tool_id` = `{{step_1.get_tool_id}}`)

   ```json
   {"fixed_parameters": {"phone": "$ref:conversation.number"}}
   ```

5. An agent publishes only with an AI profile.

   `create_llm_settings`

   ```json
   {"name": "fast", "provider": "openai", "model": "gpt-4o-mini"}
   ```

6. The profile.

   `create_profile`

   ```json
   {"name": "default", "text": {"llm_settings_id": "{{step_5.id}}"}}
   ```

7. The agent.

   `create_agent`

   ```json
   {"name": "Reception"}
   ```

8. Its draft.

   `get_draft` (`agent_id` = `{{step_7.id}}`)

   ```json
   {}
   ```

9. The root node. No tool on it: the lookup has already happened by the time
   the node is reached, and its answer is in the context. The instructions
   say what to do with it.

   `add_node` (`graph_id` = `{{step_8.graph_id}}`)

   ```json
   {
     "instructions": "The context may hold the answer of get_customers. If it holds a record, greet the caller by the name in it and use it from then on. If it holds no record, or nothing at all, greet them without a name and do not guess one."
   }
   ```

10. Named as the root.

    `set_root` (`graph_id` = `{{step_8.graph_id}}`, `node_id` = `{{step_9.id}}`)

    ```json
    {}
    ```

11. The profile on the draft, and the lookup as the pre hook.

    `edit_draft` (`version_id` = `{{step_8.id}}`)

    ```json
    {"ai_profile_id": "{{step_6.id}}", "pre_hook_tool_ids": ["{{step_1.get_tool_id}}"]}
    ```

12. Published.

    `publish_version` (`version_id` = `{{step_8.id}}`)

    ```json
    {}
    ```

## What the agent sees

No tool. Before the first word, one line of context: `get_customers`'s
answer, `{"record": {...}}` with the name or `{"record": null}`. The
instructions decide what to say with it.

## Variations

- **A browser test call** opens with no number, so the hook is handed no
  `phone` at all and answers that there was nothing to look up by; the agent
  greets without a name. Test the greeting by calling the published number.
- **Outbound calls from a campaign**: the same hook serves, because the
  contact's number opens the call under `number` too. The contact's record
  also travels with the call as context, so a campaign that already knows
  the name needs no lookup.
- **A returning caller's last order**: a second field on the same record, or
  a second collection `orders` whose `query_orders` tool sits on the root
  node; the instructions say what to mention.
- **Writing a new caller down**: attach `write_customers` (`write_tool_id`)
  to the node; it asks the person before it writes, because
  `requires_confirmation` is on by default for a collection's write tool.
