> ## Documentation Index
> Fetch the complete documentation index at: https://docs.telli.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Collect Data

> Let your agent gather specific information from callers with built-in validation and confirmation.

The Collect Data tool lets your AI agent gather specific information from callers-email, phone, license plate, address, or any custom field-with built-in validation and confirmation. A specialized sub-agent temporarily takes over: it asks for the data, handles voice quirks ("dot" for ".", letter-by-letter spelling), validates, reads it back, and only accepts it once the caller explicitly agrees. Then it hands control back to your main agent with the confirmed value.

<video loop muted playsInline style={{ width: '100%', cursor: 'pointer' }} onMouseEnter={(e) => e.currentTarget.play()} onMouseLeave={(e) => { e.currentTarget.pause(); e.currentTarget.currentTime = 0; }}>
  <source src="https://mintcdn.com/telli/yTwqc2QbhCfx6_2_/onboarding-media/Cookbooks/CollectData/CB_CollectData_01_CreateTool.mp4?fit=max&auto=format&n=yTwqc2QbhCfx6_2_&q=85&s=61ac9da0234ca3572b056f1c7e3ae398" type="video/mp4" data-path="onboarding-media/Cookbooks/CollectData/CB_CollectData_01_CreateTool.mp4" />
</video>

***

## Data Types

telli has five built-in data types, each with its own specialized sub-agent:

| Type              | What it collects                                                                   | Validation                                                      |
| ----------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| **Email**         | An email address; converts "at" to `@`, "dot" to `.`, spells back letter by letter | Valid email format ([user@domain.tld](mailto:user@domain.tld))  |
| **Digits**        | A number sequence-IDs, codes, PINs, phone numbers                                  | Digits only; optional min/max length                            |
| **License Plate** | A vehicle plate (currently German plates only)                                     | Parsed against official format; offers city-name disambiguation |
| **Address**       | Full postal address (street, city, country, optional postal code)                  | Checked against Google Address Validation API                   |
| **Custom**        | Free-form field for anything else (contract numbers, serials, company names)       | Optional custom constraints (see below)                         |

<Warning>
  **How It Works During a Call**

  1. **Main agent calls the tool**-Based on your prompt, it calls `collect_data` with the relevant key(s).
  2. **Sub-agent takes over**-A type-specific sub-agent takes control; the main agent pauses.
  3. **Collection flow**-The sub-agent asks for the data, processes voice quirks, validates, re-asks on failure, reads the value back, and waits for explicit confirmation in a **separate speech turn** (so it can't confirm its own readback).
  4. **Outcome**-Either **confirmed** (caller agreed) or **declined** (caller refused, with a reason).
  5. **Main agent resumes**-Control returns with the value, and the main agent is instructed to never re-ask or re-confirm an already-confirmed value.

  You can collect multiple fields in one call (e.g. `@collect_data` for email and case\_number together); the sub-agents run in sequence within the same task group.
</Warning>

<Tabs>
  <Tab title="Behavior Modes">
    Every task has a behavior setting:

    * **Auto (default)**-The sub-agent runs its built-in collection flow (ask, validate, read back, confirm). Optimized for accurate voice collection; no configuration needed. Best for most use cases.
    * **Custom Prompt**-Additional instructions (max 4,000 characters) appended to the built-in flow. Lets you customize *how* the agent collects without replacing the core validation. For example, greet the caller by name first, reassure a hesitant caller, or ask them to read a code slowly one digit at a time.

    <Note>
      Custom prompts are **appended**, not a replacement-the core validation and confirmation flow always runs regardless.
    </Note>
  </Tab>

  <Tab title="Validation Constraints">
    With the **Custom** data type you can add rules; all must pass for the value to be accepted:

    | Constraint           | What it does                                                                                | Example                                                                                                     |
    | -------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
    | **Exact Length**     | Exactly N characters                                                                        | A 10-character serial number                                                                                |
    | **Min / Max Length** | Between a minimum and maximum (set either or both)                                          | Reference number between 4-10 characters                                                                    |
    | **Alphabet**         | Restrict allowed character types: lowercase, uppercase, numbers, special characters, blanks | Numbers only for a PIN code                                                                                 |
    | **Regex**            | A `fullmatch` pattern plus a human-readable description                                     | Pattern `[A-Z]{2}-\d{6}` with description "two uppercase letters, a dash, then six digits (e.g. AB-123456)" |

    <Note>
      You can combine multiple constraints, but **Exact Length** and **Min/Max Length** are mutually exclusive.
    </Note>
  </Tab>
</Tabs>

***

## Integration & Prompt

<Tabs>
  <Tab title="What You Get Back">
    Collected data appears in the `call_ended` webhook under `collected_data` (or `null` if no tasks were triggered):

    ```json theme={null}
    {
      "collected_data": {
        "email": {
          "task_type": "email",
          "status": "confirmed",
          "value": "john.smith@example.com"
        },
        "case_number": {
          "task_type": "digits",
          "status": "declined",
          "value": null
        }
      }
    }
    ```
  </Tab>

  <Tab title="When to Use It">
    **Use it when** you need validated, confirmed data with a specific format (email, phone, ID with a known pattern), accuracy matters, and you want the value in a structured webhook field rather than buried in a transcript.

    **Don't use it when** the information is conversational (a name, a greeting), you only need it for context during the call, or the spell-back-and-confirm flow would feel tedious for simple low-stakes data.
  </Tab>

  <Tab title="Prompt Patterns">
    Reference tasks with `@collect_data:key`. Common patterns:

    ```
    ## Data Collection

    # Basic
    When the caller needs to update their email, use @collect_data:email.

    # Conditional
    Only collect email (@collect_data:email) if they ask for a confirmation to be sent.
    Always collect the case number (@collect_data:case_number) at the start of every call.

    # With fallback
    Try @collect_data:email. If they decline, continue and note no email was provided.

    # Multiple fields
    At the start, collect both @collect_data:customer_id and @collect_data:email for verification.
    ```
  </Tab>
</Tabs>

***

## Outcomes vs. Collected Data

|                      | Collected Data                     | Call Outcomes                                     |
| -------------------- | ---------------------------------- | ------------------------------------------------- |
| **When it runs**     | During the call                    | After the call                                    |
| **How it works**     | Sub-agent asks the caller directly | AI reads the transcript                           |
| **Caller involved?** | Yes-they provide and confirm       | No-fully automated                                |
| **Accuracy**         | Very high (validated + confirmed)  | Depends on transcript and instructions            |
| **Best for**         | Emails, IDs, codes, addresses      | Sentiment, intent, summaries, classifications     |
| **Format**           | Always a string                    | Boolean, string, number, category, multi-category |

<Tip>
  Use both together: collect the email with `@collect_data:email` (verified), and use a call outcome to score whether they're a qualified lead (AI analysis).
</Tip>

***

## Workflows-Automating What Happens After the Call

Workflows are telli's visual automation system: define what happens after a call ends-send a webhook, update your CRM, fire an email, schedule a follow-up-based on collected data and call outcomes. Each workflow is linked to a specific agent and triggers on **Call Ended** (call data available) or **Contact Created** (no call data).

<Tabs>
  <Tab title="Data Available in Workflows">
    A `Call Ended` workflow can reference everything from the call; values are unwrapped automatically (`{{callOutcome.sentiment}}` gives `"positive"` directly):

    | Category           | Reference Syntax        | Examples                                     |
    | ------------------ | ----------------------- | -------------------------------------------- |
    | **Collected Data** | `{{collectedData.key}}` | `{{collectedData.email}}`                    |
    | **Call Outcomes**  | `{{callOutcome.name}}`  | `{{callOutcome.sentiment}}`                  |
    | **Call Metadata**  | `{{call.field}}`        | `{{call.id}}`, `{{call.duration}}`           |
    | **Contact**        | `{{contact.field}}`     | `{{contact.firstName}}`, `{{contact.email}}` |
  </Tab>

  <Tab title="Example">
    ```
    [Call Ended]
        ↓
    [If: collectedData.email is not null]
        ├── True:
        │   └── [Webhook: POST to your API]
        │       Body: {
        │         "email": "{{collectedData.email}}",
        │         "sentiment": "{{callOutcome.sentiment}}",
        │         "summary": "{{callOutcome.summary}}",
        │         "call_id": "{{call.id}}"
        │       }
        └── False:
            └── [Send SMS: "We missed your email-reply to provide it."]
    ```
  </Tab>
</Tabs>

***

## Full Example

```
During the Call                          After the Call
─────────────                            ──────────────

Caller provides email                    AI reads transcript
  → @collect_data:email                    → sentiment = "positive"
  → Sub-agent validates + confirms         → appointment_booked = true
  → Stored as collectedData.email          → Stored as callOutcome.*

                    ↓                                    ↓
                    └──────────── Both feed into ────────┘
                                      ↓
                              [Workflow Trigger] "Call Ended"
                                      ↓
                        [Actions] Webhook, CRM, SMS,
                              schedule follow-up...
```

**Collected Data** gives verified, caller-confirmed input. **Call Outcomes** give AI-derived analysis of what happened. **Workflows** let you act on both-automatically, every time.

***

## Tips and Best Practices

* **Use descriptive keys**-`customer_email` beats `email_2` when reading webhook payloads.
* **Write clear descriptions**-They tell the AI *when* to use each task. "Collect the email when they request an order confirmation" beats "Email collection."
* **Set appropriate constraints**-If a code is always 8 characters, set an exact length so partial input is rejected.
* **Don't over-collect**-Each task adds a formal ask-validate-confirm flow. Reserve it for data where accuracy and structure matter.
* **Test with voice**-The flow is optimized for voice; call your agent and dictate data to check it feels natural.
* **Use Auto first**-Built-in flows are well-tested. Switch to Custom when you need specific adjustments.
* **Check the status field**-Don't assume every value is confirmed. Handle `declined`, `error`, and `in_progress` gracefully.
