# Add contacts & schedule calls
Source: https://docs.telli.com/add-contacts-and-schedule-calls
Add contacts to telli from your CRM or other systems and schedule calls with them
## Prerequisites
* A telli account with API access
* An API key from your telli dashboard (Settings > Developer)
* A CRM system, database, or automation platform (Zapier, Make, n8n) with contacts to sync
* Contact data including: first name, last name, and phone number in an [accepted format](/phone-number-format) (email and timezone are optional)
You can add contacts to telli programmatically using the API, enabling seamless integration with your CRM, database, or other systems.
## Add a single contact
Add one contact at a time using the `/v1/add-contact` endpoint:
```json theme={null}
POST /v1/add-contact
{
"external_contact_id": "crm-12345",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+14155552671",
"email": "john.doe@example.com",
"timezone": "America/New_York",
"contact_details": {
"company": "Acme Corp",
"notes": "Interested in product demo"
}
}
```
See the [Add Contact endpoint documentation](/v1/endpoint/add-contact) for complete details.
## Add multiple contacts (batch)
Add multiple contacts efficiently using the `/v1/add-contacts-batch` endpoint:
```json theme={null}
POST /v1/add-contacts-batch
{
"contacts": [
{
"external_contact_id": "crm-12345",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+14155552671",
"email": "john.doe@example.com"
},
{
"external_contact_id": "crm-12346",
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+14155552672",
"email": "jane.smith@example.com"
}
]
}
```
See the [Add Contacts (Batch) endpoint documentation](/v1/endpoint/add-contacts-batch) for complete details.
## Schedule a single call
After adding a contact, you can schedule a call using the `contact_id` returned from the add contact response. Use the `/v1/schedule-call` endpoint:
```json theme={null}
POST /v1/schedule-call
{
"contact_id": "6bd1e7e0-6d00-4c0b-ad5b-daa72457a27d",
"agent_id": "d8931604-92ad-45cf-9071-d9cd2afbad0c"
}
```
See the [Schedule Call endpoint documentation](/v1/endpoint/schedule-call) for complete details.
## Schedule multiple calls (batch)
Schedule calls for multiple contacts at once using the `/v1/schedule-calls-batch` endpoint:
```json theme={null}
POST /v1/schedule-calls-batch
{
"contacts": [
{
"contact_id": "6bd1e7e0-6d00-4c0b-ad5b-daa72457a27d",
"agent_id": "d8931604-92ad-45cf-9071-d9cd2afbad0c"
},
{
"contact_id": "7ce2e8f1-7e11-5d1c-be6c-ebb83568b38e",
"agent_id": "d8931604-92ad-45cf-9071-d9cd2afbad0c"
}
]
}
```
See the [Schedule Calls (Batch) endpoint documentation](/v1/endpoint/schedule-calls-batch) for complete details.
## Best Practices
1. **Use external\_contact\_id**: Always provide your CRM's contact ID to maintain the relationship
2. **Batch when possible**: Use batch endpoints for importing multiple contacts and scheduling calls
3. **Handle errors**: Check the response for any failed contacts in batch operations
4. **Store contact\_id**: Save the returned `contact_id` to link telli contacts back to your system and schedule calls
5. **Schedule after adding**: After adding contacts, immediately schedule calls using the returned `contact_id` to automate your workflow
# Getting Started with the telli API
Source: https://docs.telli.com/api-getting-started
Start building with the telli API: authenticate with your API key, explore the endpoints, and download the OpenAPI specifications
The telli API is a REST API with JSON request and response bodies. Use it to manage contacts and contact properties, trigger and schedule calls, retrieve call results, and work with agents and phone numbers. For real-time call events, pair it with [webhooks](/webhooks).
If you would rather integrate without writing code, telli also connects to CRMs and automation platforms. See the [integrations overview](/integrations-overview).
**Authentication**
All endpoints authenticate with an API key from the telli app (Settings > Developer), sent as a bearer token:
```bash theme={null}
Authorization: Bearer
```
## OpenAPI specification
Every endpoint in this reference is generated from the API's OpenAPI files. Download them to generate client libraries, import the API into tools like Postman, or give coding agents machine-readable API context:
| Specification | Covers | Download |
| ------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| V2 API | Contacts, contact properties, and agents, plus the [webhook](/webhooks) event payloads | [https://docs.telli.com/openapi-v2.json](https://docs.telli.com/openapi-v2.json) |
| V1 API | Calls and phone numbers, plus the deprecated contact endpoints | [https://docs.telli.com/openapi.json](https://docs.telli.com/openapi.json) |
Contact endpoints exist in both versions. Use V2 for new integrations, and see the [migration guide](/v2-migration-guide) if you are still on the deprecated V1 contact endpoints.
## Base URLs
| Service | Base URL |
| ------------------------------- | ------------------------------------------------------ |
| API | [https://api.telli.com](https://api.telli.com) |
| [MCP server](/integrations/mcp) | [https://mcp.telli.com/mcp](https://mcp.telli.com/mcp) |
| telli app | [https://app.telli.com](https://app.telli.com) |
# Connect your calendar
Source: https://docs.telli.com/calendar-integrations
Choose the right calendar integration for your telli agents and configure booking workflows for Calendly, Zeeg, Cal.com, HubSpot Meetings, or a custom calendar API.
## Overview
telli supports multiple calendar integrations so your agents can check availability and book appointments during calls.
In the telli app, open an agent and go to **Calendar Integration** to choose the calendar setup that fits that workflow.
## Integration options
### Calendly
Use Calendly when you want telli to book against an existing Calendly event type and fill booking fields dynamically during the call.
* [Calendly](integrations/calendly)
### Zeeg
Use Zeeg when you want telli to book against existing Zeeg scheduling pages and fill custom invitee questions during the call.
* [Zeeg](integrations/zeeg)
### Cal.com
Use Cal.com when your team already manages scheduling through Cal.com and you want agent-specific event routing.
* [Cal.com](integrations/cal-com)
### HubSpot Meetings
Use HubSpot Meetings when appointments should be booked through HubSpot meeting links.
* [HubSpot Meetings](integrations/hubspot-meetings)
### Custom calendar
Use a custom calendar when you want telli to call your own scheduling API instead of a built-in provider.
* [Custom calendar](integrations/custom-calendar)
## Which option should you choose?
* Choose **Calendly**, **Zeeg**, **Cal.com**, or **HubSpot Meetings** when you already use one of those scheduling tools
* Choose **Custom calendar** when you need telli to work with your own booking system or internal scheduling API
* Configure calendar integrations per agent when different agents should book for different teams, reps, or workflows
# Changelog
Source: https://docs.telli.com/changelog
We're constantly shipping new features and improvements. Here's what's new in telli.
## Do more with Charlie
Charlie can now handle more day-to-day work across telli. Ask Charlie to create, update, delete, or schedule contacts; manage contact properties; rename or duplicate agents; and rename, duplicate, reset, or enable workflows. Charlie asks for confirmation before sensitive changes, such as deleting a contact, discarding a draft, or enabling a workflow.
### Full call transcripts in workflows
[Call ended workflows](https://app.telli.com/_/workflows) can now use the full plain-text call transcript through `call.transcript`. Use it in conditions and action mappings to pass the conversation to follow-up steps, integrations, or webhooks without assembling individual transcript turns.
Filter [Conversations](https://app.telli.com/_/conversations) by calls shorter or longer than a selected duration
Choose GPT-5.6 Luna in an agent's advanced model settings
Failed workflow HTTP requests now show their response status and body in the run history
Outbound calls stop before dialing emergency and crisis short codes
Callback confirmations now name the weekday and use clear relative dates such as today or tomorrow
Set a SIP From user and host for each number on a custom SIP trunk
iOS screened calls no longer cause duplicate greetings or continue the screening prompt after the recipient answers
Knowledge bases show Processing immediately after file changes are saved
Warm transfers now wait for the supervisor before the briefing and accept short consent answers without speaking over them
Relative and yearless dates in custom call outcomes now use the call's local time
Contact responses now expose current Auto Dialer enrollment as `auto_dialer_status` in V1 and `autoDialerStatus` in V2
## Automatic data retention
telli now supports automatic data retention. If data retention is included in your plan, set how long contacts and call data are kept under [Settings > Organization](https://app.telli.com/_/settings/organization). Choose from 1 to 730 days for contacts, all call data, recordings, or transcripts. telli applies the rules daily and asks for confirmation before saving.
### More reliable warm transfers
Set between 1 and 10 attempts for each warm-transfer destination and review every dial, briefing, and outcome in the conversation timeline. Agents wait for active transfers instead of reporting failure early, then stop after the limit. New destinations start with three attempts; existing destinations stay unlimited until you set a limit.
### Better progress during longer tool calls
Voice agents now keep callers informed when a tool takes longer instead of falling silent. Custom HTTP tools with an in-progress message can wait up to 30 seconds for a result.
Ask Charlie to find affected calls and submit confirmed feedback for exact transcript turns or audio ranges
Choose Cartesia Sonic 3.6 or ElevenLabs v3 Conversational for supported voices in the Agent Builder and test calls
Known contact names now improve transcription accuracy with Soniox and supported Deepgram models
Type `{` for variable suggestions or `/` for tools and variables; malformed variables with spaces are now highlighted correctly
Calendar tools can search caller-requested date ranges up to 90 days beyond the default two-week window
Call feedback from the Agent Builder conversation tab now saves without a reload or return to the editor
Generated agent drafts now name the selected language instead of incorrectly naming English
Deleted workflow runs no longer remain in workflow lists, counts, or detail views
## Annotate call transcripts
You can now annotate a specific moment while reviewing a call transcript in [Conversations](https://app.telli.com/_/conversations). Hover an agent, contact, or tool turn to report an issue inline, then adjust the highlighted audio range and describe what went wrong. telli suggests an issue category, and saved annotations stay connected to the transcript and waveform so your team can return to the exact moment or ask Charlie to help fix it.
Review calls with a zoomable waveform, recording-aligned transcripts, turn timing details, and feedback markers in the conversation list
Call ended workflows can opt into failed calls and route on numeric SIP status codes, with trigger, start, and end timestamps available in conditions and mappings
Changes under [Settings > Organization](https://app.telli.com/_/settings/organization) now save more safely, with an explicit Save button for the inbound-call fallback, unsaved-change warnings, and confirmation before disabling call recordings or EU AI Act compliance
Edit your first and last name under [Settings > Profile](https://app.telli.com/_/settings/profile); valid changes save when you leave the field
Contact search now matches contact IDs and hyphenated external IDs in the contact list and through Charlie
Accounts can define up to 500 contact properties
The API docs now start with authentication, base URLs, and direct downloads for the V1 and V2 OpenAPI specifications
Voice agents no longer speak leaked control markers or function-call names
Calendly bookings no longer time out after a second availability check or create an appointment after the agent reports a failure
Address validation accepts empty optional fields and gives the agent actionable guidance when a value is invalid
Charlie conversations and feedback no longer skip or repeat rows during pagination
Subscribe to the new `call_rescheduled` webhook to receive the selected schedule, assigned agent, destination number, and contact data whenever a later call time is saved
## Branded SMS
You can now send SMS with your brand name instead of a phone number. [Request the sender name you want to use](https://app.telli.com/_/telephony/verifications?tab=sms-sender-ids), then select the approved sender ID in an agent's Send SMS tool or in a [workflow](https://app.telli.com/_/workflows). Before sending, telli checks that the sender ID is supported in the destination country.
Charlie now stays alongside the current page in a resizable workspace, keeps fullscreen mode after a refresh, and opens with selected transcript messages attached as context
Warm transfers can require the recipient to press `1` before the contact is connected, preventing hold music or voicemail from accepting a transfer
Scheduled and batch calls can run once on eligible outbound plans without Auto Dialer; automatic retries and custom dialing strategies remain upgrade features
Add the optional Next Call column to the contacts table to see each contact's next scheduled call and whether it is already queued
Salesforce integrations can sync Person Accounts into telli contacts when Person Accounts are enabled in Salesforce
Signup suggests requesting an invite when your work-email domain matches an existing telli workspace, while still letting you create a separate workspace
Self-serve accounts can complete five connected calls before onboarding grace ends, up from one
Reconciled call transfers now show their duration in the call details
Plan and billing-interval changes for active subscriptions open a prefilled support request instead of failing at checkout
Transfer tools no longer crash the Agent Builder when configurations are incomplete or removed
Contact CSV imports accept displayed labels for select fields, including umlaut and transliterated variants
Newly purchased telli phone numbers no longer fail provisioning while Twilio propagates them between regions
Validated address results include latitude and longitude in V1 call responses and `call_ended` webhooks when coordinates are available; call details show the same captured coordinates
Webhook documentation now provides generated payload schemas for each event, with clearer setup, signature verification, and retry guidance
## Delay steps in workflows
You can now add delay steps to [workflows](https://app.telli.com/_/workflows) before the next step runs. Set the pause in days, hours, minutes, or seconds. telli validates every path and prevents publishing when its combined delays exceed 30 days.
### Control when follow-up calls can happen
For each agent, choose whether follow-up calls can happen at any time, during the agent's calling window, or within custom hours. Blocked dates and the maximum scheduling horizon are enforced when the call is arranged. Follow-up calls also work when Auto Dialer is disabled.
Owners of self-serve subscriptions can cancel at the end of the current billing period and resume before the cancellation takes effect under [Settings > Billing](https://app.telli.com/_/settings/billing)
Owners and admins can permanently delete a call recording from the call details while retaining its transcript and analysis
[Settings](https://app.telli.com/_/settings) now use a dedicated sidebar with clearer subpage navigation, breadcrumbs, and a direct path back to the app
Billing shows usage against plan allowances, including overages and exhausted limits
Auto Dialer status, total calls, connected calls, and the Auto Dialer filter are now available for every account in the contacts table
Inbound SIP configuration for supported telli numbers now includes media encryption and allowed IP or CIDR ranges
Failed outbound call starts no longer leave contacts or calling loops stuck in a running state
Scheduled follow-ups stay intact when calling windows change, declined follow-ups are cleared reliably, and rejected times are no longer confirmed by the agent
Deleting a contact now redacts associated text-message content and contact addresses
Charlie shows actionable errors instead of silently stopping, and oversized PDF attachments are rejected before submission with a clear page limit
App loading and top-level errors no longer produce blank pages or disruptive layout shifts
The mobile sidebar closes after opening the selected page instead of covering it
More English and German iOS call-screening phrases are recognized reliably, including prompts that appear later in the screening flow
Successful checkout no longer reopens the paywall, and upcoming or sales-managed subscriptions show the correct state and name
V1 call responses and `call_ended` webhooks no longer return stale or conflicting `call_status` values
## EU AI Act compliance
You can now check and enforce AI disclosure for voice agents. For European accounts, EU AI Act compliance is enabled by default, with each agent's status shown directly in the builder. The check looks for disclosure within the first four spoken sentences, and Charlie can help fix missing disclosures.
### Appointments with BookingTime
Connect [BookingTime](https://app.telli.com/_/settings/integrations/bookingtime), choose an organization and appointment type for each agent, and let the agent find availability and create appointments during calls. When details such as the caller's name or email address are missing, the agent collects them before completing the booking.
The same phone number can now handle inbound calls for an agent while remaining available for outbound calls
Self-serve accounts can explore telli before choosing a plan; gated actions open the plan selector, where the current plan and billing interval are clearly marked
Long Charlie conversations stay responsive while answers stream instead of slowing down as the conversation grows
Charlie conversations now have direct URLs, so you can bookmark or share a conversation and reopen it from any app page
When you ask where to find or configure something, Charlie can take you directly to more relevant pages across telli
Organization owners and admins can set, replace, or remove the organization logo from [organization settings](https://app.telli.com/_/settings/organization)
Paste screenshots and other supported clipboard files directly into Charlie
Knowledge-base uploads respect your plan's storage limit, and files over 20 MB show a clear error before uploading
Warm-transfer timeouts no longer produce tool errors or finalize the same transfer more than once
Large contact exports complete in the background, keep their progress after a refresh, and no longer time out for large datasets
Signup research no longer restarts when its Charlie conversation is added to the URL, and onboarding test calls use the configured agent draft
HubSpot contact sync uses the correct portal after reconnecting and recovers from stale synchronization cursors
Collect Data tasks enforce configured minimum and maximum lengths instead of accepting out-of-range responses
Call responses and `call_ended` webhooks expose transfer destination and completion time through `transfer`
Connected inbound calls now populate successful `sip_status` and `sip_status_code` values
## From signup to your first agent
Getting started with telli is now fully self-serve. Sign up with any email address, let Charlie research your company and build a tailored first voice agent from proven production patterns, make a test call, then choose the plan that fits.
### Start from a proven use case
Charlie recommends relevant jobs for your agent based on your company website. Choose one and Charlie creates the full instructions, opening messages, and call outcomes from a catalog modeled on 20 successful production agents. A test-call action appears as soon as the agent is ready, and the conversation stays open so you can keep refining it after the call.
The agent builder puts the core configuration above the sidebar on smaller screens and lets you collapse long prompt previews
First-time invited users enter their name and accept the policies before joining an organization
Signup and login now have separate routes with clearer, intent-specific copy
telli follows your browser or operating-system language until you explicitly choose a language
Optimized raster images reduce the download size of sign-in and affected app pages
When a plan or token allowance blocks Charlie, the chat explains the correct reason and links directly to plan selection
Zeeg bookings support newer per-location keys, while unsupported custom locations are filtered out instead of failing during a call
Charlie retries after an invalid question configuration instead of waiting for an answer to a card that never appeared
Charlie question cards no longer expose internal tool-call tags in the app, Slack, or saved conversations
Charlie stays open after mobile onboarding navigates to the newly created agent
Expired, canceled, or already-used invitation links show a useful error and any other pending invitations
Phone verification fraud blocks show accurate retry and alternate-number guidance instead of a temporary outage message
Returning to a previous company website during signup refreshes the suggested company name and logo correctly
`call_ended` webhooks include `state`, final `status`, and `follow_up`, matching the modern Calls API fields
Scheduling a call with an invalid `agent_id` returns a structured 400 validation error instead of an internal server error
## Charlie in Slack
Charlie now works in [Slack](https://app.telli.com/_/settings/integrations/slack). Connect a workspace, choose where Charlie should be available, then mention it in a channel to ask about agents, calls, and results without leaving Slack.
### Choose where Charlie works
During setup, select at least one channel for Charlie, then add or remove channels from the integration page as your team changes. If one Slack workspace serves several telli accounts, each channel is assigned to one account and channels already linked elsewhere are clearly marked, so conversations and account data stay separated.
### Keeps threads and workspaces in context
Charlie reads the messages and files already shared in a thread before the mention and posts its answer back into the same thread. When it needs clarification, it can present answer buttons directly in Slack. Muted threads stay quiet until someone mentions Charlie again.
Slack Connect channels route to the correct installed workspace, and replies show which telli account Charlie is working in. Access checks run before Charlie starts work, while direct messages point people to the available linked channels.
Schedule a future call directly from a contact's details, with the agent, date, time, and optional outbound number you want to use
Account owners can review their current plan, subscription status, billing period, and usage under [Settings > Billing](https://app.telli.com/_/settings/billing)
[Call ended workflow triggers](https://app.telli.com/_/workflows) can filter for connected calls, scheduled follow-ups, not connected calls, and voicemail; call status is also available in conditions and variables
Custom HTTP tools support nested JSON objects and recursive arrays in request bodies
Outbound-number selectors include numbers reserved for the selected agent across individual calls, bulk scheduling, CSV imports, and test calls
Charlie's streaming work is grouped into collapsible sections, so multi-step changes are easier to follow
Address collection understands street names spelled with the phonetic alphabet instead of saving the cue words literally
The [MCP integration](https://app.telli.com/_/settings/integrations/mcp) provides setup actions for Claude, ChatGPT, Cursor, Codex, and Claude Code, and lets you review or revoke personal connections
Call outcomes now finalize reliably after post-processing and across every session-close path, instead of remaining stuck or publishing an incomplete result
Outbound calls retry once after a temporary provider 502 instead of immediately marking the contact as not reached
Late voicemail detection corrects the final call outcome instead of leaving the call marked as connected
Ending a call loop no longer marks the active contact as not reached before the call finishes
Switching organizations preserves valid nested pages such as Billing settings instead of landing on a broader page or a 404
Charlie conversations fall back to account context and continue when the focused agent or workflow has been deleted
## Charlie through MCP
You can now connect Claude, ChatGPT, Cursor, Codex, and Claude Code to Charlie through [MCP](https://app.telli.com/_/settings/integrations/mcp). Add the telli MCP server to the AI client you already use, authorize access, then ask Charlie to investigate or complete work in telli without switching tools.
### Connect the tools you already use
The same remote MCP endpoint works with Claude, ChatGPT, Cursor, Codex, and Claude Code. Your client opens telli for OAuth authorization, so you do not need to copy API credentials into another tool. The connection can work with every telli account you can access, with your current role applied separately in each account.
### Continues after a question
Longer requests can keep running while the MCP client checks Charlie's progress. If Charlie needs clarification, the client receives the question, sends your answer back, and continues the same conversation instead of making you restart the task.
Modular voice agents can fall back across STT, LLM, and TTS providers when a provider fails or responds too slowly, so calls are less likely to stall
From [Conversations](https://app.telli.com/_/conversations), select the exact transcript turns that need work or submit written feedback, then open Charlie with that context already attached
Drop files into Charlie from any page and send an attachment without adding a text message
Charlie creates new voice agents through a focused interview, asking one question at a time before building the initial configuration
Workflow notification emails can include only the call-outcome fields you select; existing workflows continue to include all fields by default
Charlie can return generated files as download cards and shows compact progress while completing multi-step work
Conversation searches by phone number respond faster and wait until you pause typing before running
Contact CSV exports show progress, open more reliably in Excel, and keep each contact on one row by replacing line breaks in field values
Charlie responses recover after a refresh or temporary disconnect instead of leaving the conversation stuck on a stale stream
Magic-link codes work even when an email client changes their capitalization or adds surrounding whitespace
Removing a contact from the Auto Dialer no longer leaves the contact displayed as active because of a canceled call loop
Missing or unauthorized calls no longer trigger a misleading login redirect
WhatsApp messages no longer appear in the Scheduled filter, and incompatible call-only filters are cleared when you switch channels
Call responses expose the new final call outcome through `status`, alongside lifecycle and follow-up fields; the legacy `call_status` field remains available for compatibility but is deprecated
## Charlie, everywhere!
Charlie now works everywhere in telli, not just inside the agent builder. Wherever you open it, Charlie adapts to what you have on screen - a workflow, a call, a contact, or your whole set of agents - so you can ask across the account instead of just about the agent you're editing.
### Open Charlie from any page
Ask Charlie is just a keyboard shortcut away. The main sidebar auto-collapses to give the chat room, and a new conversation picks up whatever agent, workflow, or call you had open so you don't have to re-explain the context.
### Builds workflows with you
Charlie can now create and edit [workflows](https://app.telli.com/_/workflows) directly from chat - it drops in the trigger, condition, and action blocks, asks a clarifying question when it needs one, and proposes the publish before anything goes live.
### Reaches across the account
Charlie can now pull contact activity, agent revision history, and calls from anywhere in your account, and inline pills link agents, workflows, and calls straight to their page. Autocontinue is on by default so multi-step answers keep going without a nudge, and pressing Escape stops generation cleanly.
Dutch and French are now available as app languages, including localized emails and date formats
The [Integrations](https://app.telli.com/_/settings/integrations) catalog groups Voice, CRM, and Messaging into tabs, so channel-specific integrations are easier to find
Click any image in the in-app changelog to open it in a fullscreen viewer
Workflow save warnings name the exact fields that are still missing on each node, so you know what to fix
Arrow keys move up and down the [contacts list](https://app.telli.com/_/contacts) after clicking any row, matching how conversation history already worked
Contact imports and the API accept international phone numbers in `0049…`, `00 49…`, and `491…` formats, not just `+49…`
[Workflow](https://app.telli.com/_/workflows) drafts that are saved but not yet published no longer trigger an "unsaved changes" prompt when you navigate away
Hovering an agent's persona in the [agents table](https://app.telli.com/_/agents) shows the full persona description in a tooltip
Inbound calls with a real transcript are no longer stuck as "not connected" and now finalize as completed
The Auto Dialer respects the max retry days limit even when a retry gets snapped into the next day's dialing window
Post-processing no longer refuses to close calls that have a recording attached
The Activity tab in the contact details panel is clickable again
The contact property key input shows an inline validation error instead of silently rejecting invalid keys
Combobox clear (×) buttons work reliably across the app
German labels in the contact filters read correctly again
## WhatsApp Business
telli now supports [WhatsApp Business](https://app.telli.com/_/settings/integrations/whatsapp) alongside voice and SMS. Connect your WhatsApp Business account from Integrations, choose approved templates, and message contacts through the channel they already use.
### Send WhatsApp from workflows
Drop the new Send WhatsApp block into any workflow, pick a connected WhatsApp number, select a Meta-approved template, and map template fields to contact data, variables, or custom values for automated follow-ups.
### Message during a live call
Agents can use a Send WhatsApp tool during calls, so they can send payment links, appointment confirmations, or other approved templates while the conversation is still live.
### Replies in Conversations
Inbound WhatsApp messages are attached to the same conversation history and contact timeline as voice and SMS, with channel filters in Conversations so teams can follow the full thread.
### Auto Dialer settings
The [Auto Dialer](https://app.telli.com/_/dialer) is now its own top-level page in the sidebar. Calling strategy, dialing windows, intensity, max attempts, and max retry days are configured once at the account level, and every agent inherits those defaults. You can still override any of them per agent, and a single "Disable for all agents" switch acts as a global kill switch.
Deleting a node in the middle of a linear [Workflow](https://app.telli.com/_/workflows) chain now reconnects the surrounding nodes automatically instead of cascading and orphaning everything downstream
Switch option group names are editable inline in the Workflow builder, and the selected-node focus ring matches the node's tone (brand, warning, success, destructive)
[HubSpot](https://app.telli.com/_/settings/integrations/hubspot)-synced contacts now show the HubSpot logo in the [contacts list](https://app.telli.com/_/contacts) and lock CRM-managed fields in the details panel, matching how Salesforce-synced contacts already worked
Deep links survive sign-in: opening something like `/_/settings/integrations/hubspot` while logged out now returns you to that exact page after authentication instead of dropping you on the dashboard
Scheduling success toasts name the selected agent in CSV and bulk-schedule flows, so you can confirm at a glance who was assigned
The Workflow publish dialog closes immediately on submit instead of holding a spinner, and the publish button no longer overflows on longer translated labels
Clearer Auto Dialer copy: "Disable everywhere" reads as "Disable for all agents", and no-call days now refer to "scheduled follow up calls" instead of "callbacks"
Contacts are no longer marked as "reached" after a not-reached-only call loop completes
Cal.com event type IDs reject trailing whitespace, which used to save silently and then break bookings at runtime
Knowledge base retrieval keeps every relevant context chunk in the final result instead of slicing some of them away
`POST /v1/phone-numbers/import` accepts an optional `inOutboundPool` boolean (default `true`), so you can import numbers without automatically adding them to the outbound pool
## HubSpot integration
[HubSpot](https://app.telli.com/_/settings/integrations/hubspot) is now a first-class CRM integration in telli. Use the new Create Record and Update Record nodes in Workflows to write contacts, companies, deals, tickets, or any other HubSpot object straight from a call, custom fields included.
### Branded calling for everyone
[Branded calling](https://app.telli.com/_/telephony/verifications?tab=branded-calling) is now available on every telli account. Open Verifications, and once your bundle is verified, assign individual numbers to it so your verified company name shows on the recipient's phone instead of an unknown number.
### Agent-specific outbound numbers
You can now reserve account phone numbers for one agent. The dialer respects the reservation, so that agent only calls from its reserved numbers and stops drawing from the shared pool. Useful when different agents represent different brands, regions, or campaigns and need separate caller IDs.
### Cal.com EU support
If your team is on the EU instance of [Cal.com](https://app.telli.com/_/settings/integrations/cal-com) (`api.cal.eu`), you can finally connect it to telli. The integration setup now includes a Region selector, so your availability lookups and bookings route to the right Cal.com tenant.
Importing contacts from CSV no longer blocks the request while we schedule them into call loops. The scheduling runs in the background and you see live progress in the import success step
You can force the language for call outcome analysis from the post-processing panel, so multilingual or short calls don't get classified in the wrong language
Transfer schedules and agent dialing windows can now respect your account's no-call days, so transfers and outbound calls skip public holidays automatically
Phone number validation on contacts gives clearer error messages, and the field is editable on the first click after picking a country
ElevenLabs TTS no longer stutters, repeats, or garbles spoken words
Edits to custom HTTP tools in draft mode now run in test calls; previously the published version was used instead
Voice cloning continues past step 4 again, after a regression in the new voice setup flow
Cal.com bookings: fully-booked seated slots are filtered out of availability, and the booking location field is handled correctly
The country select popover in the phone input closes after picking a country, so the field is immediately editable
Office background noise plays again on calls that requested it
Team invite emails are sent to the correct recipient
Buying a phone number no longer leaves an orphaned Twilio number behind when the purchase request errors out
## Address data collection
The Collect Data task has a new address type. Add it to an agent and it walks the caller through providing a complete mailing address (street, city, postcode, country) in one structured step, validating the input and writing the parsed fields back onto the contact. It joins the existing email, license plate, digits, and generic collectors as a first-class option in the builder.
### Insert workflow blocks from edges
Hover the line between two connected workflow nodes and a small plus appears. Click it to drop a new block in right there, instead of rebuilding the chain through the picker just to slot something in the middle.
### Conversations
Voice and SMS now sit together on the new [Conversations](https://app.telli.com/_/conversations) page. The audio player has 5-second left/right seek shortcuts, a Copy ID action on the header, and red markers on the waveform wherever you've left negative feedback so problem spots are easy to find. SMS leaves beta this week, and Conversations is set up to absorb more channels as we add them.
The Contacts empty state now shows a one-click Connect button for Salesforce, so new accounts can wire up their CRM without leaving the page
Settings tabs scroll horizontally on narrow viewports, so the tab strip no longer overflows or gets clipped
Destructive confirmations (delete, disconnect, block, unassign) all use a consistent red action button, so irreversible actions are easier to spot
The workflow builder shows a loading state while the canvas mounts, instead of briefly flashing an empty page
Long message rows in conversation history are now width-constrained, so transcripts stay readable on wide screens
The agent builder sidebar shows a scroll shadow whenever there's more content below the fold
Voice cloning from a recorded sample works again, after a regression in last week's voice tooling
Custom HTTP tools can now leave JSON mode even when the JSON editor contains invalid JSON, so you're not stuck reformatting before you can switch back
Outbound callbacks scheduled after an inbound call no longer fire a duplicate dial
Pasting a multi-line value into a templated-string field preserves newlines instead of collapsing them
The frontend error page silently retries once before reporting, so transient chunk-load failures right after a deploy stop showing up as crashes
Dark mode is fixed on the "Success" and "Your agent is ready!" account-setup screens
Zeeg's duration picker stays visible while you select a duration, and the options now match what Zeeg actually offers
The active workflow node renders above its neighbors instead of being clipped
The `call_ended` webhook payload (and `GET /v1/calls/:id` plus the list endpoint) now include `sip_status_code` (integer) and `sip_status` (string), exposing the SIP-level outcome we were already storing on the call
## Zeeg calendar
telli now supports [Zeeg](https://app.telli.com/_/settings/integrations/zeeg) as a calendar provider alongside Calendly. Connect with an API key, pick a scheduling page, and your agents can check availability and book meetings during a call. Custom invitee questions map to booking fields, so the agent captures the right data and writes it straight to Zeeg.
### Name your versions on publish
You can now give a name to every published version of an agent or a workflow. telli auto-suggests a short title from the diff and you can edit it inline before publishing. Version history, the version picker, and related dropdowns show the title alongside the date and version number, so older revisions are easier to scan.
### Custom prompts for Collect Data tasks
The Collect Data task in the agent builder has a new Prompt behavior. Pick Auto to keep the default sub-agent instructions, or write your own prompt to steer how the email, license plate, generic, or digits task asks for the value, confirms it, and handles edge cases - without touching the main agent prompt.
Agents can now stay on a call for up to 2 hours - the builder slider, validation, and call execution all respect the new limit
When buying a phone number, the selection and confirmation now show Voice and SMS capability badges, so you can pick the right number for the job
Scheduled calls in the contact timeline now show the agent name and the from-number, matching how past calls already appear
Discarding an agent draft now opens a full diff dialog instead of a plain confirm, so you can see exactly which changes you'd lose
The Variables panel's referenced indicator now picks up `{{variable}}` usage in the inbound and outbound first messages, not only in the main prompt
The active tab in the contact details panel (Details or Activity) is now kept in the URL, so deep links and reloads land on the same tab
Inbound phone numbers missing a leading `+` are now normalized, so calls from those numbers route correctly again
Up/down arrow navigation in conversation history now works when focus is in the right detail pane, not only in the left list
The agent builder prompt scrolls reliably again - the resize handle no longer steals the scroll area
The send-SMS-from-number control no longer renders with broken spacing
Inbound SMS now show the correct source label in the contact timeline
Press `Delete` or `Backspace` with selected contacts to open the bulk delete confirmation - typing in form fields is unaffected
Press `Escape` to close an open workflow node panel and return to the workflow canvas
## SMS
telli now supports SMS alongside voice. You can text contacts from a workflow, agents can send templated SMS during a live call, and inbound replies land back in the same conversation thread.
### Send SMS from a workflow
Drop the new Send SMS block into any workflow, write your template, and pick which telli number to send from. Use it for post-call follow-ups, scheduled-callback confirmations, or any trigger where a quick text is the right answer. Outbound SMS are billed by usage.
### Text during a live call
Configure one or more SMS templates on an agent and the agent gets a runtime tool to send the right one mid-conversation. Useful for sending a payment link, a calendar invite, or a callback confirmation while the caller is still on the line.
### Replies in the same thread
When a contact replies to a telli number, the message is attached to the existing thread and shows up in Conversations and on the contact timeline, so SMS context sits right next to voice context.
Finnish and Danish are now available as agent languages, with matching voice mappings across the product
Agent analytics adapts to your data: turn-count and call-duration buckets scale automatically, and the by-version view has show-all and isolate controls so you can compare versions one at a time
Workflow webhooks support templated URLs and encrypted secret values, so you can fan out to any endpoint and authenticate cleanly
Call durations show in m:ss in the app, and the call-ended webhook and API payload now include `call_length_sec` alongside `call_length_min`
The Schedule Call workflow node warns when the selected agent's Auto Dialer is turned off, so calls don't queue silently
"Call history" is now "Conversations" across the app, reflecting both voice and SMS
The salutation field is back in the manual contact creation form
Safari dialogs no longer overflow on small viewports - applies to the shared dialog primitives and to the calendar and knowledge base configuration dialogs
## Trigger Workflows on new contacts
The [Workflows](https://app.telli.com/_/workflows) trigger picker now includes new contacts. When a contact lands in telli - through CSV import, the API, or any other source - the workflow you've enabled for the contact-created trigger runs automatically. Combine actions like updating a property, calling a webhook, or writing the contact to Salesforce, so every new contact follows the same flow without any manual setup.
### Duplicate any workflow
The Workflows list has a new Duplicate action. One click drops in a copy as a disabled draft, so you can edit the new version without touching the original.
### Send a bad call to Charlie for a fix
When you spot a problem in builder call history, click the new Charlie wand next to the thumbs on any agent reply - or leave negative feedback on a call and pick "Try fix with Charlie". Either way, Charlie picks up the transcript up to that point and proposes a change you can review inline.
Outbound calls now play your voicemail message exactly once, even when the assistant and the background detector both react to the same beep
Auto Dialer settings have a new account-level timezone, used whenever a contact doesn't have its own timezone set
Agents with their own dialing windows now also respect your account-level no-call days, so configured holidays are skipped no matter how an agent's own schedule is set
Press Space to play or pause the audio player anywhere in the dashboard, and every audio button now has a tooltip explaining what it does
Click the title in the agent or workflow breadcrumb to rename it inline - press Enter or click outside to save
`externalId` is available in the agent variable menu, so prompts can reference it directly during a call
Phone numbers now use the same readable international format across the phone-number page, call history, breadcrumbs, and blocked-number dialogs, instead of unformatted numbers
The "Update contact" workflow node has a clearer label and icon, True/False values look consistent across run details and node summaries, and notification email subjects are cleaned up before sending
The contact details panel no longer crashes on older custom property rows, so accounts using legacy properties can open contact pages again
The call status icon now matches the actual call status, instead of always showing the ringing icon
Pressing Escape in the contacts list closes any open details panel, sheet, or dialog first - a second Escape clears the bulk selection
Loading placeholders for blocked phone numbers, call history badges, and filter buttons now match their final sizes, so the layout no longer shifts when data loads
## Filter contacts by property
The [Contacts](https://app.telli.com/_/contacts) table now lets you filter by contact properties. Combine multiple filters to narrow down to the exact contact set you want, then act on the results - schedule calls, cancel pending dials, or open a single contact - without leaving the page.
### Block out no-call days
The auto-dialer and "Call me later" now respect specific dates you mark as no-call days. Set public holidays, company-wide closures, or any individual date in [dialer settings](https://app.telli.com/_/dialer), and telli skips them automatically instead of placing outbound calls when it shouldn't.
### Block phone numbers from reaching your agents
You can now block specific phone numbers from reaching your agents on inbound calls. Add a number from a call's details or directly in Settings → Telephony → [Blocked phone numbers](https://app.telli.com/_/telephony/blocked-phone-numbers), and any future inbound call from that number is rejected before it touches an agent. The list is account-wide, and outbound calling is unaffected.
Import a custom voice from Cartesia or ElevenLabs yourself by pasting its voice ID into voice settings, no request to telli needed
Voice settings let you pin a specific voice model per provider, with a Default option that follows telli's recommendation
Agents can schedule callbacks in relative time, like "in 30 minutes" or "in 2 hours", in addition to absolute times
Cold transfers now support SIP URI targets with optional REFER headers, unlocking handoffs to platforms like Zendesk
Date inputs gained month and year dropdowns, so far-back dates like birthdates are quick to set
Krisp is no longer selectable for background noise reduction in the agent builder - AI Coustics is the recommended replacement, and existing Krisp configs continue to work as legacy
Charlie can now show inline voice preview cards in the builder chat, so you can play samples without leaving the conversation
Knowledge base retrieval now pulls in sibling chunks from the same article, so the agent gets richer context per match
Removing a contact from the Auto-Dialer no longer leaves a stale call loop that blocks deletion of the agent
The Agent Builder no longer crashes intermittently when the chat panel loads
Salesforce surfaces a clear error when the connected user is missing API access or the session is invalid, with guidance to reconnect
Creating a tool with a duplicate name now returns a clear validation error instead of failing generically
## Workflows for every Salesforce object
The [Workflows](https://app.telli.com/_/workflows) Salesforce action is no longer limited to Lead and Contact. You can now create or update any standard or custom Salesforce object. Build a workflow that writes the call outcome and any data your agent captured to the right Task, Case, Opportunity, or custom object after each call, with no extra integration code.
### Persistent chat history in the Agent Builder
Charlie's chats now stick around. Conversations stay available across reloads and sessions, you can switch between past chats from a history menu, and start a fresh one when you want a clean slate. telli auto-generates a short title for each chat so older threads are easy to scan.
A dedicated data collection task captures phone numbers and other digit sequences more reliably than the general task
Workflow conditions display as grouped OR/AND blocks, so complex branching logic is easier to read at a glance
CSV import now clearly flags duplicate external IDs within the file, so you can resolve them before the import runs
Salesforce multipicklist values are now written correctly in post-call automation, instead of dropping selections
Calls no longer fail outright when a contact-stored transfer number is invalid - the agent handles it gracefully at runtime
Outbound callbacks scheduled via "Call me later" now use the same telli number as the original call, instead of switching to a different one
## Workflows
[Workflows](https://app.telli.com/_/workflows) let you automate what happens after a call. Pick a trigger, drop in actions and conditions on the canvas, and decide what telli should do with each completed call without writing custom integration code.
Available action blocks include sending call data to a webhook, updating a contact field or custom property in telli, and creating or updating a Salesforce record when Salesforce is connected. Use If/else and Switch conditions to branch on values from the trigger, the call outcome, or any earlier block, so different calls can take different paths through the same workflow.
Edits stay as a draft while you build, and the canvas flags any block that is incomplete or references a value that no longer exists. Once published, the workflow runs automatically for new completed calls, and you can test it manually from the Runs tab against a real call before relying on automatic execution.
New multi-select option for call outcome field type, with matching filters and email conditions
Set both max retry days and max attempts on smart dialing - the auto-dialer stops on whichever limit hits first
New `agentPhoneNumber` variable lets prompts reference the number the agent is calling from, so the agent can dictate it back to the caller
Transfer destinations in the agent builder now accept variables from the contact, so you can route a transfer to a number stored on the contact record
Outbound calls now show the from-number alongside the contact number in call details
Notification emails for answered calls with a scheduled callback now include the conversation transcript
Audio waveform in call review now highlights the portion matching the messages you select in the transcript
telli phone numbers used for testing are clearly marked with a test-only badge
Builder remembers resized panel widths between sessions, and tooltips on the first message and timer controls explain what each does
Deleting a contact property from Settings now requires a type-to-confirm step, so the action cannot happen by accident
Contacts Reached on the dashboard reflects the correct count again
Builder bubble menu closes when you click outside the editor
Variables panel no longer shows a duplicate tooltip on the referenced indicator
## Meet Charlie, your Agent Builder co-pilot
Charlie is the AI assistant in the Agent Builder chat panel. Tell Charlie what you want the agent to do differently, and Charlie drafts the prompt edits. Review each change inline, accept what looks right, and reject the rest without leaving the builder.
Give Charlie context however fits: type an instruction, drop in a screenshot or document, or hold to record a voice message. Attachments preview in the chat before you send. Not sure where to start? The chat surfaces prompt suggestions like reviewing your agent against best practices, so you can run a quick audit and let Charlie propose the edits.
### Smarter data collection in long sequences
When your agent collects several data points in a row, like email, contract number, and license plate, it now coordinates them as one flow instead of handling each as a separate task. The agent stays on track and stops looping back to ask for values the caller already confirmed, so the conversation feels more natural and the data you capture is more reliable.
GPT-5.4 mini is now available as an LLM choice for your agents
HTTP tools support path parameters, so you can call APIs that put values directly in the URL
Refined diff view between agent versions makes it easier to scan changes before you publish
Builder chat attachments now show inline previews, with a clearer drop area
Navigate between calls in Call History with the up and down arrow keys
Call History defaults to completed calls and opens the most recent one
telli prompts you to refresh when a new app version is released
Selecting messages in the builder chat works reliably again, so you can pull the right snippets from a past call into Charlie
Variable suggestions in the prompt editor no longer get clipped by surrounding panels
New `GET /v1/list-calls` endpoint: list calls and filter by `contact_id` or `agent_id`
## Refine agents from their own call history
The Agent Builder now ties refining an agent and reviewing its calls together in one place. Call History inside the builder gives you an overview, full transcript, and an audio player that follows along with the conversation as it plays, with consolidated filters to find the calls you care about. You can pick individual messages from a past call and bring them into the chat to refine the agent based on what actually happened.
The builder chat, powered by the most capable reasoning model available today, now accepts file uploads and voice input, so you can hand telli more context without leaving the conversation.
### Version history and version-targeted test calls
Version history is more useful too. Before you publish, you can review a diff of what changed against the live version, restore an older revision when something needs to go back, and start a test call against a specific agent version to verify a change before rolling it out.
Builder filters for call history are consolidated into a single menu, so it is easier to narrow down to the calls you want to review
Agent and call outcome filters on the calls page are now searchable, so it is faster to find the right value in long lists
Call transcripts now show which agent ran the call, making it easier to follow conversations across agents
Recording links now resolve correctly when an agent is configured to record only the agent audio
Successful calls no longer show a misleading SIP status alongside the result
Line spacing in the prompt editor is corrected, so prompts are easier to read while editing
The formatting toolbar in the Agent Builder is restored, so you can apply rich text formatting again
## New Agent Builder
You can now build and refine agents in the new Agent Builder. Instead of jumping between separate forms, chat, and settings, the builder gives you one cockpit for prompt editing, tools, call behavior, version history, and publish controls. That makes it easier to iterate on an agent, keep track of draft changes, and decide exactly when a new version should go live.
Day-to-day editing also feels much smoother. You can stay in the same flow while testing ideas, review earlier versions directly in the builder, and publish changes when they are ready instead of treating every edit like an all-or-nothing update.
Czech is now available as an agent language across the product
Call end states are more precise, making it easier to understand why a call stopped
Variable and tool suggestions in the Agent Builder are easier to use and less noisy while editing
Large agent lists are faster to browse
Inbound calls are matched and routed more reliably even when caller numbers arrive in different international formats
Temporary webhook timeouts, rate limits, and short outages are less likely to drop call updates
Non-German calls respond more reliably with improved speech recognition
Call openings are less likely to stall while telli infers salutations
## Debug mode in call details
You can now switch the conversation view in call details into a debug mode that shows the raw tool trace for a call. This makes it easier to inspect tool calls, arguments, outputs, errors, and timing details without leaving telli when you need to understand why a call behaved a certain way.
Debug mode is built directly into the call details experience, so you can move between the conversation and the underlying trace in the same place while reviewing a call.
### Arabic and Turkish language support
Agents now support Arabic and Turkish. You can select both languages in the agent configuration, and the language and voice mappings are available across the product so these setups work consistently end to end.
Warm transfers now support variables in post-dial DTMF, so you can forward dynamic values like IDs or extensions instead of only static digits
Call outcome instructions can now use contact variables dynamically, making it easier to tailor analysis prompts to the current contact
Scheduler names can now be renamed inline on the detail page instead of through a separate edit flow
The Agents page now shows a clearer empty state with a direct Create Agent action for first-time setup
Calendly setup now shows clear errors for invalid API keys and missing permissions, including the required scopes next to the API key field
Outbound calls with permanent SIP errors no longer retry unnecessarily when the destination number cannot be reached
## Call Scheduler
You can now use Call Scheduler for outbound workflows where time to call matters. Sync contacts from your CRM, create a scheduler, and let telli call new matching contacts automatically as soon as they come in. This is especially useful when new leads should be contacted right away.
You can decide when telli should evaluate a contact, delay the first call when needed, and automatically enroll existing matching contacts in the background when you launch a new scheduler.
### Inbound Contact Lookup
You can now identify unknown inbound callers before the conversation starts. In Settings > Integrations > [Inbound Contact Lookup](https://app.telli.com/_/settings/integrations/contact-lookup), configure a webhook that receives the caller's phone number and returns contact details. telli creates or enriches the contact before the agent picks up, so your agent starts the call with the right context immediately.
Failed and not-reached calls now show plain-language SIP status explanations in call details
The audio player now stays visible while you scroll through long conversation transcripts
telli now warns you before queuing contacts while the Auto Dialer is disabled
Voicemail and no-dialogue calls are classified more reliably, reducing wrong follow-ups and repeated retries
Inbound contact matching is more reliable for duplicate contacts and quick consecutive inbound calls
Salesforce connection failures now show clearer errors
## Improved inbound routing and callback configuration
You can now configure inbound calls and callbacks separately. In agent settings, callbacks have their own default action and exceptions, so you can decide when the original agent answers, when a callback is forwarded elsewhere, and when it is rejected. There is also an account-level fallback for unmatched inbound calls when telli cannot map the incoming number to an agent or known callback.
### Warm transfer controls
Warm transfers are easier to fine-tune. You can adjust how the agent briefs the human before the transfer and limit how long the destination phone rings before telli cancels the attempt. This makes handoffs more predictable when nobody picks up and gives you more control over the transfer flow.
### Multilingual agents
Agents can now be multilingual, and the language picker is easier to use across many supported languages. Open the language dropdown in agent settings to scan the full list or choose the multilingual option when one language is not enough for the workflow.
Search directly in the language dropdown when choosing an agent language
Call attempt intervals are easier to read with clearer attempt labels and timeline structure
Toast notifications have a cleaner, easier-to-scan design
The sidebar is restructured into clearer groups, making navigation easier to scan
Call audio is more stable, with fewer cases of choppy voice playback and unexpected noises
Warm transfer behavior is more reliable again after recent transfer issues
## Native Salesforce integration
You can now connect [Salesforce](https://app.telli.com/_/settings/integrations/salesforce) in telli and keep contact and lead data in sync with your workspace. Open Settings > Integrations to connect your Salesforce org, review the connection state, and continue into mappings and sync setup from the same flow.
### Custom dashboard date ranges
You can now inspect dashboard metrics for any custom date range. Open the time picker on the dashboard, switch to "Custom", and choose the exact days you want to review. telli updates the top metrics and charts for that range and compares them against the previous period of the same length.
Contact source visibility is clearer across CRM-synced contacts, including manual creation, import, inbound call, and CRM sync
Integration details now show who installed an integration, making setup ownership easier to understand
Contact properties are now easier to create and manage directly in the UI
Salutations are now inferred across supported languages, not just German
API key rotation is now presented more clearly as a destructive action before the current key is invalidated
Scheduled call times no longer appear in the past across accounts
Long agent names no longer break the picker in call scheduling flows
## Agent-level dialing windows and dashboard improvements
You can now set dialing windows per agent instead of only at the account level. In the agent settings under "Auto Dialer", choose "Override dialing windows" to define a custom schedule for that specific agent. This is useful when different agents serve different regions or should dial at different times of day.
The dashboard now shows 6 key metrics instead of 4, with improved layout and better use of space
Dashboard charts show skeleton loading states while data is being fetched
Infrastructure improvements to the outbound call scheduler for higher throughput on large campaigns
You can now search calls in the telephony page
Fixed dashboard not being scrollable when there are many call outcomes
Fixed contact search breaking when the query contains special characters
Fixed contact sheet not warning about unsaved changes when closing
## Agent data collection and more languages
You can now configure your agent to collect structured data from callers during a conversation. In the agent settings under "Data collection", define tasks for emails, license plates, or custom fields with validation rules. The agent will naturally ask for and confirm each piece of data during the call. After the call, collected values appear in the call details with status indicators.
### More languages
telli agents now support 7 additional languages: Romanian, Swedish, Lithuanian, Hungarian, Bulgarian, Croatian, and Greek. You can set any of these in your agent's language settings.
The contacts table now shows the total number of contacts, so you always know how many records you have
Voicemail messages can now be limited to the first call attempt only, instead of leaving a message on every retry
The default app theme now follows your system preference instead of defaulting to light mode
The contacts table now has proper pagination with page navigation
Fixed scrambled table columns on narrow screen widths
Fixed cursor pagination losing precision on large contact lists
Fixed breadcrumb and flag icon shrinking in the contacts table
Fixed scrolling issues in the settings page tabs
Fixed cropped column names during CSV import
Fixed error when importing CSV files with duplicate external IDs
Internal tokens like \[endCall] are no longer spoken aloud by the agent
## In-app changelog
telli now has a built-in changelog. When there are new updates, a "What's New" card appears in the sidebar. You can also find it anytime in the user menu under "What's new?" or visit the full changelog in the docs.
# Contact lookup webhook
Source: https://docs.telli.com/contact-lookup-webhook
Identify unknown callers by looking up their phone number in your system before the call starts
## Overview
The contact lookup webhook lets you identify unknown inbound callers by looking up their phone number in your system before the call connects. When enabled, telli sends a POST request to your endpoint with the caller's phone number, and your endpoint responds with the contact's details.
This is useful when you want to:
* Greet callers by name
* Route calls based on customer data
* Pass custom properties to your agent's prompt via dynamic variables
The webhook is called before the call connects. The timeout you configure adds to the caller's ringing time. Keep your endpoint fast and set the timeout as low as possible.
## Setup
1. Go to **Settings → Integrations**
2. Select **Contact Lookup Webhook**
3. Enter your webhook URL
4. Set a timeout (1–10 seconds)
5. Optionally add custom headers and query parameters
6. Enable the integration and click **Save**
## Request
When an inbound call arrives from an unknown number, telli sends a POST request to your webhook URL:
```json theme={null}
{
"event": "contact_lookup",
"phone_number": "+14155551234",
"to_number": "+14155550000"
}
```
| Field | Type | Description |
| -------------- | ------ | ----------------------------------------------------------------- |
| `event` | string | Always `"contact_lookup"` |
| `phone_number` | string | The caller's phone number in [E.164 format](/phone-number-format) |
| `to_number` | string | The telli phone number that was called, in E.164 format |
Any custom headers and query parameters you configured in the integration settings are included in the request.
### Signature verification
The request includes an `x-telli-signature` header that you can use to verify the request was sent by telli. The signature is an HMAC-SHA256 hash of the request body, signed with your account's API key.
## Response
Your endpoint should return a JSON response with the contact's details:
```json theme={null}
{
"contact": {
"first_name": "Jane",
"last_name": "Doe",
"salutation": "Ms.",
"email": "jane@example.com",
"external_id": "usr_12345",
"external_url": "https://crm.example.com/contacts/12345",
"phone_number": "+14155551234",
"properties": {
"plan": "enterprise",
"account_manager": "John Smith",
"priority": 1
}
}
}
```
| Field | Type | Required | Description |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `first_name` | string | Yes | Contact's first name (1–50 characters) |
| `last_name` | string | Yes | Contact's last name (1–50 characters) |
| `salutation` | string | No | Salutation or title (e.g. "Mr.", "Ms.", "Dr.") |
| `email` | string | No | Contact's email address |
| `external_id` | string | No | Your system's identifier for the contact. If a contact with this ID already exists, it will be updated instead of creating a duplicate. |
| `external_url` | string | No | A URL to the contact in your system |
| `phone_number` | string | No | The contact's phone number (defaults to the caller's number if omitted) |
| `properties` | object | No | Custom contact properties. Keys must match your configured property keys. Values must match the property's data type: string, number, boolean, `YYYY-MM-DD` date, ISO 8601 date-time, email address, [phone number in E.164 format](./phone-number-format) with a leading `+`, configured select value, or array for multi-select. |
### Unknown caller
If the phone number is not found in your system, return an empty contact:
```json theme={null}
{
"contact": null
}
```
### Custom properties
The `properties` object lets you pass custom data that matches your [contact properties](/platform/contact-properties) configuration. Property keys must match the keys you've defined in **Settings → Contact properties**. Values that don't match a defined property key are ignored.
## Error handling
* If your endpoint returns a non-2xx status code, the call proceeds with an unknown contact
* If your endpoint doesn't respond within the configured timeout, the call proceeds with an unknown contact
* If the response body doesn't match the expected format, the call proceeds with an unknown contact
In all error cases, the call is never blocked — the caller will always be connected.
## Example implementation
Here's a minimal example using Node.js and Express:
```javascript theme={null}
app.post("/telli/contact-lookup", (req, res) => {
const { phone_number } = req.body;
// Look up the contact in your database
const contact = db.findContactByPhone(phone_number);
if (!contact) {
return res.json({ contact: null });
}
res.json({
contact: {
first_name: contact.firstName,
last_name: contact.lastName,
email: contact.email,
external_id: contact.id,
properties: {
plan: contact.plan,
account_manager: contact.accountManager,
priority: contact.priority,
},
},
});
});
```
# Cookbooks Overview
Source: https://docs.telli.com/cookbooks/overview
Practical guides and examples for building with telli.
Learn how to craft effective prompts for your telli agents.
Edit your agent's prompt with help from Charlie, telli's built-in AI assistant in the Agent Builder.
Learn how to use tool calls to let your agent perform specific actions during a call.
Learn how to use variables to personalize your agent's prompts dynamically.
Automatically extract structured information from every call your agent handles.
Give your agent access to your own documents so it can answer questions using your specific information during calls.
Let your agent actively gather specific pieces of information from callers like email addresses, license plates, or case numbers.
Let your agent book appointments during calls by connecting to Calendly, Zeeg, Cal.com, HubSpot, or your own calendar system.
Give your agent a custom voice by cloning a professional voice for use in your calls.
Hand off live calls to a human team member. Cold transfers, warm transfers, and context-based routing.
Reach out to contacts automatically. Pick a strategy, retry intensity, and dialer windows that fit your campaign.
# Integrate with your CRM
Source: https://docs.telli.com/crm-integration
Integrate telli with your CRM to sync contacts and call data. This enables you to automate contact management, schedule calls, and sync call outcomes with your CRM or other systems.
## Overview
CRM integration with telli allows you to:
* **Sync contacts**: Automatically add contacts from your CRM to telli for AI voice calls
* **Track call outcomes**: Receive real-time updates about call results and analysis
* **Update CRM records**: Automatically update contact status, notes, and fields based on call outcomes
* **Maintain data consistency**: Keep your CRM and telli in sync using `external_contact_id`
## Integration Flow
```mermaid theme={null}
flowchart LR
A[Your CRM] -->|Add contacts| B[telli]
B -->|Make calls| C[AI Agent Calls]
C -->|Call outcomes| D[Webhooks/API]
D -->|Update records| A
```
## Key Integration Points
### Add Contacts in telli
Sync contacts from your CRM to telli so your AI agents can call them:
* **Single contact**: Add one contact at a time via API
* **Batch import**: Add up to 1,000 contacts in a single request
* **Automation platforms**: Use Zapier, Make, or n8n for no-code integration
Learn more: [Add contacts](/v1/endpoint/add-contact)
### Schedule calls in telli
Schedule calls with the contacts you just added:
* **Single call**: Schedule one call at a time via API
* **Batch scheduling**: Schedule up to 1,000 calls in a single request
* **Automation platforms**: Use Zapier, Make, or n8n for no-code integration
Learn more: [Schedule calls](/v1/endpoint/schedule-call)
### Receive call data in your CRM
Get real-time updates about call outcomes to keep your CRM current:
* **Webhooks**: Receive instant notifications when calls complete
* **Call analysis**: Access transcripts, summaries, and automated insights
**Possible events:**
* [call\_ended](/webhooks/events/call-ended): Get complete call data when calls finish
* [auto\_dialer\_status\_changed](/webhooks/events/auto-dialer-status-changed): Track when contacts enter or exit the auto dialer
* [contact\_status\_changed](/webhooks/events/contact-status-changed): Deprecated; use `auto_dialer_status_changed` for auto dialer changes
The `contact_status_changed` webhook is deprecated and will be removed on July 1, 2027.
Learn more: [Webhooks](/webhooks)
## Next Steps
* [Add contacts & schedule calls](/add-contacts-and-schedule-calls) - add contacts to telli and schedule calls with them
* [Webhooks](/webhooks) - Set up an endpoint to receive call outcomes
* [Automation Platforms](./integrations/zapier) - Use automation platforms for quick setup
# Custom HTTP Tools
Source: https://docs.telli.com/custom-tools
Enable your AI agents to call external APIs during conversations
Custom tools allow your AI agents to access external information by connecting to your APIs.
## How It Works
```mermaid theme={null}
sequenceDiagram
participant Customer
participant AI Agent
participant Your API
Customer->>AI Agent: "What's my account balance?"
AI Agent->>Customer: "Let me look that up. What's your account number?"
Customer->>AI Agent: "ACC-12345"
AI Agent->>Your API: GET /balance?account=ACC-12345
Your API-->>AI Agent: {"balance": 1250.50, "currency": "USD"}
AI Agent->>Customer: "Your current balance is $1,250.50"
```
## Setup
Go to your agent settings
In the Tools section click **+ Add** to create a new custom tool
Fill in the configuration:
| Field | Description |
| -------------------- | -------------------------------------------------------- |
| **Name** | Identifier for your tool (e.g., `check_account_balance`) |
| **Description** | What the tool does and when to use it. |
| **HTTP Method** | GET, POST, PUT, PATCH, or DELETE |
| **URL** | Your API endpoint (must use HTTPS) |
| **Response Timeout** | 1-10 seconds |
Configure headers, query parameters, and request body using the value types
below
Add business rules and conversation flow for using the tool in your agent's system prompt
## Value Types
Use these value types for headers, query parameters, and request body:
| Type | Description | Use Case |
| ------------------- | ------------------------------ | ---------------------------- |
| **Constant** | Static values | Fixed config |
| **System Variable** | Contact/call data | Customer email, phone number |
| **LLM Parameter** | AI-extracted from conversation | Account numbers, product IDs |
| **Secret** | Encrypted sensitive data | Auth tokens, passwords |
### System Variable
To add contact and call data to your request you can use two available types:
**Standard Fields:** Built-in contact and call information
* `contact.firstName`, `contact.lastName`, `contact.phoneNumber`, `contact.externalId`
* `call.id`
Use the dropdown in the UI to browse all available variables.
**Contact Details:** Custom fields you define per contact (accessed via `contact.contactDetails.*`)
* Example: `contact.contactDetails.customerId`, `contact.contactDetails.accountTier`
### LLM Parameter
The AI extracts these values from the conversation by asking questions or inferring from context. When you configure an LLM Parameter, you provide:
* **Name**: The parameter name in the API request
* **Description**: Instructions for the AI on what to extract and the expected format
* **Data Type**: String, Number, or Boolean
The AI uses the description to understand when and how to collect this information from the customer.
**Example:** A parameter named `account_number` with description "Customer's account number (format: ACC-12345)" tells the AI to ask for and extract an account number in that format.
# Agent Builder
Source: https://docs.telli.com/deep-dives/agent-builder
Build, refine, and publish your agents from one cockpit
The **Agent Builder** is your central cockpit for building and refining an agent. Instead of jumping between separate forms, chat, and settings, the builder gives you one place to edit the prompt, manage tools, configure call behavior, review version history, and publish changes.
## Open the Builder
1. Log in to your account
2. Navigate to **Agents** → select an agent
The agent opens directly in the Agent Builder.
## Editing with Charlie
**[Charlie](/platform/charlie)**, telli's AI assistant, sits in the chat panel on the left of the Builder. Tell Charlie what you want to change and it will draft prompt edits for you to review. This section covers Charlie inside the Builder — see the [Charlie page](/platform/charlie) for everything it can do across your account.
### Give Charlie instructions
You can be specific or general — both work:
* **Specific change:** "Shorten the greeting and add a goodbye line at the end"
* **General feedback:** "The agent isn't friendly enough — make it ask more personal questions"
### File uploads and voice input
The chat accepts file uploads and voice input, so you can hand over a transcript, a script document, or a quick voice note without leaving the conversation.
### Review and accept changes
When Charlie proposes edits, they appear inline in the prompt:
* 🟢 Accept the change
* 🔴 Reject the change
You can accept or reject changes individually or all at once. Charlie creates **checkpoints** as it works, so you can restore an earlier state at any time.
**When to use Charlie vs. manual editing:** Start with Charlie when you want
help structuring a section, rewording, or applying a general improvement.
Switch to direct editing for small, surgical tweaks once you know exactly what
you want.
## Editing the prompt directly
The prompt editor in the center of the Builder lets you read and edit the system prompt directly, with rich-text formatting (bold, italic, lists, headings).
**Best Practice:** Keep prompts clear, concise, and conversational. See
[Prompt Best Practices](/prompt-best-practices) for the full blueprint.
### Variables
Reference dynamic data inside your prompt with `{{variableName}}`. Type `{{` in the editor and an autocomplete dropdown shows everything available, grouped by category:
* **System variables** — read-only values like `{{firstName}}`, `{{lastName}}`, and the current date
* **Contact properties** — your [contact properties](/platform/contact-properties), automatically available
Variables render as styled chips in the editor, so prompts stay readable.
### Tools
Reference tools your agent can call with `@toolName`. Tools that have multiple configured instances — like `@collect_data` and `@transferCall` — take a colon-suffixed identifier that picks the specific instance (e.g. `@collect_data:email`, `@transferCall:sales`). The Builder's tools sidebar inserts the correct reference for you. Tell the agent in the prompt *when* to use each tool and the conditions for triggering it.
## Versions and publishing
Edits in the Builder produce a **draft**. The live agent keeps running the published version until you publish a new one.
* **Diff against live** — review what changed before publishing
* **Restore an older version** — roll back to a previous revision when something needs to go back
* **Test against a specific version** — start a test call against any version to verify a change before rolling it out
## Reviewing past calls in the Builder
**Conversation History** lives inside the Builder so you can refine an agent based on what actually happened on calls. You get the full transcript, an audio player that follows along with the conversation, and consolidated filters to narrow down the calls you want to review.
You can pick individual messages from a past call and bring them into the chat with Charlie to refine the agent based on real conversation moments.
# Auto-Dialer
Source: https://docs.telli.com/deep-dives/auto-dialer
Automated contact outreach system
The telli Auto-Dialer is a feature designed to make contact outreach efficient. It automatically schedules calls until either the contact is reached or gives up after a certain period.
## Call Loop
When a contact enters a "call loop," the system attempts to reach them repeatedly until one of two conditions is met:
1. The contact answers and has a conversation
2. The maximum number of attempts or days is reached
You can check a contact's next scheduled call via:
* The web app interface
* The [/get-contact](/v1/endpoint/get-contact) API endpoint
## Calling Strategies
The auto-dialer offers two strategies for scheduling call retries:
### 1. Smart Calling (Recommended)
Smart calling automatically adjusts call frequency over time. It starts with frequent attempts and gradually reduces to avoid overwhelming contacts.
#### Intensity Modes
Choose how aggressively the system should retry contacts:
Best for: **Time-sensitive campaigns, hot leads**
| Period | Call frequency |
| --------- | --------------- |
| Day 1 | Up to 3 calls |
| Days 2-10 | 2 calls per day |
| Day 11+ | 1 call per day |
Best for: **General outreach**
| Period | Call frequency |
| --------- | --------------- |
| Day 1 | Up to 3 calls |
| Days 2-4 | 2 calls per day |
| Days 5-10 | 1 call per day |
| Day 11+ | 1 call per week |
Best for: **Follow-ups, nurture campaigns**
| Period | Call frequency |
| -------- | --------------- |
| Day 1 | Up to 2 calls |
| Days 2-4 | 1 call per day |
| Day 5+ | 1 call per week |
All calls respect your configured [Dialer Windows](#dialer-windows) and enabled weekdays.
Days are calendar days from the first call attempt.
### 2. Defined Intervals
This strategy allows manual definition of retry intervals after unsuccessful calls.
Let's look at an example. Suppose we have the following intervals set:
After the first call, we wait for 20min and then call again. After the second call we wait for 1h, after third 2h and 30min etc.
If a scheduled time falls outside the [Dialer Window](#dialer-windows), the call will be scheduled for the next available time.
## Dialer Windows
Dialer windows define permitted calling hours to ensure contacts are only called during appropriate times.
### Timezone Handling
Dialer windows are always interpreted in the contact's timezone when one is set. If no timezone is specified for a contact, the system defaults to using your account's timezone shown above the dialer windows.
For contacts with specified timezones, here's how it works:
* If your dialer window is set to 9:00 AM - 5:00 PM
* And you have a contact in Los Angeles (PST) and another in New York (EST)
* The Los Angeles contact will be called between 9:00 AM - 5:00 PM PST
* The New York contact will be called between 9:00 AM - 5:00 PM EST
This ensures calls are made at appropriate local times for each contact, regardless of your account's timezone setting.
Manual calls triggered via [/initiate-call](/v1/endpoint/initiate-call) or the web app bypass dialer window restrictions.
## Frequently Asked Questions
### When is a contact marked as reached?
A contact is considered reached when they answer and have a conversation. Voicemails, unanswered calls, or connection issues trigger retry attempts.
### What happens with calling errors?
After two consecutive errors, the call loop ends automatically, regardless of remaining attempts.
### How do manual calls affect the call loop?
Manual calls are executed immediately. If unsuccessful, the next call follows the defined strategy.
### What happens when dialer intervals change?
The system dynamically adjusts schedules while maintaining dialer window compliance.
### How are reached contacts handled?
Manually calling a previously reached contact restarts their call loop.
### How do maximum attempt changes work?
Changes take immediate effect for active call loops but don't affect completed ones.
### What happens when disabling/re-enabling the auto-dialer?
When re-enabled, past-due calls trigger immediately. To prevent this:
1. Use `/v1/remove-from-auto-dialer` for specific contacts
2. Adjust maximum intervals to auto-remove contacts
# Call Analysis
Source: https://docs.telli.com/deep-dives/call-analysis
Understand conversation insights and outcomes
# Intelligent Monitoring
Each call is automatically distilled into structured fields. Define your own **Custom Call Outcomes** in the app, and we'll populate them from the call transcript—reliably and in a strict schema. **It turns messy call transcripts into simple fields you can trust, so you can search, filter, and act on calls without rereading them.**
> **At a glance**
>
> * Add fields per agent under **Post-processing → Custom call outcomes**
> * Choose a type (Boolean, Text, Number, Category)
> * Select **Reason** to add an optional explanation for each value
> * View results in **Conversation History** and receive them via [webhook](/webhooks)
telli provides both **System Call Outcomes** (built-in fields managed by the telli team like Summary,
Dialogue, Transfer, Voicemail) and **Custom Call Outcomes** (fields you define and manage yourself).
Both types appear in Conversation History and webhooks under the `call_analysis` and `call_outcome` keys.
***
## How it works
A dedicated extraction model analyzes the **conversation transcript** and fills your fields:
* **Schema-strict:** output always matches the field type you selected.
* **No guessing:** if a value isn't clearly supported by the transcript, it's **`null`**.
* **Robust to STT:** built for speech-to-text imperfections and everyday phrasing.
* **Date-aware:** the model knows the call's date, weekday, and local time, so relative wording like "tomorrow" or "next Friday" and dates said without a year resolve against the day of the call.
If **Reason** is enabled for a field, we also return a short explanation for the chosen value.
***
## Create a field
1. Go to **Agents → \[agent] → Post-processing → Custom call outcomes → Add**.
2. **Name** the field (use `snake_case`, e.g., `lead_conversion`).
3. Pick a **Field type**.
4. Write clear **Instructions** (see [templates](#instruction-templates-copy%2Fpaste)).
5. **Save**. New calls will be analyzed for this field.
**Field types**
* **Boolean (True/False):** yes/no or binary states
* **Text:** short free-form text
* **Number:** integer or decimal
* **Category:** one option from a list you define (use labels your team understands)
***
## Plan before you add fields
* What decision will this field drive? (reporting, routing, QA, follow-ups)
* Which **type** is simplest for that decision?
* For **Category**, define a small, clear option set.
* Keep the **Instructions** unambiguous and focused on what to extract.
* Confirm downstream systems handle **new fields** and **`null`** safely.
***
## Instruction templates (copy/paste)
* **Boolean — lead conversion**
Set to `true` only if the customer clearly agreed to be contacted again (explicitly or by accepting next steps). Otherwise set to `false`.
* **Category — lost reason**
Select the single best explanation for why the customer is not proceeding.
* Choose `NO_INTEREST` if the customer is no longer interested in the product.
* Choose `PREVIOUSLY_CONTACTED` if the customer already been contacted about this topic
* Choose `TOO_EXPENSIVE` if the customer expresses reluctance about the price of the product
* **Text — follow-up timing**
Return the agreed follow-up time concisely (e.g., `Thursday 09:00`).
* **Number — quantity requested**
Return the total number of units requested.
* **Text — insurance provider (string example)**
Return the exact insurance provider the customer says they are a member of (e.g., `Insurance A`, `Insurance B`, `Insurance C`).
**Best practices**
* One decision per field.
* Prefer **Category** over Text when you'll filter or chart results.
* Include brief examples in the instructions if edge cases exist.
* Remember that the analysis is based on the call transcript and the date and time of the call.
***
## Data you'll receive
Each analysis field follows a consistent data structure:
```json theme={null}
{
"call_outcome": {
"lead_conversion": {
"value": true,
"reason": "Customer confirmed they want to be contacted."
},
"lost_reason": {
"value": "NO_INTEREST",
"reason": "Customer explicitly mentioned that they are not planning to buy right now."
},
"insurance_provider": {
"value": "Insurance A",
"reason": "Customer stated their insurer explicitly."
}
}
}
```
Notes:
* **Reason** appears only if enabled for that field.
* Fields may be **`null`** when the transcript doesn't clearly support a value.
# Call Transfer
Source: https://docs.telli.com/deep-dives/call-transfer
How cold and warm transfers use fixed destinations or Phone Number contact properties
Warm transfers are temporarily unavailable for Duo agents. Cold transfers remain available. Saved warm-transfer destinations are skipped during calls.
Call transfer allows your AI agent to hand off conversations to human team members when needed. telli supports two transfer methods: cold transfer (immediate) and warm transfer (briefed).
## Cold Transfer
Immediately connects the caller to the destination number without any briefing.
**How it works:**
1. AI informs caller they're being transferred
2. Call connects directly to destination
3. AI disconnects immediately
If the destination doesn't answer, the caller will hear ringing indefinitely.
The AI agent cannot return to help.
**Technical detail:** Cold transfers use the SIP REFER method. For every cold
transfer, telli includes `X-Telli-Call-Id` in the SIP REFER request headers,
with the telli call id as its value for downstream call correlation.
***
## Warm Transfer
Places caller on hold while the AI briefs the human agent before connecting them.
**How it works:**
1. AI places caller on hold with music
2. AI calls destination in a separate session
3. AI summarizes the conversation for the human agent
4. Human agent accepts or declines the transfer
5. If accepted: caller connects to the briefed agent
6. If declined/unavailable: AI returns to caller with explanation
**Key advantages:**
* Human receives conversation summary before speaking to customer
* Automatic detection of voicemail or unavailability
* AI can resume conversation if transfer fails
* Better customer experience with informed handoff
### Timeout Handling
| Scenario | Detection Time | Result |
| ---------------------- | -------------- | ---------------------- |
| No answer | 5 minutes max | AI returns to caller |
| Voicemail detected | 10-30 seconds | AI returns immediately |
| Agent says unavailable | Immediate | AI returns immediately |
| Caller hangs up | Immediate | Transfer cancelled |
***
## Configure a transfer destination
In the Agent Builder, open **Tools → Transfers** and select **Add transfer tool...**.
Configure these fields:
* **Label**: Internal name (for example, `sales` or `support`)
* **Transfer target**: A fixed phone number or a Phone Number contact property
* **Description**: When the agent should use this destination
* **Transfer type**: Cold or warm
Warm transfers add two optional limits:
* **Ring timeout (seconds)**: Cancel the transfer when the destination does not answer within 5–120 seconds. Leave it empty to let the destination ring for up to 5 minutes.
* **Max transfer attempts**: Refuse further attempts to this destination after 1–10 dials within the same call. Leave it empty to let the agent decide based on the conversation.
Phone-number targets must use [E.164 format](../phone-number-format) with a leading `+` (for example, `+4917612345678`). Cold transfers also accept `tel:` and `sip:` URIs. Warm transfers accept phone numbers and `tel:` URIs.
You can add multiple transfer destinations. The AI will choose based on
conversation context.
## Contact-specific destinations
A transfer target can use a **Phone Number** [contact property](../platform/contact-properties) instead of a fixed number. This lets the same transfer configuration route each contact to a different destination.
When the call starts, telli replaces the property reference with that contact's value and normalizes valid formatted phone numbers. Changes during the call affect only later calls.
If the property is empty or does not contain a valid phone number, the call still starts. The transfer tool returns an error only if the agent tries to use that destination.
See [Route each contact to a different destination](../cookbooks/call-transfers/overview#route-each-contact-to-a-different-destination) for setup instructions.
# Data Collection
Source: https://docs.telli.com/deep-dives/collected-data
Actively collect and validate structured data from callers during a call
Collect Data is temporarily unavailable for Duo agents. Your agent can ask for information in the conversation, but the structured collection tasks do not run.
Collect Data lets you define structured fields that the AI agent actively collects during a conversation. The agent asks the caller, validates the input, and confirms it -- all in real-time.
> **At a glance**
>
> * Configure fields per agent under **Collect Data**
> * Choose a data type: Email, License Plate, Digits, Address, or Custom
> * Add optional validators to Custom fields for data quality
> * Customize task behavior when a field needs special collection or confirmation instructions
> * View results in **Conversation History** and receive them via [webhook](/webhooks)
**Collect Data vs Call Analysis:** [Call Analysis](/deep-dives/call-analysis)
passively extracts information from the transcript *after* the call. Collect
Data is an *active, in-call* process -- the agent asks the caller directly,
validates their response, and confirms it before moving on.
***
## Data types
### Email
Collects and validates an email address from the caller. The agent guides the caller through spelling out the address and confirms it.
### License Plate
Collects and validates a license plate number. Currently supports German license plates.
### Digits
Collects number-only values such as order numbers, PINs, or customer IDs. You can optionally set minimum and maximum length.
### Address
Collects a postal address from the caller, including street address, ZIP/postal code, city, and country. The agent validates the address with Google Maps, asks for missing details when needed, and confirms the final address with the caller. Address entries include latitude and longitude in call responses and the `call_ended` webhook when validation produced coordinates.
### Custom
Collects any freeform data with optional validators for quality control:
| Validator | Description |
| ---------------------- | --------------------------------------------------------------------------------------- |
| **Exact length** | Value must be exactly N characters |
| **Min/max length** | Value must be between N and M characters |
| **Alphabet** | Restrict allowed characters (lowercase, uppercase, numbers, special characters, spaces) |
| **Regular expression** | Value must match a regex pattern (provide a human-readable description for the agent) |
Use validators to ensure data quality. For example, a 6-digit case number
could use "exact length = 6" and "alphabet = numbers only".
***
## Setting up Collect Data
1. Go to **Agents > \[agent] > Collect Data**
2. Click **Add** and select a data type (Email, License Plate, Digits, Address, or Custom)
3. Set the **key** -- this identifier appears in the webhook payload and UI
4. Keep **Behavior** set to **Auto** for the built-in collection flow, or choose **Prompt** to add instructions for how this specific field should be collected and confirmed.
Use the main prompt to tell the agent when to start a Collect Data task. Use
the task's Behavior setting for how the collect-data sub-agent should ask,
repeat, spell back, or confirm the value once the task has started.
***
## How it works during a call
When the agent determines it's time to collect data (based on your description), it starts an interactive sub-conversation:
1. The agent asks the caller for the information
2. The caller provides the data
3. The agent validates it against the configured type and constraints
4. The agent reads back the value and asks the caller to confirm
5. The caller confirms or corrects it
The caller can also **decline** to provide the data. In that case the status is set to `declined`.
***
## Adjust the prompt
After adding Collect Data tasks, you **must** update your agent's prompt in
the [Agent Builder](/deep-dives/agent-builder) to tell the agent when and how
to use them. Without prompt instructions, the agent won't know when to collect
the data.
Adding Collect Data tasks makes a `collect_data` tool available to the agent, but the agent needs instructions in the prompt to know *when* to trigger it. Open the Agent Builder and add clear instructions that specify the trigger and the order relative to other steps.
**Example: Appointment booking with email collection**
```
When the caller wants to book an appointment:
1. First, use the collect_data tool to collect their email address.
2. Then, proceed with finding an available time slot.
3. Only confirm the booking after the email has been successfully collected.
```
**Example: Support ticket with case number**
```
At the start of every support call, ask the caller for their case number
and use the collect_data tool to collect it. If they don't have one,
continue without it but let them know a new case will be created.
```
**Example: Insurance claim with license plate**
```
When the caller wants to file a claim:
1. Use the collect_data tool to collect their license plate number.
2. Use the collect_data tool to collect their email address for the confirmation.
3. Then gather the details of the incident.
```
**Example: Delivery with address collection**
```text theme={null}
When the caller needs something delivered by post:
1. Use the collect_data tool to collect their postal address.
2. Ask for street address, ZIP or postal code, city, and country.
3. Only continue with the delivery confirmation after the address has been successfully validated/collected.
```
The more specific you are about the timing and order, the more reliably the agent will collect at the right moment in the conversation.
***
## Results
### Conversation History
Navigate to **Conversation History**, click on a call, and scroll to the **Collect Data** section. Each field shows:
* **Field name** -- the key you defined
* **Value** -- the collected data, or empty if not collected
* **Status badge:**
* **Confirmed** -- caller confirmed the value
* **Declined** -- caller declined to provide the data
* **Error** -- collection failed due to an error
* **In progress** -- collection started but the call ended before completion
### Webhooks
Collect Data results are included in the [`call_ended` webhook event](/webhooks/events/call-ended) as a `collected_data` field. Address entries also include `latitude` and `longitude` when address validation produced coordinates. See the [webhook documentation](/webhooks) for the full payload format.
***
## Best practices
* **Write clear descriptions** so the agent knows exactly when and what to collect. Vague descriptions lead to inconsistent collection behavior.
* **Add validators** to Custom fields to reduce errors. A regex validator with a human-readable description helps the agent explain requirements to the caller.
* **Mention collection in your prompt** -- reference when the agent should trigger each collection task in your prompt instructions.
* **Use Behavior for collection style** -- put spelling, read-back, repeat, and confirmation instructions in the task's Behavior setting rather than the main prompt.
# Feedback System
Source: https://docs.telli.com/deep-dives/feedback-system
Improve your agent through iterative feedback
The feedback system in telli allows you to share both what works well and what doesn’t—whether it’s technical errors, confusing behavior, or questions about the agent’s responses. This helps us debug calls, resolve larger behavior issues, and optimize performance — ultimately improving your telli agent and enhancing your experience in the telli app.
## Getting Started
1. Log in to your account
2. Navigate to Conversation history
3. Select the interaction you want to review
4. From here, you can provide direct feedback on summaries, responses, or call outcome fields
## Giving Feedback
You can easily submit structured feedback by using the 👍🏻 👎🏻 icons.
* Use 👎🏻 **Downvote** to flag issues and suggest improvements.
* Use 👍🏻 **Upvote** to confirm when the agent performed well, highlight good responses, or reinforce desirable behavior.
### 1. Call Outcome Feedback
* Review call summaries and outcome fields
* If something doesn't look right (e.g., summary too long), downvote the point
* Select an issue type (e.g., "Incorrect Analysis")
* Add a short note and send feedback
### 2. Conversation-Level Feedback
* Open the Conversations tab to review the full transcript
* Click on any agent turn to evaluate if the response was correct, useful, or needs improvement
* Provide feedback such as "Phrase this question differently" or "Agent should respond more concisely"
* Downvote the turn and submit your suggested adjustment
This helps us identify recurring issues and tune the agent accordingly.
## Feedback History & Customer Portal
All submitted feedback is stored and can be viewed in the **[Customer Portal](https://portal.usepylon.com/telli)**, where you'll find:
* A complete overview of all reported issues and how they were resolved
* The history of messages exchanged with our support team (via Slack or email)
* The ability to submit new tickets for:
* Feature requests
* Agent adjustments
* Scheduling a support call with us
You can access the portal directly from the chat window inside telli.
## Best Practices
* **Be specific** when giving feedback (e.g., "Make the summary 30% shorter" instead of "summary is bad")
* Use the issue type selector to **categorize feedback** and speed up resolution
* Regularly review your **feedback history** to see progress and avoid duplicate submissions
* Don't hesitate to submit **feature requests** - they help shape the product roadmap
# Get Started
Source: https://docs.telli.com/en/get-started
Follow this checklist to achieve results with your first telli agent
Building your first AI voice agent is easier than you might expect. The four sections below walk you through it one piece at a time, from deciding what your agent should do, to getting your account ready, to watching it handle its first real calls.
Identify a specific sales, operations or support use case where telli can help (ideally starting with a simple, high-impact scenario that delivers value quickly and one KPI defining your success).
Create your regulatory phone number bundle, manage contact properties or set your dialing windows.
Create your regulatory phone number bundle to ensure compliant outbound calling.
How to create a regulatory bundle and buy phone numbers in telli.
Define and structure the contact data your agent will use during calls.
Create custom fields for your contacts and reference them in your agent
prompt.
Configure when your agent is allowed to place calls.
Configure dialing windows, calling strategies, and retry behavior.
We guide you through all the steps of how you can easily set up your first telli agent.
Step-by-step overview of creating, configuring, and testing your first agent.
Analyse calls, identify areas of improvement so you can refine your agent and improve your success criteria.
Track key metrics like reach rate, call duration, and success rate.
Analyze individual calls, transcripts, and call outcome fields.
Extract structured insights from calls to measure your KPI.
Submit feedback on calls to continuously improve your agent.
# Agent Editor Charlie
Source: https://docs.telli.com/en/get-started/agent-editor-charlie
Use Charlie to review and improve your agent
[Charlie](/platform/charlie) is the AI assistant that lives inside your agent. Type what you want to change in plain language, and Charlie drafts the edit for you to review before anything is saved.
## Interacting with Charlie
Open Charlie from the chat panel on the left side of the agent builder, then describe what you'd like to do in plain language. There are no commands or special wording to learn.
Charlie shows you exactly what will change before anything goes live, so you stay in control.
Keep refining in the same conversation, or roll back to a previous checkpoint at any time.
Charlie's edits always go through a review step, and you can revert to a previous checkpoint at any time. You stay in control of what reaches your live agent.
## What Charlie can help with
Charlie understands plain language and goes well beyond prompt edits: call control, call outcomes, tools and integrations, analyzing past calls, and turning feedback into fixes. See the [Charlie overview](/platform/charlie) for everything it can do across your account.
## Try these prompts
***"Add a call outcome that analyzes the customer's sentiment after each call. The output should be a score from 1 to 5."***
***"Add a yes/no call outcome that captures whether the customer agreed to a follow-up meeting."***
***"Add a call outcome that categorizes the lead as 'interested', 'not interested', or 'needs more information'."***
***"Analyze my recent calls and find what could be improved. Show me specific examples and suggest a fix."***
***"Look at the last 10 calls where the customer hung up early and tell me what went wrong."***
***"Find the most common objections customers raised this week and suggest how the agent should respond to them."***
***"Check my prompt against best practices and help me add anything that's missing."***
***"Review my prompt for edge cases the agent doesn't handle well and add instructions for them."***
***"Make my prompt more concise without losing important behavior."***
## Go deeper
Dive into advanced workflows, tips, and real-world examples for getting the most out of Charlie.
## Next Steps
Learn how to use variables so your agent can reference the right context and information during calls.
# Create an Agent
Source: https://docs.telli.com/en/get-started/create-a-new-agent
Create your first AI voice agent
Two ways to get started: pick a template that's close to what you need, or upload your own script if you want full control. Either way, you'll have a working first draft in a few minutes.
Use one of the pre-drafted templates for various use cases to jumpstart your agent creation.
Provide more context to your company, industry and most importantly the main goal of the agent.
Choose a name and language for the agent and select from our default voice selection.
Review the generated agent structure and click "Build" to create your custom agent.
Draft your conversation flow outlining the greeting, questions, main tasks, handoffs, and closing. Check out our [sample script](https://docs.google.com/document/d/10uL87kOAOHJSDmz3oZRt9WZjoNdlkgMLr6r5j0h_bEY/edit?usp=sharing) for reference.
Upload your conversation flow document to the agent builder.
Choose a name and language for the agent and select from our default voice selection.
Review the generated agent structure and click "Build" to create your custom agent.
## Next Steps
After creating your agent, you're ready to:
Review the generated agent draft and personalize its voice, tone, and core setup so it matches your brand and use case.
# Agent Creation Overview
Source: https://docs.telli.com/en/get-started/introduction
Set up your first agent from scratch
Time to build your first agent. The three steps below take you from a blank slate to an agent that's ready for real calls. The advanced section is there when you want to push further.
## What you'll learn
Create your agent in the telli platform, then personalize its voice, tone, call handling, and call outcomes so it matches your brand and use case.
Use Charlie, the built-in AI editor, to make guided improvements, and learn how to insert variables so your agent uses the right context on every call.
Run real test calls, refine your agent based on what you hear, and publish your changes so they go live for new calls.
Extend your agent with tool calls, knowledge bases, and custom voice cloning once the basics are in place.
# Knowledge Bases
Source: https://docs.telli.com/en/get-started/knowledge-bases
Give your agent access to your own documents during calls
Upload your FAQs, product docs, or policies as a knowledge base, and your agent can look things up live during a call instead of having to guess.
## What you can put in a knowledge base
* FAQs and company info
* Product details and pricing
* Policies, terms, and procedures
* Long service descriptions that don't belong in the prompt
## Set up a knowledge base
Open **Knowledge Base** in the sidebar, click **Create**, and give it a clear name so you can recognize it later. Then drag in your documents: PDF, Word, `.txt`, or `.md`. You can include up to 5 files per knowledge base, totalling roughly 1,500 pages.
In the agent builder, open the **Tools** panel and connect your knowledge base. From now on, the agent can search it whenever it's helpful during a call.
Processing takes a few minutes after upload. Once it's ready, the agent decides on its own when to search. No special trigger phrases are required, though you can use `@searchKnowledgeBase` in your prompt for more control over when searches happen.
Give your agent access to your own documents so it can answer questions using your specific information during calls.
## Next Steps
Clone a custom voice so your agent can sound exactly the way you want on every call.
# Publish your Agent
Source: https://docs.telli.com/en/get-started/publish-your-agent
Make your draft changes go live for new calls
Publishing takes everything in your draft and makes it live for new calls. Until you hit Publish, your edits stay in the draft and don't change anything for callers.
## Publish your changes
In the top-right of the agent builder, click **Publish**. The button is only active when you have unpublished changes and no errors. The **Review changes** dialog opens with a side-by-side comparison between your published (live) version and your current draft, so you can confirm exactly what will change.
Click **Publish** in the dialog to roll out the changes. They take effect immediately for new calls handled by this agent.
Every previous version is preserved in **Version history**. You can restore an earlier version at any time if a published change doesn't land the way you expected.
Published changes only apply to **new calls**, and calls already in progress aren't affected. The previous version is preserved in **Version history**, so you can always roll back.
## Next Steps
Extend your agent with tool calls so it can trigger actions and workflows during conversations.
# Refine your Agent
Source: https://docs.telli.com/en/get-started/refine-your-agent
Improve your agent based on real call data
Refining your agent is an iterative process: review what happened on real calls, hand the issues to Charlie, and review the proposed changes before saving them. Walk through one full cycle below.
Open **Conversation History** and drill into a recent call. Read the transcript, listen to the recording, and check the call outcomes to spot specific moments your agent could handle better.
On the call detail view, hover the turn you want to fix and click the **+**
button beside it. This sends the moment you flagged into the Charlie chat
along with the full transcript, the call outcomes, and any notes you've added.
Charlie uses all of that as context, so the fix it suggests is grounded in
what actually happened on this specific call.
In the Charlie chat, describe the improvement in plain language, or accept the
auto-prepared context that came from your feedback. Charlie can edit the
prompt, tools, call control, or outcomes, whatever it takes to address the
issue.
Charlie shows you exactly what will change before anything is applied. Read
through the changes to make sure the fix lands the way you want.
Keep the change in your draft if it looks good, or revert the checkpoint if it didn't land. Use **Version history** to roll back to an earlier version when you need to.
Re-test after every meaningful change so you can see the impact right away. Head back to [Test your Agent](/en/get-started/test-your-agent) to run another test call.
## Give feedback to telli
You can also give the telli team feedback directly from the call detail view
by clicking the 👍 or 👎 icons next to call summaries, outcomes, and
individual conversation turns. We use this feedback to debug calls, fix
bigger behavior issues, and improve the product for you.
Full guide to the thumbs up / thumbs down feedback system and how to use it
well.
## Tips
* Make **small, focused changes** so you can clearly see what improved and what didn't.
* Look at **patterns across multiple calls** instead of reacting to a single one, since recurring issues are the highest-leverage fixes.
* Read through our **"Charlie Cookbook"** linked below to see best practices
Dive into advanced workflows, tips, and real-world examples for getting the
most out of Charlie.
## Next Steps
Once your agent performs the way you want, publish your changes so they go
live.
# The telli Agent Interface
Source: https://docs.telli.com/en/get-started/review-and-personalize
Customize how your agent sounds, how it handles calls, and what it captures from every conversation.
When you open an agent, you land in the **Agent Interface**, the single place to configure how your agent sounds and behaves on calls. The center of the builder holds the **prompt editor**, where you write the agent's instructions. [**Charlie**](/en/get-started/agent-editor-charlie), telli's AI editor, lives in a chat panel on the left and can make guided edits to anything on this page for you. A sidebar on the right gives you quick access to your **Tools** and **Variables**, while the tabs along the top (**Agent**, **Call Control**, and **Call Outcomes**) group every other setting by category.
Choose a name and voice that fits your brand and is easy to pronounce. You can also set how fast your agent speaks to customers. With Cartesia voices, you can additionally configure the emotion.
Set the greeting your agent uses to open inbound and outbound calls so the conversation starts the way you want. Use [variables](/en/get-started/working-with-variables) like `{{firstName}}` to personalize each greeting per contact.
Open the prompt editor to fine-tune your agent's instructions, behavior, and conversation flow, or hand the edits off to [Charlie](/en/get-started/agent-editor-charlie). Reference [tools](/en/get-started/tool-calls) and [knowledge bases](/en/get-started/knowledge-bases) the agent can use during a call so it knows when to take action or look something up.
Set the core controls that apply to every call, including max call length, call recording, noise cancellation, and background sound.
We recommend keeping **Agent Audio** enabled at all times. That way you get full visibility after every call into how your agent sounds and behaves.
Define how the agent handles incoming calls (assigned number, answer or forward behavior) and outbound calls (voicemail handling, call screening, and callbacks).
Decide when the agent is allowed to call by enabling the autodialer and setting your calling strategy and dialing windows.
The autodialer is available at both **account** and **agent level**. Agent-level settings always override account-level settings.
Define the outcomes you want extracted after every call. Pick a field type, give it a name, and write instructions so the agent knows how to determine the outcome. The agent extracts these outcomes after the call has ended, based on the generated transcript.
Boolean outcomes can also appear as metrics on your [dashboard](/platform/performance-dashboard), so you can track yes/no trends like conversion rates over time.
Review the built-in outcomes telli captures automatically for every call, so you know which signals you get out of the box without any setup.
### Types of Call Outcomes
Custom call outcomes can be one of the following field types. The type is locked once you save the outcome, so pick the right one up front.
| Type | What it returns | Example question |
| :--------------- | :---------------------------------------- | :-------------------------------------------------------------------------------- |
| **Boolean** | Yes / No | *"Did the caller schedule an appointment?"* |
| **Text** | A free-text answer | *"Summarize what product the caller was interested in."* |
| **Number** | A numeric value | *"How many units did the caller want to order?"* |
| **Category** | One option from a predefined set | *"What was the caller's sentiment?"* (`Positive`, `Neutral`, `Negative`) |
| **Multi-Select** | One or more options from a predefined set | *"Which features did the caller mention?"* (`Pricing`, `Integrations`, `Support`) |
Every change you make here is saved as a draft, and no past version is lost. To set the new version live, [click **Publish**](/en/get-started/publish-your-agent). The new settings will be used right away for every new call.
## Tips
* Match the voice to your brand identity
* Test different voice speeds with your team
* Try different background noise settings to see what feels natural
* Use our **"How to Prompt"** and **"Call Outcomes"** cookbooks to see our best practices for agent customization
Learn how to craft effective prompts for your telli agents.
Automatically extract structured information from every call your agent handles.
## Next Steps
After configuring your agent's persona, you're ready to:
Use Charlie in the agent editor to review your draft and make guided improvements to your agent.
# Test Your Agent
Source: https://docs.telli.com/en/get-started/test-your-agent
Test your agent with a live call
Test calls let you hear what your agent actually sounds like before any real customer does. Run a few before you publish to catch issues early.
Test calls always go to a contact, so add the number you want to test with (usually your own) as a new contact in the **Contacts** page first.
In the agent builder, click **Test call** to open the dialog. Pick the **agent version** (your draft with unsaved changes or any saved revision), the **contact** to call (their properties get substituted into your variables), and the **outgoing phone number** to call from. Then click **Call** to start the test.
Once the call ends, head to **Conversation History** and select the call to drill into the details.
Use the side-by-side view to listen to the recording while following along in the transcript. This is the fastest way to spot anything that didn't sound right.
Confirm that the outcomes you defined got extracted correctly. If something is missing or wrong, your outcome definition or prompt may need a tweak.
A few habits that pay off: always test your draft version before publishing,
use a real contact so your variables get filled in, and run a few different
scenarios (both the typical conversation and the unusual ones).
## Next Steps
Take what you learned from testing and improve your agent's prompt, behavior,
and setup.
# Tool Calls
Source: https://docs.telli.com/en/get-started/tool-calls
Let your agent take action during a call
Tool calls let your agent **take action during a conversation**, like searching a knowledge base, scheduling a callback, transferring a call, or pulling data from your own system.
## What tools your agent can use
| Category | What it does | Examples |
| ---------------------- | ----------------------------------------------- | -------------------------------------------------- |
| **Built-in tools** | Ready to use on every agent. | `@endCall`, `@callMeLater`, `@waitForUserToReturn` |
| **Knowledge bases** | Let the agent answer from your own documents. | `@searchKnowledgeBase` |
| **Calendar tools** | Book appointments during a call. | Calendly, Cal.com, HubSpot |
| **Transfer tools** | Move the call to another agent or number. | `@transferCall:sales` |
| **Collect data tools** | Actively capture specific info from the caller. | `@collect_data:email` |
| **Custom tools** | Connect to your own system mid-call. | CRM lookup, account check |
## Add a tool to your agent
In the agent builder, switch to the **Tools** panel in the sidebar to see every tool category available to your agent.
Choose the tool category that fits your use case (for example, a calendar connection or a custom tool) and fill in the configuration form.
In the prompt editor, click the tool in the Tools sidebar to insert it as `@toolName`. The sidebar always inserts the correct reference, so you don't have to remember the exact name.
In the prompt, you only define **when** the agent should use a tool, not **how the tool works technically**. The technical configuration lives in the agent settings.
Learn how to use tool calls to let your agent perform specific actions during a call.
## Web search for Duo
For a Duo agent, open **Tools > System** and enable **web\_search** to let the agent
search the web for current public information during calls. Web search is off by
default. Use the insert button to add `@web_search` to the prompt, then describe
when the agent should search. Charlie can also enable or disable this setting.
## Next Steps
Give your agent access to your own information so it can answer questions during calls.
# Voice Cloning
Source: https://docs.telli.com/en/get-started/voice-cloning
Give your agent a custom voice
Voice cloning lets you give your agent a custom voice (your own, a colleague's, or a professional voice actor's), so the agent sounds exactly the way you want on every call.
## Three ways to get a custom voice
| Option | When to use |
| ---------------------------- | ------------------------------------------------------------------------------------------------------- |
| **Clone in-app** | The fastest path. Record or upload your own audio sample and let telli build a voice from it. |
| **Import from a provider** | If you already have a voice configured with a provider like ElevenLabs or Cartesia, import it directly. |
| **Professional voice clone** | Best quality. Submit a longer recording and let our team produce a high-fidelity clone for you. |
For premium-quality voice cloning (especially if the voice will be used at scale), follow the **Professional Voice Cloning** process in the cookbook below. It walks you through the recording requirements and submission steps.
Give your agent a custom voice by cloning a professional voice for use in your calls.
## Next Steps
Automate post-call actions like updating contacts, syncing your CRM, or sending webhooks.
# Workflows
Source: https://docs.telli.com/en/get-started/workflows
Automate actions that fire after every call
Workflows turn what happens on a call into automatic follow-up actions. Pick a trigger like **Call ended**, add the actions you want (send a webhook, update a contact, sync to Salesforce), and telli takes care of the rest after every call.
## What you can automate
| Use case | What it does |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Push call data to your tools** | Send call summaries, outcomes, and collected data to a URL after every call. |
| **Keep contact records current** | Update telli contact fields or custom properties based on what the agent collected. |
| **Sync your CRM** | Create or update Salesforce records straight from a completed call. |
| **Branch on call results** | Use conditions so only certain calls trigger certain actions (e.g. only qualified leads sync to Salesforce). |
## Available blocks
Triggers decide **when** a workflow runs. Today, workflows run after a completed call:
| Block | Use it to |
| :------------- | :----------------------------------------------------------------- |
| **Call ended** | Evaluate the workflow after a completed call from a selected agent |
Action blocks perform work outside the workflow branch itself:
| Block | Use it to |
| :--------------------------- | :---------------------------------------------------------- |
| **Webhook** | Send call, contact, and workflow data to another system |
| **Update contact** | Update contact fields or custom contact properties in telli |
| Salesforce **Create record** | Create a Salesforce record when Salesforce is connected |
| Salesforce **Update record** | Update a Salesforce record when Salesforce is connected |
Condition blocks decide which path the workflow should follow:
| Block | Use it to |
| :------------ | :-------------------------------------------------------------- |
| **If / else** | Split the workflow into a true branch and a false branch |
| **Switch** | Route the workflow through multiple branches based on one value |
## Build your first workflow
Go to **Workflows** in the telli dashboard and click **Create Workflow**.
Use a name that describes the result, like *"Send call summary to CRM"* or *"Update lead status after call"*.
Choose **Call ended** as the trigger, then pick the agent whose completed calls should start the workflow.
Use the **+** button on the canvas to add blocks after the trigger. Configure each block in the side panel, and add **If / else** or **Switch** when only some calls should follow a given path.
Click **Publish** once the trigger and required blocks are configured. Publishing creates the version telli uses for new runs.
Flip the **Enabled** switch to turn on automatic execution. A workflow must be published before it can be enabled.
Test with a manual run before relying on automatic execution. In the workflow builder, open the **Runs** tab, click **Run manually**, and pick a completed call to step through the workflow against real data.
## Go deeper
Full reference for triggers, blocks, manual runs, version history, and end-to-end examples like logging telli calls back to Salesforce.
# Working with Variables
Source: https://docs.telli.com/en/get-started/working-with-variables
Personalize every call with contact info, dates, and details that change per contact
Variables are placeholders in your agent's prompt that get filled with real values during a call. They let your agent reference the right context, like the contact's first name, the current date, or a custom field, without you having to write a different prompt for every call.
Every [contact property](/platform/contact-properties) you set up in telli automatically appears here as a variable you can use in the prompt.
## Types of variables
telli organizes variables into three categories. You can browse all of them in the **Variables panel** in the agent builder sidebar.
| Category | What it contains | Example |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| **Contact Properties** | Information attached to the contact the agent is calling, including built-in fields and any custom properties you create. | *The first name of the person being called* |
| **Agent Variables** | Placeholders defined within the agent itself for values specific to that agent's use case. | *A campaign name configured on the agent* |
| **System Variables** | Runtime values telli provides automatically, like the current date or time. | *Today's date when the call happens* |
## Creating and Working with Variables
If the built-in contact fields don't cover what you need, you can create your own contact properties and use them like any other variable.
Go to **Contacts → Contact Properties** and create a new property. Give it a clear name and choose the right type (text, number, yes/no, date, etc.).
Back in the agent builder, open the Variables panel and find your new property under **Contact Properties**. Insert it into the prompt wherever it's needed.
In the agent builder, switch to the **Variables** tab in the right-hand sidebar to see every variable available to your agent, grouped by category.
The custom contact properties you defined are now available here as variables you can use in your prompt.
Place your cursor in the prompt where the value should appear, then click a variable in the panel. It will be inserted as a clearly styled pill so it's easy to spot. A green dot next to the variable in the variables menu means that it is implemented somewhere in the prompt.
Insert a variable by selecting it from the **Variables panel**, or type `{{` in the prompt and choose the variable from the dropdown.
Ask [Charlie](/en/get-started/agent-editor-charlie) to insert the variable for you. Describe in plain language where you'd like it to appear, and Charlie adds it to the prompt.
## Tips
* Use clear, descriptive names for custom contact properties so they're easy to find later.
* Test with a real contact to make sure values get filled in as expected.
* Contact properties can be imported in bulk via CSV upload.
* Read through our Cookbook on **Variables** below
Learn how to use Variables to personalize your agent's prompts dynamically
## Next Steps
Make a test call to see how your agent uses the variables you've set up.
# The EU's disclosure requirements
Source: https://docs.telli.com/eu-ai-act-transparency
The EU adopted the EU AI Act in 2024, and its obligations take effect in stages. Since August 2, 2026, the transparency rules apply to AI systems that interact directly with people. For phone agents, two points matter above all:
* **AI disclosure** — callers should be able to tell, at the start of the call, that they're speaking with an AI and not a human.
* **Audio recording** — if you record the audio of calls, callers should be informed of that too.
## AI disclosure
The simplest approach is to introduce your agent as an **"AI assistant"** **right in the greeting**. In any case, the disclosure should come **before the conversation turns to the actual topic** or the person you're calling shares any **sensitive information**.
[Charlie](/en/get-started/agent-editor-charlie), your agent builder right in the app, knows the legal requirements and the right wording. It can update your greeting and notices directly, and when you publish it also checks that all requirements are met.
## Audio recording
If you record the audio of a call, the caller must always be informed at the start of the conversation. In Germany, audio recording also requires explicit consent.
In telli, you can easily make audio recording conditional on the caller's consent — so a call is only recorded once they agree.
For Duo agents, recording consent is temporarily unavailable. If your agent is configured to require consent, telli leaves recording off and skips the consent question.
**Legal disclaimer**
This information is for general guidance only and does not constitute legal advice. You are responsible for reviewing and complying with the requirements that apply to your use case.
# Get started with telli
Source: https://docs.telli.com/get-started-with-telli
This tutorial demonstrates how to use any integration platform to automate adding contacts, scheduling calls, and updating CRM contact status based on call outcomes.
## Prerequisites
* Accounts at [telli](https://telli.app) and an integration platform like [Make.com](https://www.make.com), [Zapier](https://zapier.com), [n8n](https://n8n.io), or similar
* CRM system (e.g. Airtable, Salesforce, Pipedrive, etc.) or any other system to store your contact information
## Setting Up Your Integration Platform
### 1. Adding Contacts to telli
When new contacts are added to your CRM, you can automatically add them to telli:
1. Create a new workflow in your integration platform
2. Add a trigger module for your CRM (e.g., "New Contact" in your CRM)
3. Add a module to call telli's add-contact API endpoint
* Configure the HTTP request with:
* Method: POST
* URL: `https://api.telli.com/v1/add-contact`
* Headers: Include your telli API key (found in the telli dashboard)
* Body: Map relevant contact fields from your CRM
* A successful response will look like:
```json theme={null}
{
"contact_id": "XXX-XXX-XXX-XXX"
}
```
### 2. Scheduling Calls
To automatically schedule calls when certain conditions are met:
1. Create a trigger based on your business logic (e.g., when a deal reaches a specific stage)
2. Add a module to call telli's scheduling API endpoint
* Configure the HTTP request with:
* Method: POST
* URL: `https://api.telli.com/v1/schedule-call`
* Headers: Include your telli API key (found in the telli dashboard)
* Body: Include contact\_id from the previous response (from the contact creation step)
* A successful response will look like:
```json theme={null}
{
"message": "success",
"loop_id": "XXX-XXX-XXX-XXX"
}
```
### 3. Processing Call Information with Webhooks
Once you've set up contact addition and call scheduling, enhance your workflow by capturing real-time call events:
1. **Set up a webhook in your integration platform and telli**:
* Create a new workflow with a webhook trigger in your integration platform
* Copy the webhook URL that your platform provides
* Go to Settings > Developer in your telli dashboard
* Click "Configure" under "Webhook configuration" and add the webhook URL as an endpoint
* Enable the `call_ended` event to receive updates when calls are completed
2. **Process the webhook data**:
* Parse the JSON data received from telli
* The payload will include details about the call outcome, analysis, and transcript
3. **Update your CRM based on call outcomes**:
* Create conditional logic to handle different call outcomes:
* **Appointment Scheduled**: Update contact status to "Qualified Lead"
* **Interested but No Appointment**: Mark for follow-up
* **Not Interested**: Update status accordingly
* **Not Reached**: Schedule another call attempt
## Example Workflow
Here's a practical example of a complete workflow:
1. A new lead is added to your CRM through an online form
2. Your integration platform automatically adds the contact to telli
3. telli schedules and makes an introduction call to the lead
4. Based on the call outcome, your CRM is updated with:
* Call summary and key insights
* Next steps (appointment, follow-up, etc.)
* Updated lead status
By leveraging integration platforms with telli, you create an automated system that keeps your CRM updated in real-time with valuable insights from customer calls, helping your team prioritize leads effectively and take timely actions based on actual customer interactions.
You can find hands-on tutorials for each integration platform in the respective sections.
# Overview
Source: https://docs.telli.com/integrations-overview
Learn how to integrate telli with your existing tools and workflows. This enables you to automate contact management, schedule calls, and sync call outcomes with your CRM or other systems.
## Integration Options
telli offers several ways to integrate with your stack:
### CRM
Connect telli to your CRM so agents can use current customer data and your team can keep records aligned with call workflows:
* **[Salesforce](integrations/salesforce)**
* **[HubSpot](integrations/hubspot)**
### Automation Platforms
Connect telli with popular automation platforms to create powerful workflows using native telli integrations and pre-built templates:
* **[Zapier](integrations/zapier)**
* **[Make](integrations/make)**
* **[n8n](integrations/n8n)**
### Calendar Integrations
Schedule appointments directly through telli agents. Fetch appointment slots and book appointments using our native integrations with Calendly, Zeeg, Cal.com, and HubSpot, or build custom integrations:
* **[Connect your calendar](calendar-integrations)**
### Custom Integrations
Additional capabilities:
* **[Custom Tools](custom-tools)** - Create custom tools that your agents can use during calls
* **[SIP Numbers](/platform/phone-numbers#connect-your-existing-number-with-sip)** - Connect your own SIP infrastructure
* **[Webhooks](webhook)** - Receive real-time events about calls, contacts and more
### Reference
* **[Phone Number Format](phone-number-format)** - Which phone number formats telli accepts and how numbers are normalized to E.164
## Getting Started
Get your API key from Settings > Developer in your telli dashboard
This is the quickest way to get started. You can use our pre-built templates
to automate your workflows.
Fetch contacts from your CRM and add them to telli.
Schedule calls with the contacts you just added.
Set up telli webhooks to receive call data from your telli agents and update your CRM.
Enhance your telli agent with additional integrations:
| Integration | Options |
| ---------------- | ---------------------------------------------------------------- |
| **Calendar** | Calendly, Zeeg, Cal.com, HubSpot, or custom calendar integration |
| **Phone Number** | Your own SIP trunk |
| **Custom Tools** | Create custom tools that your agents can use during calls |
# bookingtime
Source: https://docs.telli.com/integrations/bookingtime
Connect bookingtime to telli so agents can check availability and book appointments into your bookingtime calendar during calls.
## Overview
telli connects to bookingtime as an app you install into your organization. Once installed, you sign in to
bookingtime once from telli to confirm which organizations are yours. An agent can then read your live availability
and book appointments directly into your bookingtime calendar while it is still on the phone with the caller.
You connect bookingtime once per telli account. Each agent then chooses which of your connected organizations it
books into, and which appointment type it books, so different agents can book different things.
## Before you start
* A telli agent that should book appointments
* A bookingtime organization, with admin access to its **Store**
* At least one active appointment template in bookingtime, under **Settings → Appointment templates**
* The telli installation key below, also available in telli under **Settings → Integrations → bookingtime**
## Install the telli app in bookingtime
In bookingtime, go to **Store → Apps**, then choose **Install with key** in the top right.
Copy and paste this 64-character installation key, then choose **Next**.
```text theme={null}
sNcUzWzHaRsZA0YyFKYkqOhWHLuD3bVSzBVP42anATOoOAJHF8GADACSpCH8NKD2
```
bookingtime shows what the app can do before you install it. Expand **Permissions** to see the full list.
telli asks for the access it needs to read availability and make a booking, and nothing else.
| Permission | Why telli needs it |
| -------------------------------- | -------------------------------------------------------- |
| View organization | Confirm which organization you are connecting |
| View booked appointment template | List the appointment types you can choose from |
| View date and time | Read bookable slots |
| View appointments | Read an appointment after booking it |
| Book appointments | Create the appointment during the call |
| Cancel appointments | Cancel an appointment booked by an agent |
| Move appointments | Reschedule an appointment booked by an agent |
| Edit appointments | Correct the details of an appointment booked by an agent |
| Undo cancellations | Reinstate an appointment cancelled by mistake |
| View customers | Match the caller to a customer you already have |
| Add customers | Create a customer record when the caller is new to you |
Choose **Install**. The app appears in your Apps list straight away.
Install the app in every bookingtime organization you want telli to book into. An agent can only book into
organizations where the app is installed and that you connected in the step below.
## Connect bookingtime in telli
In telli, go to **Settings → Integrations**, open **bookingtime**, and choose **Connect**.
bookingtime asks you to sign in with your bookingtime login. This is how telli confirms which organizations are
yours: telli only ever sees the organizations that the account you sign in with is a member of.
telli lists the organizations the connection proved, and you are returned to the integration page. If you belong
to several organizations, all of them are connected and you choose between them per agent.
If bookingtime reports that your user has no access, check that the telli app is installed in that organization
and that your bookingtime login is a member of it.
## Set up an agent
Open the agent you want to configure in telli, then in **Calendar Integration**, select **bookingtime**.
Pick the bookingtime organization this agent books into, from the organizations you connected.
telli loads the appointment types available in that organization. Pick the one this agent should book. If your
organization has only one, telli selects it for you.
Save the agent so telli uses bookingtime during calls.
## How booking works
* The agent reads bookable slots for the selected appointment type, covering the next 14 days
* When the caller picks a time, telli books it against your bookingtime calendar during the call
* The appointment appears in bookingtime immediately, and bookingtime sends its usual confirmation
### Customer records
Every bookingtime appointment belongs to a customer. When an agent books, telli reuses the customer it created for
that contact before, or matches one you already have by email address. Only when neither matches does telli create a
new customer record.
### Email addresses
bookingtime needs an email address to confirm an appointment to the customer. If telli does not already have one
for the contact, the agent asks the caller for it before booking.
# Cal.com
Source: https://docs.telli.com/integrations/cal-com
Connect Cal.com to telli so agents can book meetings against your Cal.com availability during calls.
## Overview
Cal.com is configured per agent so each workflow can point to a different event and booking flow.
This gives you flexibility when teams use different schedules or when one agent handles a specific booking use case.
## Before you start
* A telli agent that should book appointments
* A Cal.com account
* A Cal.com API key
* The Cal.com event type you want the agent to use
## Set up Cal.com in telli
Open the agent you want to configure in telli.
In **Calendar Integration**, select **Cal.com**.
Add your API key and the event type that should be used for bookings.
Save the agent so bookings can use the selected Cal.com schedule during calls.
## How it works
* Each agent can point to a different Cal.com setup
* telli checks availability and books meetings using that agent's configured event type
* This is useful when different teams or workflows should book into different calendars
# Calendly
Source: https://docs.telli.com/integrations/calendly
Connect Calendly to telli so agents can check availability, book meetings, and fill Calendly booking questions during calls.
## Overview
Calendly is configured per agent so each workflow can use the right event type, booking link, and booking-field mapping.
This works well when different agents book different meeting types or route to different teams.
## Before you start
* A telli agent that should book appointments
* A Calendly account and event type
* A Calendly API key with at least `users:read`, `organizations:read`, `event_types:read`, `availability:read`, and `scheduled_events:write`
Booking through the Calendly API requires a paid Calendly plan. Booking is not possible via the API on the Free plan, even with the
correct API key scopes.
## Set up Calendly in telli
Open the agent you want to configure in telli.In **Calendar Integration**, select **Calendly**.
Create a Calendly API key with at least `users:read`, `organizations:read`, `event_types:read`, `availability:read`, and
`scheduled_events:write`, then add it and select the Calendly event type that should be used for bookings.
Open the settings for the selected event type and map the booking questions you want telli to fill automatically.
Save the agent so telli can use the selected Calendly setup during calls.
## Booking fields
When connecting Calendly, you can configure booking fields to automatically fill the custom questions on your Calendly event type during booking.
Each booking field maps a Calendly question to a value source.
| Type | Description | Example |
| -------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------ |
| **Constant** | A fixed value, always the same | Brand name, company name |
| **Contact property** | Resolved from the contact's data. If not available, the agent asks the caller for it | Phone number, email, first name |
| **System variable** | Resolved from internal system data using a path expression | Contact ID, timezone |
| **LLM parameter** | Collected by the agent during the conversation | Reason for appointment, preferred language |
## How it works
* telli uses the configured Calendly event type for that agent
* Booking fields are resolved automatically during booking
* Contact properties and constants are filled without extra conversation
* LLM parameters prompt the agent to collect missing information from the caller
Every required custom question on your Calendly event type must have a corresponding booking field configured, otherwise the booking will
fail. Optional questions without a mapping are skipped.
# Custom calendar
Source: https://docs.telli.com/integrations/custom-calendar
Connect your own calendar or scheduling API to telli so agents can fetch availability and book appointments through custom endpoints.
## Overview
The custom calendar option lets you connect your existing calendar or appointment system with telli agents.
Use this when you want telli to work with your own booking infrastructure instead of a built-in provider.
## Before you start
* A telli agent that should book appointments
* An API endpoint to return available appointment slots
* An API endpoint to book a selected appointment slot
* Publicly reachable endpoints for telli to call
## Set up a custom calendar in telli
Open the agent you want to configure in telli.
In **Calendar Integration**, select **Generic Calendar**.
Provide the **Available URL** and, if telli should complete bookings, the **Book URL** for your scheduling API.
Save the agent so telli can request availability and book appointments through your endpoints.
## Request flow
```mermaid theme={null}
sequenceDiagram
participant Customer
participant telli Agent
participant Your System
Customer->>telli Agent: "I'd like to make an appointment"
telli Agent->>Your System: Request available slots
Your System-->>telli Agent: Return list of time slots
telli Agent->>Customer: Present available appointments
Customer->>telli Agent: Select preferred time
telli Agent->>Your System: Book selected slot
Your System-->>telli Agent: Confirm booking
telli Agent->>Customer: Confirm appointment
```
## Endpoints
For full appointment scheduling, implement both endpoints below. If you only want telli to fetch availability, the booking endpoint can stay unset.
### Get available slots
This endpoint returns a list of available appointment slots.
```json theme={null}
POST /available
{
"contact": {
"id": "telli contact identifier",
"type": "Contact",
"externalId": "your contact identifier",
"externalUrl": null,
"salutation": null,
"firstName": "Ada",
"lastName": "Lovelace",
"phoneNumber": "+4915112345678",
"timezoneIana": "Europe/Berlin",
"email": "ada@example.com",
"autoDialerStatus": "in_dialer",
"createdAt": "2026-03-13T09:00:00.000Z",
"updatedAt": "2026-03-13T09:00:00.000Z",
"properties": [
{
"key": "appointment_type",
"value": "demo",
"dataType": "select",
"label": "Appointment Type"
}
]
},
// Deprecated legacy field, kept for backwards compatibility.
"contact_id": "telli contact identifier",
// Deprecated legacy field, kept for backwards compatibility.
"external_contact_id": "your contact identifier",
// Deprecated legacy field, kept for backwards compatibility.
"contact_details": {
"foobar": "baz"
}
}
```
```json theme={null}
{
"available": [
{
"start_iso": "2024-01-01T10:00:00.000",
"end_iso": "2024-01-01T10:30:00.000"
}
]
}
```
### Book appointment
This endpoint handles the actual booking of a selected time slot.
```json theme={null}
POST /book
{
"contact": {
"id": "telli contact identifier",
"type": "Contact",
"externalId": "your contact identifier",
"externalUrl": null,
"salutation": null,
"firstName": "Ada",
"lastName": "Lovelace",
"phoneNumber": "+4915112345678",
"timezoneIana": "Europe/Berlin",
"email": "ada@example.com",
"autoDialerStatus": "in_dialer",
"createdAt": "2026-03-13T09:00:00.000Z",
"updatedAt": "2026-03-13T09:00:00.000Z",
"properties": [
{
"key": "appointment_type",
"value": "demo",
"dataType": "select",
"label": "Appointment Type"
}
]
},
// Deprecated legacy field, kept for backwards compatibility.
"contact_id": "telli contact identifier",
// Deprecated legacy field, kept for backwards compatibility.
"external_contact_id": "your contact identifier",
"start_iso": "2024-01-01T10:00:00.000",
// Deprecated legacy field, kept for backwards compatibility.
"contact_details": {
"foobar": "baz"
}
}
```
```json theme={null}
{
"status": "success"
}
```
```json theme={null}
{
"status": "failed",
"reason": "Appointment slot is no longer available"
}
```
## Implementation notes
* `contact` is the preferred field for contact data and follows the V2 contact shape, including `autoDialerStatus` and typed `properties`
* `contact.autoDialerStatus` is the current auto-dialer enrollment state: `in_dialer` or `not_in_dialer`. The [`auto_dialer_status_changed` webhook](/webhooks/events/auto-dialer-status-changed) uses the same values for status-change events
* `contact_id`, `external_contact_id`, and `contact_details` are deprecated legacy fields that are still sent for backwards compatibility
* `contact_details` contains the old flat key-value payload from V1 dynamic variables
* Use UTC timestamps in ISO 8601 format
* telli uses `start_iso` as the slot identifier
* Return HTTP 200 responses and indicate success or failure in the response body
* Make sure telli can reach your endpoints
## Request authentication
If you want to verify requests from telli, check the `x-telli-signature` header.
```javascript theme={null}
const crypto = require("crypto");
function verifyRequest(payload, signature, apiKey) {
const expectedSignature = crypto
.createHmac("sha256", apiKey)
.update(JSON.stringify(payload))
.digest("hex");
return signature === expectedSignature;
}
```
# HubSpot
Source: https://docs.telli.com/integrations/hubspot
Connect HubSpot to telli to sync Contacts into your workspace and map HubSpot properties to telli contact properties.
## Overview
The telli HubSpot integration lets you bring CRM data into telli without building a custom sync.
Today, the integration is focused on inbound sync from HubSpot to telli:
* Connect HubSpot with OAuth from the telli app
* Sync HubSpot `Contact` records into telli
* Map HubSpot contact properties to telli contact properties
* Filter which HubSpot contacts should be synced
* Keep agents up to date with current CRM context during calls
The HubSpot CRM integration is currently in beta. Use workflows to write completed call data back to HubSpot, including creating or updating HubSpot records after calls. This is separate from inbound Contact sync.
HubSpot Meetings is a separate calendar integration. Use [HubSpot Meetings](./hubspot-meetings) when you want agents to book appointments through HubSpot meeting links during calls.
## Before you start
* Access to the telli app
* Access to the HubSpot portal you want to connect
* A HubSpot user who can authorize the telli app with contacts and contact schema permissions
* A clear idea of which telli contact properties should receive HubSpot data
## Set up HubSpot in telli
In telli, go to **Settings** -> **Integrations** -> **HubSpot** and select **Configure** to open the setup page.
Select **Connect HubSpot** and complete the HubSpot OAuth flow for the portal you want to connect.
Select **Contact** as the object to sync. The current HubSpot CRM integration supports HubSpot Contacts only.
Map HubSpot contact properties to the telli contact properties your team uses.
Narrow the contacts that should be synced when you only want a subset of HubSpot contacts in telli.
Save your configuration and confirm the integration status is connected and active.
## How the sync works
After OAuth is completed, telli reads HubSpot Contact metadata so you can configure mappings in the app.
When you save those mappings, telli prepares the HubSpot Contact sync configuration and keeps the HubSpot Journal subscription aligned with your enabled configuration. HubSpot Contact creates, updates, restores, and deletes are processed using the mapping and filter configuration you defined.
## Field mappings
Use field mappings to decide how HubSpot data should populate telli contacts.
* Map HubSpot contact properties to the telli properties your workflows already use
* Configure mappings for HubSpot `Contact` records
* The HubSpot contact URL is synced automatically
This gives your agents current CRM context before and during calls without manually copying data between systems.
## Sync filters
You can add extra conditions to control which HubSpot contacts are synced to telli.
By default, HubSpot contacts must have a first name, last name, and phone number to sync into telli. You can then add additional filters in the integration settings to further narrow the contacts that qualify.
## Write back to HubSpot with workflows
Inbound Contact sync brings HubSpot contact data into telli. To send completed-call data back to HubSpot, use [workflows](../platform/workflows) with HubSpot create or update record actions.
Workflow write-back can create or update HubSpot records based on call outcomes, collected data, or other workflow conditions. It is configured separately from the Contact sync mappings on this page.
## Manage the connection
From the HubSpot integration page in telli, you can:
* Reconnect an existing HubSpot portal
* Disconnect the OAuth connection while preserving mappings for later reconnection
* Remove the integration entirely
* Redeploy the sync configuration if the status shows it is not deployed
## Troubleshooting
* **Authorization expired or token revoked**: reconnect the integration from the HubSpot settings page in telli.
* **Missing permissions**: reconnect HubSpot to approve the latest telli permissions if telli reports that required permissions are missing.
* **Records not syncing**: confirm the integration is connected and active, then review the Contact mappings and sync filters.
* **Contact is skipped**: make sure the HubSpot contact has a first name, last name, and phone number, and that it matches any additional filters you configured.
* **Wrong integration type**: use [HubSpot Meetings](./hubspot-meetings) for appointment booking. The HubSpot CRM integration is for CRM sync and workflow write-back.
# HubSpot Meetings
Source: https://docs.telli.com/integrations/hubspot-meetings
Connect HubSpot Meetings to telli so agents can book appointments through HubSpot meeting links during calls.
## Overview
HubSpot Meetings is configured per agent so each one can use the right meeting link, duration, and routing.
This is useful when different agents book for different reps, teams, or sales motions.
## Before you start
* A telli agent that should book appointments
* A HubSpot account using Meetings Scheduler
* A HubSpot private app access token
* The meeting link slug you want telli to use
## Set up HubSpot Meetings in telli
Open the agent you want to configure in telli.
In **Calendar Integration**, select **HubSpot Meetings**.
Add your access token, meeting link slug, and meeting duration.
Save the agent so telli can use the selected HubSpot meeting flow during calls.
## Find the meeting link slug
1. Make sure you are using HubSpot's [Meetings Scheduler](https://www.hubspot.com/products/sales/schedule-meeting)
2. Open Meetings Scheduler in HubSpot
3. Select the meeting you created and copy the meeting link
4. Use the last part of that URL as the meeting link slug in telli
## Create a private app
1. Create a private app by following HubSpot's official guide
2. In the scopes tab, include these scopes:
* `crm.objects.contacts.write`
* `crm.schemas.contacts.write`
* `scheduler.meetings.meeting-link.read`
* `tickets`
3. Create an access token and add it to telli
# Make
Source: https://docs.telli.com/integrations/make
This tutorial demonstrates how to use the verified telli integration in [Make.com](https://www.make.com) to automate adding contacts, scheduling calls, and getting call data.
## Prerequisites
* Accounts at [telli](https://telli.app) and [Make.com](https://www.make.com)
* API key from your telli dashboard (Settings > Developer)
## Workflow Templates
Get started quickly with pre-built Make.com templates:
* [Add contacts & schedule calls in telli](https://www.make.com/en/integration/14245-make-com-telli-integration?templatePublicId=14245)
* [Receive telli call events and sync your CRM](https://www.make.com/en/integration/14280-receive-telli-call-events-and-sync-your-crm?templatePublicId=14280)
## Receive call events in Make
If your Make scenario receives call events from telli, you need to connect the webhook in both tools:
1. In Make, create or open a scenario with a webhook trigger and copy the webhook URL that Make provides
2. In telli, go to Settings > Developer and click "Configure" under "Webhook configuration"
3. Click "Add Endpoint" and paste the Make webhook URL
4. Enable the events you want to receive, such as `call_ended`
5. Run a test call and verify that Make receives the webhook payload
See [Webhooks](../webhooks) for the full webhook setup.
## Next Steps
Once you've set up the telli integration, you can:
1. **Create workflows** that automatically add contacts from your CRM to telli
2. **Schedule AI voice calls** based on triggers from other systems
3. **Receive call data** via webhooks and update your CRM automatically
4. **Integrate with popular tools** like Airtable, Google Sheets, Salesforce, and more
For detailed workflow examples, see the [Add contacts & schedule calls](../add-contacts-and-schedule-calls) and [Webhooks](../webhooks) guides.
# MCP
Source: https://docs.telli.com/integrations/mcp
Connect Claude, ChatGPT, and other AI tools to Charlie through the telli MCP server.
The telli MCP server lets AI tools like Claude, ChatGPT, and Codex talk to [Charlie](/platform/charlie), telli's AI assistant, over the [Model Context Protocol](https://modelcontextprotocol.io). Once connected, you can ask questions and trigger work in telli from the tools you already use.
This is a **personal** connection. It authorizes the AI tool as you, works across every telli account you can access, and applies your role in each account. Teammates manage their own connections.
## Before you start
You need:
* Access to the telli app.
* An AI tool that supports remote MCP servers over HTTP.
## Connect your AI tool
Add the telli MCP server to your tool, then authorize it when the tool sends you to telli. The server URL is:
```bash theme={null}
https://mcp.telli.com/mcp
```
Open Claude's connector settings, add a custom connector, and paste the server URL.
Enable developer mode under **Apps & Connectors**, then create a connector with the server URL.
Use the **Add to Cursor** button on the MCP integration page in telli. It opens Cursor and asks to add the telli MCP server.
Run the command below, then authenticate with `codex mcp login telli`.
```bash theme={null}
codex mcp add telli --url https://mcp.telli.com/mcp
```
Run this command in your terminal:
```bash theme={null}
claude mcp add --transport http telli https://mcp.telli.com/mcp
```
The MCP integration page in telli lists these tools with copy-ready links and commands, so you don't have to type the server URL by hand.
## How the connection works
You add the telli MCP server to your AI tool. The tool then sends you to telli to authorize the connection via OAuth, and receives its own access credentials once you approve.
Each authorized tool shows up as a connection on the MCP integration page, where you can remove it at any time. Removing a connection stops the tool's access, though it may keep working for a few minutes until its current token expires.
## Available tools
Once connected, your AI tool can use these tools:
| Tool | What it does |
| :--------------------------- | :--------------------------------------------------------------------------------- |
| `ask_charlie` | Send a message to Charlie. Starts a new conversation or continues an existing one. |
| `get_charlie_conversation` | Get the latest response from a conversation while Charlie is still working. |
| `list_charlie_conversations` | List your recent Charlie conversations across your accounts. |
| `list_telli_accounts` | List the telli accounts you can access, with your role in each. |
## Manage your connections
From the MCP integration page in telli, you can see every AI tool you have authorized, when it was authorized, and remove any connection you no longer need. Removing a connection requires the tool to be authorized again before it can reconnect.
# Microsoft Teams
Source: https://docs.telli.com/integrations/microsoft-teams
Connect Charlie to your Microsoft 365 tenant so your team can message it in Microsoft Teams.
The Microsoft Teams integration brings [Charlie](/platform/charlie), telli's AI assistant, into Microsoft Teams. Once connected, your team can message Charlie directly or in conversations where the Charlie bot has been added to ask about agents, calls, and results without leaving Teams.
## Before you start
You need:
* Access to the telli app.
* A Microsoft 365 account that can sign in to the tenant you want to connect. No admin role is required.
## Connect Microsoft Teams
Connect a Microsoft 365 tenant before your team can talk to Charlie in Teams. telli connects the Charlie app to your tenant and stores the connected tenant metadata for your telli account.
In telli, go to **Settings** > **Integrations** > **Microsoft Teams** and select **Enable**.
Sign in with a Microsoft account in the tenant you want to connect and approve the sign-in.
Microsoft redirects you back to telli after approval. telli saves the connection and shows the connected tenant.
## How the connection works
You start the OAuth flow from telli to connect the Charlie app to your Microsoft 365 tenant. telli verifies the response and stores the connected tenant metadata so Charlie can handle Microsoft Teams messages for your account.
Once connected, Charlie can respond to direct messages and to conversations where the Charlie bot has been added.
## Talk to Charlie in Microsoft Teams
Charlie answers questions about your telli agents and calls, the same way it does in the Charlie chat panel in telli.
* **In a direct message**, message Charlie and it replies.
* **In a channel or group chat**, add the Charlie bot and @mention it to start a conversation.
Try asking Charlie things like:
* How did our calls go yesterday?
* Why wasn't this contact reached?
* Which of my agents handles the most calls?
* Summarize this week's call results.
## Manage the connection
From the Microsoft Teams integration page in telli, you can:
* Reconnect the tenant.
* Disconnect the tenant so Charlie stops responding to Microsoft Teams messages, while keeping the integration in place.
* Delete the integration entirely.
## Troubleshooting
* **This tenant is already connected**: a Microsoft Teams tenant can only be connected to one telli account at a time. Disconnect it from the other account before connecting it here.
* **Reconnect required**: if the integration shows an error, reconnect the tenant from the Microsoft Teams settings page in telli.
# n8n
Source: https://docs.telli.com/integrations/n8n
This tutorial demonstrates how to use the verified telli node in [n8n](https://n8n.io) to automate adding contacts, scheduling calls, and updating CRM contact status based on call outcomes.
## Prerequisites
* Accounts at [telli](https://telli.app) and [n8n](https://n8n.io)
* API key from your telli dashboard (Settings > Developer)
* Verified community nodes enabled in your n8n workspace
* Enable the telli community node in your n8n workspace - see here how it's done:
Follow these steps to enable and use the telli community node in your n8n workspace.
### Step 1: Access the Admin Panel
1. Log into your n8n workspace
2. In the left sidebar, click on **"Admin Panel"** (the cloud icon)
### Step 2: Navigate to Workspace Settings
1. In the top navigation bar, click on **"Manage"**
2. In the secondary navigation bar, select **"Workspace"**
### Step 3: Enable Verified Community Nodes
1. Scroll down to the **"Verified community nodes"** section
2. Toggle the switch to **"on"** (it should turn orange when enabled)
3. Click **"Save changes"** at the bottom right
### Step 4: Install and Use the telli Node
1. Go back to your workflow canvas
2. Click the **"+"** button to add a new node
3. Search for **"telli"** in the node search
4. Select the **telli** node (it will show as verified with a green checkmark)
5. The telli node provides two actions:
* **Add a new contact to telli**
* **Schedule a call with telli**
## Workflow Templates
Get started quickly with pre-built n8n workflows:
* [Connect Airtable contacts to telli for automated AI voice call scheduling](https://n8n.io/workflows/3803-connect-airtable-contacts-to-telli-for-automated-ai-voice-call-scheduling/)
* [Update Airtable CRM with telli call event data and appointment status](https://n8n.io/workflows/3802-update-airtable-crm-with-telli-call-event-data-and-appointment-status/)
## Next Steps
Once you've enabled the telli community node, you can:
1. **Create workflows** that automatically add contacts to telli
2. **Schedule AI voice calls** based on triggers from other systems
3. **Update your CRM** with call outcomes and appointment status
4. **Integrate with popular tools** like Airtable, Google Sheets, and more
For detailed workflow examples, see the [Add contacts & schedule calls](../add-contacts-and-schedule-calls) and [Webhooks](../webhooks) guides.
# Salesforce
Source: https://docs.telli.com/integrations/salesforce
Connect Salesforce to telli to sync Contacts, Leads, and Person Accounts into your workspace and map CRM fields to telli contact properties.
## Overview
The telli Salesforce integration lets you bring CRM data into telli without building a custom sync.
Today, the integration is focused on inbound sync from Salesforce to telli:
* Connect Salesforce with OAuth from the telli app
* Sync Salesforce `Contact`, `Lead`, and Person Account records into telli
* Map Salesforce fields to telli contact properties
* Filter which Salesforce records should be synced
* Keep agents up to date with current CRM context during calls
Use workflows to write completed call data back to Salesforce, including logging calls as Salesforce tasks.
## Before you start
* Access to the telli app
* A Salesforce admin who can install a managed package in the org you want to connect
* Access to the Salesforce org you want to connect (production or sandbox)
* A Salesforce user with the **API Enabled** permission. If your org uses [API Access Control](https://help.salesforce.com/articleView?id=sf.security_api_access_control_all_users.htm\&type=5), the user must also be authorized for the telli connected app, or have the broad **Use Any API Client** permission.
* A clear idea of which telli contact properties should receive Salesforce data
## Set up Salesforce in telli
In telli, go to **Settings** -> **Integrations** -> **Salesforce** and select **Enable** to open the setup dialog.
On the **Install** step, select **Install** to open the Salesforce package installation page in a new tab. A Salesforce admin must complete the installation in the target org before OAuth will work. If the package is already installed, select **Continue with existing installation** to skip ahead.
The telli package must be installed in the target org first. Without it, OAuth fails with a "Cross-org OAuth flows are not supported for this external client app" error.
On the **Authorize** step, select **Connect production** or **Connect sandbox** depending on the org you are connecting, and complete the Salesforce OAuth flow.
Select which Salesforce objects telli should listen to. The current integration supports **Contact**, **Lead**, and **Person Account**. Person Account sync is only offered when [person accounts](https://help.salesforce.com/s/articleView?id=sales.account_person.htm\&type=5) are enabled in your Salesforce org, and only records with `IsPersonAccount = true` are synced — business accounts are never sent to telli.
Expand each synced object and map Salesforce fields to the telli contact properties your team uses.
Narrow the records that should be synced when you only want a subset of Salesforce records in telli.
Save your configuration and confirm the integration status is connected and deployed.
## How the sync works
After the telli package is installed and OAuth is completed, telli reads the Salesforce schema for the supported objects so you can configure mappings in the app.
When you save those mappings, telli deploys the Salesforce sync components required for the integration. After deployment, creates and updates on the synced objects are sent to telli in real time and applied using the mapping configuration you defined. Deletions are also synced on a regular daily basis.
## Authentication
telli connects to Salesforce using the OAuth 2.0 web server flow with PKCE. When you authorize the connection, you sign in on Salesforce's own login page and grant telli access to the connecting user. telli keeps the session active automatically — your Salesforce username and password are never shared with or stored by telli.
## Field mappings
Use field mappings to decide how Salesforce data should populate telli contacts.
* Map Salesforce fields to the telli properties your workflows already use
* Configure mappings separately for each synced object
* For person accounts, the person's fields live on `Account` under `Person` names — for example `PersonEmail` and `PersonMobilePhone`
* The Salesforce record ID and record URL are synced automatically
This gives your agents current CRM context before and during calls without manually copying data between systems.
## Sync filters
You can add extra conditions to control which Salesforce records are sent to telli.
By default, Salesforce contacts must have a first name, last name, and phone number to sync into telli. You can then add additional filters in the integration settings to further narrow the records that qualify.
## Manage the connection
From the Salesforce integration page in telli, you can:
* Reconnect an existing Salesforce org
* Disconnect the OAuth connection while preserving mappings for later reconnection
* Remove the integration entirely
* Redeploy the sync configuration if deployment falls out of sync or fails
## Troubleshooting
* **Session expired or token revoked**: reconnect the integration from the Salesforce settings page in telli.
* **REST API access blocked**: if telli says Salesforce blocked REST API access, make sure the connecting Salesforce user has **API Enabled**. If your org uses [API Access Control](https://help.salesforce.com/articleView?id=sf.security_api_access_control_all_users.htm\&type=5), approve the telli connected app for that user or grant **Use Any API Client**, then reconnect the integration.
* **Misconfigured badge**: hover over the orange **Misconfigured** badge for details. In production Salesforce orgs, make sure [Deploy Processes and Flows as Active](https://help.salesforce.com/s/articleView?id=platform.flow_distribute_deploy_active.htm\&type=5) is enabled. If it is not enabled, new flow deployments require an admin to activate them manually in Salesforce.
* **Deployment failed or out of sync**: use **Redeploy** from the integration page.
* **Records not syncing**: review the selected sync objects, field mappings, and sync filters after the connection and deployment status are healthy.
# Slack
Source: https://docs.telli.com/integrations/slack
Install Charlie in your Slack workspace so your team can ask about agents and calls by mentioning it in a channel.
The Slack integration brings [Charlie](/platform/charlie), telli's AI assistant, into your Slack workspace. Once connected, your team can mention Charlie in a channel to ask about agents, calls, and results without leaving Slack.
## Before you start
You need:
* Access to the telli app.
* A Slack workspace admin who can approve the Charlie app during installation.
* Permission to add Charlie to the channels you want it to see.
## Connect Slack
Connect a Slack workspace before your team can talk to Charlie in Slack. telli installs the Charlie Slack app into the workspace and securely stores the workspace bot token for your telli account.
In telli, go to **Settings** > **Integrations** > **Slack** and select **Enable**.
Sign in with a Slack account that can administer the workspace and approve the Charlie app permissions.
Slack redirects you back to telli after approval. telli saves the connection and shows the connected workspace.
Choose the channels Charlie should be part of. Charlie only sees messages in channels it has been added to.
Pick a channel where Charlie introduces itself, so your team knows how to reach it.
## How the connection works
A workspace admin starts the OAuth flow from telli, and Slack asks them to approve the Charlie app permissions. telli then verifies the OAuth response, encrypts the Slack bot token, and stores the workspace metadata so Slack event handling can use the correct installation.
Once connected, Charlie can receive Slack events for mentions, channels, files, and reactions according to the scopes approved during installation.
## Talk to Charlie in Slack
Charlie answers questions about your telli agents and calls, the same way it does in the Charlie chat panel in telli.
* **In a channel**, mention Charlie in a message and it replies in the thread.
* **In a thread**, keep the conversation going by mentioning Charlie in your reply. Send `mute` to have it stop listening in that thread and `unmute` to have it listen again.
Charlie only works in channels it has been added to; it does not respond to direct messages.
Try asking Charlie things like:
* How did our calls go yesterday?
* Why wasn't this contact reached?
* Which of my agents handles the most calls?
* Summarize this week's call results.
## Manage channels
Charlie only sees messages in channels it has been added to. From the Slack integration page in telli, select **Manage channels** to add Charlie to more channels or remove it from channels it no longer needs.
## Manage the connection
From the Slack integration page in telli, you can:
* Reconnect the Slack workspace.
* Disconnect the workspace so Charlie stops responding to Slack messages, while keeping the integration in place.
* Delete the integration entirely.
## Troubleshooting
* **Charlie doesn't respond in a channel**: confirm Charlie has been added to that channel, and that you mentioned it in the message.
* **Reconnect required**: if the integration shows an error, reconnect the workspace from the Slack settings page in telli.
# WhatsApp Business
Source: https://docs.telli.com/integrations/whatsapp
Send WhatsApp template messages from agents and workflows in telli
WhatsApp Business lets you send approved WhatsApp template messages from agents and workflows. Use it when a contact expects updates on WhatsApp, when delivery and read status matter, or when a template-based follow-up is better than an SMS.
## Before you start
You need:
* Admin access to the Meta business that owns the WhatsApp Business Account.
* A WhatsApp Business Account and phone number that can be used for the official WhatsApp Business Platform.
* Permission to grant `whatsapp_business_management` and `whatsapp_business_messaging` during Meta setup.
* At least one approved WhatsApp template in Meta before you configure WhatsApp messages in telli.
* A contact with a valid phone number.
* Consent to message the recipient on WhatsApp.
## Connect WhatsApp Business
Connect WhatsApp Business before using WhatsApp messages from agents and workflows. telli uses Meta Embedded Signup to connect your WhatsApp Business Account, register the selected phone number, and load approved templates from Meta.
In telli, go to **Settings** > **Integrations** > **WhatsApp Business**.
Click **Continue with Facebook** and sign in with a Meta account that can administer the WhatsApp Business Account.
In the Meta setup dialog, select the Meta business, WhatsApp Business Account, and phone number you want telli to use.
Keep the telli tab open while setup finishes. telli saves the connection and registers the selected phone number.
The WhatsApp Business integration page shows connected phone numbers and their status. The phone number must be **Active** before agents or workflows can send WhatsApp messages.
## Handle two-step verification
If Meta reports that two-step verification is enabled for the selected phone number, telli shows a warning with a link to WhatsApp Manager.
Click **Open Manager** from the warning in telli.
In WhatsApp Manager, disable two-step verification for the selected phone number.
Return to telli and click **Retry registration**. Use the same setup flow instead of starting a new connection.
## Add another phone number
After the first phone number is connected, click **Add phone number** on the WhatsApp Business integration page and repeat Embedded Signup. telli lists all connected WhatsApp phone numbers in the integration detail page.
## Manage templates
WhatsApp templates are managed in Meta. In telli, WhatsApp tool and workflow dialogs include a **Manage templates** link when the connected Meta account is available.
After a template is approved in Meta, select it in an agent tool or workflow action, choose the language, preview the message, and map any template fields to telli data.
## Send WhatsApp from an agent
Use an agent WhatsApp tool when the agent should send a configured template during a live call, such as an appointment reminder, confirmation, or follow-up link.
Open the agent you want to edit, then go to the agent tools section in the builder sidebar.
Under **WhatsApp**, click **Add WhatsApp tool**.
Give the message a label, such as `appointment_reminder` or `booking_link`. Labels must start with a letter or underscore and can contain letters, numbers, and underscores.
Select the active WhatsApp Business phone number that should send the message.
Select an approved Meta template and language. If the template has fields, preview the message and map each field to a contact value or constant value.
Add the WhatsApp tool reference to the prompt, for example `@sendWhatsApp:appointment_reminder`, and explain when the agent should send it.
The agent can only send the WhatsApp templates you configure. This keeps outbound messages predictable and aligned with Meta template approval.
## Send WhatsApp from a workflow
Use the **Send WhatsApp** workflow action when telli should message a contact automatically.
Go to **Workflows**, then create a workflow or open an existing draft.
Select the event that should start the workflow. WhatsApp works especially well after **Call ended** and contact-based triggers.
Add a **Send WhatsApp** action block after the trigger or after a condition.
Select the active WhatsApp Business phone number that should send the message.
**To** defaults to the contact phone number. You can select another contact property of type **Phone number**, a **Collected Data** field in a call-based workflow, or a fixed number. Fixed numbers use [E.164](../phone-number-format#fields-that-require-strict-e164). The message stays associated with the workflow contact.
Select an approved template and language, preview the message, then map any template fields to workflow data or constant values.
Publish the workflow, then run it manually against a recent call or contact before relying on automatic sends.
See [Workflows](../platform/workflows) for the full workflow publishing and run-monitoring flow.
## Review WhatsApp activity
WhatsApp conversations appear in **Conversations** alongside calls and SMS. Open a conversation to see the message history and delivery state.
WhatsApp activity also appears on the contact timeline, so you can review follow-ups next to the contact's calls and scheduled activity.
Common delivery states are:
| Status | Meaning |
| :---------- | :---------------------------------------------------------- |
| `queued` | telli sent the message to Meta and it is waiting to be sent |
| `sent` | Meta accepted and sent the message |
| `delivered` | WhatsApp reported delivery to the recipient |
| `read` | The recipient read the message |
| `failed` | The send failed before delivery |
Inbound replies from known phone numbers are added to the matching WhatsApp conversation. Replies from unknown numbers create a new contact from the sender number.
## Limitations
* Business-initiated WhatsApp messages use approved Meta templates.
* Free-form outbound messages are not available from workflows or agent tools.
* WhatsApp sends require an active WhatsApp Business phone number connected in telli.
* Templates must be managed and approved in Meta before they can be selected in telli.
* WhatsApp Calling templates and dynamic WhatsApp Flow templates are not supported.
* Additional WhatsApp usage charges may apply.
## Remove WhatsApp
You can remove individual WhatsApp phone numbers from the integration page. If you remove the last active phone number, WhatsApp sending becomes unavailable until another phone number is connected.
# Zapier
Source: https://docs.telli.com/integrations/zapier
This tutorial demonstrates how to use [Zapier](https://www.zapier.com) to automate adding contacts, scheduling calls, and updating CRM contact status based on call outcomes.
## Prerequisites
* Accounts at [telli](https://telli.app) and [Zapier](https://www.zapier.com)
* API key from your telli dashboard (Settings > Developer)
## Workflow Templates
Get started quickly with pre-built Zapier templates:
* [Add contacts & schedule calls in telli](https://zapier.com/webintent/create-zap?template=255629409)
* [Update CRM based on call outcome](https://zapier.com/webintent/create-zap?template=255629554)
## Next Steps
Once you've connected telli to Zapier, you can:
1. **Create Zaps** that automatically add contacts from your CRM to telli
2. **Schedule AI voice calls** based on triggers from other apps
3. **Update CRM records** with call outcomes using webhooks
4. **Integrate with popular apps** like Airtable, Google Sheets, Salesforce, and more
For detailed workflow examples, see the [Add contacts & schedule calls](../add-contacts-and-schedule-calls) and [Webhooks](../webhooks) guides.
# Zeeg
Source: https://docs.telli.com/integrations/zeeg
Connect Zeeg to telli so agents can check availability, book meetings, and fill Zeeg scheduling page questions during calls.
## Overview
Zeeg is configured per agent so each workflow can use the right scheduling page, duration, and booking-field mapping.
This works well when different agents book different meeting types or route to different teams.
## Before you start
* A telli agent that should book appointments
* A Zeeg account with an active scheduling page
* A Zeeg API token from [Account Settings > API](https://app.zeeg.me/account/settings/api-access)
* A Zeeg plan that allows availability lookup and programmatic booking through the API
Use a least-privilege Zeeg API token. For telli, the token needs access to:
| Scope | Why telli needs it |
| ------------- | ---------------------------------------------------------------- |
| `events:read` | Read scheduling pages and their custom invitee questions |
| `timetable` | Read available time slots for the selected scheduling page |
| `booking` | Write bookings by creating the booked Zeeg event during the call |
Zeeg's availability and booking API endpoints require an active paid subscription.
## Set up Zeeg in telli
Open the agent you want to configure in telli.In **Calendar Integration**, select **Zeeg**.
Create a Zeeg API token with `events:read`, `timetable` for reading availability, and `booking` for writing bookings. Then add it to
telli. telli loads the scheduling pages available to that token.
Choose the Zeeg scheduling page the agent should use for availability checks and booking.
Open the settings for the selected scheduling page and map the booking questions you want telli to fill automatically.
Save the agent so telli can use the selected Zeeg setup during calls.
## Booking fields
When connecting Zeeg, you can configure booking fields to automatically fill the custom invitee questions on your Zeeg scheduling page during booking.
Each booking field maps a Zeeg question to a value source.
| Type | Description | Example |
| -------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------ |
| **Constant** | A fixed value, always the same | Brand name, company name |
| **Contact property** | Resolved from the contact's data. If not available, the agent asks the caller for it | Phone number, email, first name |
| **System variable** | Resolved from internal system data using a path expression | Contact ID, timezone |
| **LLM parameter** | Collected by the agent during the conversation | Reason for appointment, preferred language |
## How booking works
* telli lets you pick one Zeeg scheduling page per agent
* During calls, the agent checks available slots for the selected page
* When the caller chooses a slot, telli books the Zeeg event with the collected caller details
* Booking fields are filled automatically during booking
* Contact properties and constants are used without extra conversation
* LLM parameters prompt the agent to collect missing information from the caller
Every required custom question on your Zeeg scheduling page must have a corresponding booking field configured, otherwise the booking will
fail. Optional questions without a mapping are skipped.
# Introduction
Source: https://docs.telli.com/introduction
Overview of the telli platform and what you can build with it
> This section is designed for users of our platform and provides an overview over our current capabilities.
**telli agents turn leads into sales opportunities** by reaching your customers, leading human-like conversations, and completing tasks end-to-end.
## Feature Overview
Our platform offers several key features:
* **[Charlie:](/platform/charlie)** Build and refine agents, automate workflows, and analyze your calls with telli's built-in AI assistant.
* **[Auto Dialer:](deep-dives/auto-dialer)** Automate your outbound calling campaigns with intelligent dialing strategies.
* **[Call Analysis:](deep-dives/call-analysis)** Get detailed insights and analytics from your phone conversations with AI-powered transcription and analysis.
* **[Calendar Integrations:](/calendar-integrations)** Seamlessly integrate with your existing calendar systems for automated scheduling and booking.
* **[API Endpoints:](/v1/endpoint/)** Use telli's REST API to programmatically manage contacts, schedule calls, and integrate with your existing systems.
* **[Webhooks:](/webhooks)** Receive real-time notifications about call events, status changes, and outcomes.
* **[Custom Tools:](/custom-tools)** Extend telli's capabilities with custom integrations and workflows.
* **[Integration Examples:](/integrations-overview)** Learn how to connect telli with popular platforms like Make, n8n, and Zapier.
If you have any questions or need assistance, please don't hesitate to reach out to us at [support@telli.com](mailto:support@telli.com).
# Phone Number Format
Source: https://docs.telli.com/phone-number-format
Which phone number formats telli accepts, how numbers are normalized, and why everything is stored in E.164
telli stores phone numbers in **[E.164](https://en.wikipedia.org/wiki/E.164)**, the international standard format: a `+`, the country code, and the subscriber number, with no spaces, punctuation, or national dialing prefix - for example `+4915112345678` or `+14155552671`. Numbers that pass validation are normalized to this form and returned that way in API responses, webhook payloads, and exports.
You don't have to submit numbers in exact E.164, though. Every path that brings contacts into telli - the contact API, CSV import, and the CRM integrations - runs numbers through the same normalization and validation, then stores the E.164 result.
## Accepted input formats
Using a German mobile number as an example, these inputs all normalize to `+491751234567`:
| Input | Accepted | Notes |
| ------------------ | -------- | ---------------------------------------------------------------- |
| `+491751234567` | ✅ | Already E.164 |
| `+49 175 1234567` | ✅ | Spaces, dots, hyphens, and parentheses are ignored |
| `0049 175 1234567` | ✅ | The `00` international prefix is converted to `+` |
| `491751234567` | ✅ | A missing `+` is added when the digits start with a country code |
| `+491751234567` | ✅ | Leading and trailing whitespace is trimmed |
| `01751234567` | ❌ | National format without a country code - see below |
After normalization, telli validates the number against the numbering plan of the detected country and rejects numbers that aren't valid there, such as an unrecognized country code or an impossible length.
## Why national formats are rejected
A number like `01751234567` is ambiguous: without a country code, telli can't tell whether it's a German, Austrian, or other number. Guessing a default country would silently turn some numbers into valid-looking but wrong international numbers - and calls would reach the wrong person. That's why numbers must carry explicit country context.
Convert national numbers to E.164 on your side before sending them. For German numbers that's a simple transformation: replace the leading `0` with `+49` (`01751234567` → `+491751234567`). The rule differs by country - Italian numbers keep their leading zero after `+39`, for example - so for anything beyond a single known country, use a library instead of a hand-rolled rule.
If you integrate in code, use a [libphonenumber](https://github.com/google/libphonenumber) port for your language to parse and format numbers to E.164 - ports exist for JavaScript, Python, Java, PHP, and most other ecosystems.
## Where this applies
The same normalization and validation runs on the main paths where phone numbers enter telli:
* The contact API - [v2](/v2/endpoint/create-contact) and [v1](/v1/endpoint/add-contact)
* [CSV import](/platform/contacts#csv-import) in the app
* The [HubSpot](/integrations/hubspot) and [Salesforce](/integrations/salesforce) CRM integrations
Numbers accepted on these paths are stored - and later returned in [webhook payloads](/webhooks), API responses, and exports - as the normalized E.164 number.
### Fields that require strict E.164
A few fields skip the lenient normalization and only accept numbers that already start with `+`. The `00` prefix and bare country-code forms are rejected there:
* Values of custom [contact properties](/platform/contact-properties) with the `phone_number` type (spaces and punctuation are tolerated)
* An agent's [transfer number](/deep-dives/call-transfer) (digits only - no spaces or punctuation)
* Recipient numbers in a [Send SMS](platform/sms) or [Send WhatsApp](integrations/whatsapp) workflow action
# Agents
Source: https://docs.telli.com/platform/agents
Manage and configure your AI Voice agents
The Agents section is your hub to manage and configure all your AI Voice agents. Each agent is a unique persona designed to handle specific sales and customer service calls. On the main page, you'll find a list of all your configured agents, each entry showing its Agent Name, Persona Name, and whether Auto Dialer is enabled, allowing you to quickly view and then dive into detailed configurations for each.
## Agent Details
Clicking on an agent opens the [Agent Builder](/deep-dives/agent-builder), your central cockpit for fine-tuning its behavior and capabilities. You'll find a unique Agent ID for integrations or debugging, the prompt editor, and [Charlie](/platform/charlie) — the AI assistant available in the chat panel — to customize the agent's instructions, personality, and overall conversational flow.
For telli duo, you and Charlie cannot change the Thinking model. Collect Data, recording consent, voicemail messages, iOS call screening, call-me-later scheduling, and warm transfers are currently unavailable for Duo. You can still use direct transfers, explicit recording, voicemail detection with hangup, and post-call outcomes.
## Persona
Under Persona, you can define the agent's identity:
**Persona Name**: The name your agent uses to refer to itself during conversations (e.g., Anna).
* **Language and Voice**: The primary language is selected by default but you can choose a different voice (e.g. accents and gender) that matches your brand's tone. You can preview the voice directly. If you want to add your own custom voice, please contact [support@telli.com](mailto:support@telli.com).
* **Background Noise**: Set an ambient background sound, like "Office," to make the call sound more natural.
* **First Messages**: Customize the initial greetings for both outgoing calls (e.g., "Hello, my name is `{{personaName}}` from XY. Am I speaking with salutation `{{salutation}}` `{{lastName}}`?") and incoming calls. Dynamic variables ensure personalization.
## Calls
The Calls section manages your agent's telephony settings:
* **Max Call Length**: Set maximum duration allowed for each call.
* **Transfer Number**: Define a number where the agent can transfer calls if human intervention is needed (e.g., for complex issues or sales handovers). Learn more about [Call Transfer](/deep-dives/call-transfer).
* **Incoming Pickup Number**: Assign a dedicated Incoming Pickup Number to route calls directly to this agent. In this case, we recommend enabling Incoming Pickup so the agent can answer calls from unrecognized numbers.
* **Recognized Callbacks**: Enable Recognized Callbacks so the agent can identify returning callers based on your contact data. Compared to the Incoming Pickup feature, this will only allow callers who have been recognized to call back.
* **Call me later**: The Call Me Later feature allows customers to request a callback at a more convenient time, which the agent will automatically schedule, if the autodialer is enabled.
* **Call recording**: Control whether each call is recorded (remember to check relevant regulatory requirements). We recommend enabling call recordings during the testing period to gain better insights into agent behavior and accelerate agent optimization.
## Auto Dialer
* **Auto Dialer & Strategy**: For outbound operations, we recommend enabling the Auto Dialer to schedule missed calls automatically. You can choose between [Smart Dialing](/deep-dives/auto-dialer), an AI-based strategy optimizing for the best call times, or Interval-based dialing, which allows you to set specific intervals between call attempts.
* **Max Retry Days**: Set the maximum number of days the system will attempt to call a contact using the Smart Dialing strategy, ensuring persistent but not overly aggressive outreach.
## Calendar Integration
Enable the Calendar Integration to automatically schedule appointments during business hours. Learn more about [Calendar Integration](/calendar-integrations).
## Tools & Post Processing
The Tools section allows you to enable key functionalities. Your agent can be configured to End a Call when appropriate and to use Voicemail Detection to identify when it has reached a voicemail.
**Post-processing is crucial for data collection and follow-up**:
**Agent Extractions**: Define specific keywords or phrases that agents should identify and extract from conversations. These extractions are crucial for categorizing call outcomes, identifying customer issues, or capturing important data points for post-call analysis and CRM updates. Some extractions can be added to the dashboard as Call Outcomes. Learn more about [Call Analysis](/deep-dives/call-analysis).
**Notification Emails**: Set up email addresses to receive notifications based on agent interactions.
# Branded Calling
Source: https://docs.telli.com/platform/branded-calling
Show your verified business name on recipients' phones during outbound calls
Branded Calling shows your verified business name on recipients' phones when your telli agents make outbound calls from assigned numbers. It adds your business name to the caller identity; your [outbound phone number configuration](./phone-numbers) still determines which number telli uses.
## Eligibility
Branded Calling is available only for active, telli-managed phone numbers.
Imported SIP numbers cannot be added.
A phone number must match the country of the registration and can belong to only one Branded Calling registration at a time.
## Request Branded Calling
Go to **Phone numbers** > **Verifications** > **Branded Calling**.
Select **Add Branded Calling Registration**, then select **Message us**. telli guides you through manual onboarding and tells you when the registration is ready.
## Registration statuses
| Status | What it means | Your action |
| ------------------ | ------------------------------------------------ | --------------------------------------- |
| **Pending Review** | The registration is waiting for review. | No action is required. |
| **In Review** | The registration is being reviewed. | Follow telli's guidance. |
| **Rejected** | The registration could not be approved. | Message telli to review the next steps. |
| **Approved** | The registration is ready for number management. | Add or remove matching phone numbers. |
## Assign or remove phone numbers
You can manage phone numbers after the registration is approved.
Go to **Phone numbers** > **Verifications** > **Branded Calling**. Open the registration's **Actions** menu, then select **Manage numbers**.
Add or remove telli-managed phone numbers from the same country as the registration. A number assigned to another registration does not appear under **Available Numbers**.
Review of a new number assignment can take 24-48 hours. Removal can also take 24-48 hours.
## Verify Branded Calling
After the 24-48-hour assignment review, allow up to two more hours for the display name to update. Then place an outbound test call from the assigned number.
### Use different display names
Each registration has one display name. Request a separate registration when different brands, teams, or campaigns need different names. Assign each outbound number to the registration whose name it should show.
## Limits
| Item | Limit |
| -------------------- | ------------------------------------------------- |
| Eligible numbers | Active, telli-managed phone numbers |
| Imported SIP numbers | Not supported |
| Registration country | Must match the phone number country |
| Number assignment | One Branded Calling registration per phone number |
| Number management | Approved registrations only |
| Assignment review | Up to 24-48 hours |
| Display propagation | Up to two hours after review |
| Removal | Up to 24-48 hours |
## Troubleshooting
### The registration does not appear
Selecting **Message us** starts manual onboarding; it does not create the registration immediately. Continue the support conversation with telli.
### A phone number cannot be managed
Confirm that the registration is **Approved**, the number is managed by telli, and both use the same country. Also check whether the number is already assigned to another registration. Message telli if you still cannot manage it.
# Conversation History
Source: https://docs.telli.com/platform/call-history
Comprehensive view of all call interactions and analysis
## Call List
The left-hand side displays a chronological list of all recent calls.
Each entry shows:
* Phone number of the contact
* Call direction – usually Outgoing
* Call status – e.g. Completed, Mailbox, or - Not Reached
* Time of call – e.g. "2 hours ago", "3 days ago"
You can filter this list by:
1. **Call Status**:
* **Completed**: A full conversation was conducted.
* **Answered**: The call was picked up, but not fully completed (e.g. early hang-up, asking for a callback).
* **Voicemail**: The agent reached voicemail.
* **Error**: The call failed due to technical reasons (often due to an invalid phone number or phone provider issues).
* **Not Reached**: The call attempt failed (e.g. no answer, unavailable).
2. **Time Range**: Filter calls within a specific time range
3. **Agent**: See calls made by a specific Voice AI agent or use case funnel.
4. **Custom Call Outcomes**: Specific Outcomes you have defined in the [Call Analysis](/deep-dives/call-analysis) section.
5. **Tools**: Filter calls by the tools the agent used. Select **Book appointment** to find calls where the agent attempted a calendar booking, including failed attempts. Check the tool result in the call details to confirm whether the booking succeeded.
## Call Details
When you click on a call, you'll see the call details on the right-hand side:
* **Basic information**: Phone number, contact name, agent, language, call direction, disconnection reason
* Call duration and final call status
* **Call Outcomes**:
* **Summary**: A short AI-generated summary of the call content and outcome
* **Dialogue**: Whether a real conversation happened (true or false)
* **Call Score**: A qualitative rating based on logical flow, clarity, and satisfaction
* **Sales Performance**: Did the agent achieve the intended goal?
* **Call Outcomes**: Follow-up, Interest, Appointment, Voicemail, Transfer, or any other outcome you have defined in the [Call Analysis](/deep-dives/call-analysis) section.
* **Conversation**: Read the transcript or listen to the conversation again (if Call Recordings is enabled). This helps you to understand and review the whole conversation between your customer and the telli agent.
You can also provide feedback on the call outcome and the AI agent's performance by clicking the 👍🏻 👎🏻 buttons on the right-hand side of each call analysis field or within each sentence of the transcript. Read more about this in the [Feedback System](/deep-dives/feedback-system) section.
# Charlie
Source: https://docs.telli.com/platform/charlie
Meet Charlie, the AI assistant built into telli that builds agents, automates workflows, and analyzes your calls with you
**Charlie** is the AI assistant built into telli. Describe what you want in plain language, and Charlie does the work with you: it builds and refines agents, sets up workflows, digs through past calls, and answers questions about your account. Agent and workflow changes stay drafts until you review and approve them.
## Where you find Charlie
Charlie is one shortcut away on every page: press `Cmd+J` (`Ctrl+J` on Windows) or click **Ask Charlie** in the header. A new conversation picks up the agent or workflow you have open, so you don't have to re-explain the context. Conversations have direct URLs, so you can bookmark one and reopen it from any page.
The chat takes more than typed text: drop in files like a transcript, a script document, or a screenshot, or use voice input to talk through what you want.
## What Charlie can do
### Build and refine agents
In the [Agent Builder](/deep-dives/agent-builder), Charlie sits in the chat panel next to the prompt editor. It drafts prompt edits that appear inline for you to accept or reject, adjusts call behavior like voicemail detection, recording, transfers, and dialing strategy, sets up [call outcomes](/deep-dives/call-analysis), and connects tools, calendars, and knowledge bases. When you sign up and provide your company website, Charlie researches your company and builds your first agent with you.
### Build workflows
Charlie creates, duplicates, and edits [workflows](/platform/workflows) directly from chat — it drops in the trigger, condition, and action blocks, asks a clarifying question when it needs one, and proposes the publish before anything goes live. A duplicate copies the current draft, including unpublished changes, into a new disabled, unpublished workflow. The source workflow stays unchanged.
Charlie can also propose enabling or disabling the workflow you have open. Select **Review** to open the Workflow Builder confirmation dialog; the production state changes only after you confirm there. Enabling runs the current published revision, while unpublished draft changes stay in the draft. Publish a workflow before asking Charlie to enable it. This confirmation flow is available only in the telli app, not in Slack, Microsoft Teams, through MCP, or in scheduled Charlie tasks.
### Analyze calls and performance
Charlie searches past calls by date, outcome, transcript content, or tool errors, pulls full transcripts into the chat, and summarizes results across your account. From [Conversation History](/platform/call-history), you can select the exact transcript turns that need work and open Charlie with them already attached, turning a bad call moment into a concrete improvement.
### Manage contacts
Charlie can create one contact or update one specific existing contact when you ask. It can set built-in fields such as name, phone number, email, and timezone, plus your typed custom contact properties.
Contact creation and updates are available in the telli app, Slack, and through MCP. They are not available in Microsoft Teams or scheduled Charlie tasks.
When you explicitly ask Charlie to delete one contact, it shows the contact's verified name and phone number for review. Select **Review**, then confirm the existing deletion dialog. Confirmation deletes the contact and anonymizes its personal data. Canceling or a failed attempt leaves the request available to retry.
Charlie can find up to 100 specific contacts and propose adding them to the [Auto Dialer](/deep-dives/auto-dialer) with a selected agent. You can review and edit the agent or outgoing number before you select **Schedule calls**. Contacts already in the Auto Dialer remain selectable, with a warning that confirmation may update their active scheduling settings.
In the telli app, Charlie can also create [contact-property definitions](./contact-properties) and update their descriptions or append Select options. Before creating a property, Charlie shows the exact schema and asks you to confirm it. Explicit description-only updates run directly. Before adding Select options, Charlie shows the new options and asks you to confirm because existing options cannot be removed. These actions change the account schema, not a property value on one contact.
Charlie can also propose removing up to 100 specific contacts from the Auto Dialer. Select **Review** in the chat to open the same confirmation dialog used on the Contacts page. No scheduling or removal happens until you confirm the exact contacts in that dialog.
Deletion, scheduling, and removal approvals are available when you use Charlie in the telli app. Charlie in Slack, Microsoft Teams, through MCP, or in a scheduled Charlie task cannot present these confirmation dialogs.
Creating and updating contact-property definitions is also available only in the telli app.
### Find your way around
Ask where to find or configure something and Charlie takes you to the right page. Its answers link agents, workflows, and calls inline, so anything it mentions is one click away.
## You stay in control
Charlie keeps drafts and confirmations where review is required.
* **Every edit is a draft.** Prompt edits appear as inline diffs you accept or reject — individually or all at once — and every agent or workflow change lands in the draft. The live version keeps running until you publish.
* **Checkpoints.** While you work on an agent in the Builder, Charlie saves checkpoints so you can roll back within that session in one click.
* **No surprise publishes.** Charlie never publishes an agent or workflow on its own — it always asks first.
* **No surprise activation changes.** Charlie never enables or disables a workflow on its own — you confirm the change in the Workflow Builder.
* **No surprise deletions.** Charlie never deletes a contact on its own — you review the verified contact and confirm the deletion first.
* **No surprise scheduling.** Charlie never schedules contacts in the Auto Dialer on its own — you confirm the contacts and calling configuration first.
* **No surprise removals.** Charlie never removes contacts from the Auto Dialer on its own — you confirm the exact set first.
## Charlie beyond the app
Mention Charlie in a channel to ask about agents, calls, and results without leaving Slack.
Message Charlie directly or add it to a conversation in Teams.
Connect Claude, ChatGPT, Cursor, and other AI tools to Charlie through the telli MCP server.
## Learn more
A step-by-step guide to reviewing and improving an agent with Charlie.
Advanced workflows, example prompts, and best practices.
# Contact Properties
Source: https://docs.telli.com/platform/contact-properties
Define a structured schema for your contacts with typed custom properties that you can use across the telli platform
Contact properties let you define a structured schema for your contacts — adding custom fields like "Lead Type", "Contract Start Date", or "Preferred Language" that are relevant to your business. Unlike free-text notes, every property has a defined type, so your data stays clean and consistent across all contacts.
### Why Contact Properties?
Before contact properties, the only way to attach custom data to a contact was through unstructured text fields. This worked, but had significant downsides:
* **No consistency** — One team member might write "yes", another "true", another "ja"
* **No validation** — Typos and formatting mistakes went unnoticed
* **No structure** — You couldn't filter, sort, or report on free-text fields reliably
Contact properties solve this by introducing **typed, validated fields** that ensure your contact data is structured and reliable.
***
## Property Types
telli supports the following property types, each with its own input and validation:
| Type | Description | Example Value |
| ---------------- | --------------------------------------- | --------------------------------------------- |
| **Text** | Free-text string | "Enterprise" |
| **Number** | Numeric value | 42 |
| **Boolean** | Yes or No | Yes |
| **Date** | Calendar date | 2025-03-15 |
| **Date & Time** | Calendar date with time | 2025-03-15 at 14:30 |
| **Select** | Single choice from a predefined list | "Gold" |
| **Multi-Select** | Multiple choices from a predefined list | "German", "English" |
| **Phone Number** | Phone number in international format | +49 172 1234567 |
| **Email** | Email address | [sarah@example.com](mailto:sarah@example.com) |
> **Tip:** Choose the most specific type for your data. For example, use **Date** instead of **Text** for dates — this ensures proper validation and a better editing experience in the contact details panel.
***
## Create Your First Property
### 1. Navigate to Settings
Head over to **Settings** and select the **Properties** tab.
### 2. Click "Create Property"
Click the **Create property** button to open the property editor.
### 3. Configure the Property
Fill in the details for your new property:
* **Type** — Select the data type (e.g. Text, Select, Date)
* **Key** — Give it a clear key (e.g. `lead_source`). Keys cannot change after creation
* **Description** *(optional)* — Add a note to help your team understand what this property is for
### 4. Add Options (Select & Multi-Select only)
If you chose **Select** or **Multi-Select**, you'll need to define the available options. Simply type the option name and press Enter to add it.
### 5. Save
Click **Save** and your new property is immediately available across all contacts.
***
## Editing Properties
You can edit an existing property at any time from the **Properties** tab in Settings. Click the menu icon next to any property and select **Edit**.
You can change or clear the **description** of any custom property. For Select and Multi-Select properties, you can also **add new options**.
### Important limitations when editing
* **You cannot change the property key or type** after creation. If you need a different key or type, create a new property and delete the old one
* **You cannot remove existing options** from Select or Multi-Select properties. This protects contacts that already have those values assigned. You can only add new options
***
## Manage Properties with Charlie
In the telli app, ask Charlie to create a custom contact property or update its description and Select options. Charlie shows the exact schema and asks you to confirm before creation. Explicit description-only updates run directly. Before adding Select options, Charlie shows the new options and asks you to confirm because existing options cannot be removed.
***
## Deleting Properties
To delete a property, click the menu icon next to it and select **Delete**. You'll be asked to confirm.
> **Warning:** Deleting a property permanently removes it and its values from all contacts. This action cannot be undone.
***
## Using Properties on Contacts
### In the Contact Details Panel
When you open a contact, you'll see your custom properties listed under the **Custom Properties** section. Each property renders with an appropriate input — a date picker for dates, a dropdown for select fields, checkboxes for multi-select, and so on.
Simply click on any property value to edit it, then hit **Save** to persist your changes.
### In the Contacts Table
You can show your custom properties as columns in the contacts table. Click the **column picker** icon to toggle which properties are visible.
### In Agent Prompts
You can reference contact properties inside your agent's prompt so the AI personalizes its conversation based on each contact's data. This is done through the **Variables** panel in the Agent Builder.
#### The Variables Tab
When editing an agent prompt in the Agent Builder, expand the **Variables** panel at the bottom of the editor. It shows three categories of variables you can insert into your prompt:
| Category | Description |
| ---------------------- | --------------------------------------------------------------------------- |
| **Custom variables** | Variables you define yourself — useful for values you want to set per agent |
| **Contact properties** | Your custom contact properties, automatically available as variables |
| **Read only** | System variables like `currentDate`, `firstName`, or `callDirection` |
Your contact properties appear automatically in the **Contact properties** section — no extra setup needed. Click any variable to copy it to your clipboard, then paste it into your prompt.
#### Referencing Variables in the Prompt
Variables use the `{{variableName}}` syntax. When you type `{{` in the prompt editor, an autocomplete dropdown appears with all available variables grouped by category. Select a variable to insert it as a styled chip in the editor.
For example, a prompt might look like:
```
You are a customer service agent. The caller's name is {{firstName}}.
Their loyalty tier is {{Loyalty Tier}} and their preferred language
is {{Preferred Language}}.
```
Contact property variables display their human-readable name (e.g. "Loyalty Tier") in the editor, making prompts easy to read and maintain.
#### How It Works at Runtime
When a call starts, telli replaces each `{{variable}}` with the actual value from the contact's data. If a contact has "Loyalty Tier" set to "Gold", the agent receives a prompt with that value filled in. Variables that don't have a value for a given contact are removed from the prompt automatically.
***
## Properties in CSV Import
When you import contacts via CSV, you can map CSV columns directly to your custom properties.
### Automatic Mapping
telli automatically detects properties by key, label, or the `Label (key)` header format used in the CSV template.
### Manual Mapping
During import, you can map any CSV column to an existing custom property or create a new one directly in the wizard.
In the import wizard, you can create these property types:
* Text
* Number
* Boolean
* Date
* Date & Time
* Phone Number
* Email
For existing **Select** and **Multi-Select** properties, map the CSV column to that property during the mapping step. For multi-select properties, separate multiple values in one cell with commas.
For the full CSV import flow, supported system fields, and scheduling options, see [Contacts](./contacts#csv-import).
***
## System Properties vs. Custom Properties
telli distinguishes between two kinds of properties:
| | System Properties | Custom Properties |
| -------------------- | ------------------------------------------ | ------------------------------------ |
| **Examples** | First Name, Last Name, Phone Number, Email | Lead Source, Contract Date, Language |
| **Who defines them** | Built into telli | Created by you |
| **Can be edited** | Values can be edited | Values and definitions can be edited |
| **Can be deleted** | No | Yes |
System properties are the core fields every contact has. Custom properties are the fields you add to match your business needs.
***
## Current Limits
| Resource | Limit |
| ------------------------------- | --------------------------------------------------------------------------------------- |
| Property types available | 9 (Text, Number, Boolean, Date, Date & Time, Select, Multi-Select, Phone Number, Email) |
| Options per Select/Multi-Select | Unlimited (append-only — existing options cannot be removed) |
| Property type changes | Not supported after creation |
> **Note:** These limits may change as we continue to improve the platform.
***
**💡 Pro tip:** Start by defining properties for the data your agents reference most during calls — things like customer tier, contract type, or preferred language. Once defined, reference them in your agent prompt via the Variables panel in the Agent Builder to deliver personalized conversations automatically.
# Contacts
Source: https://docs.telli.com/platform/contacts
Full visibility into your contact outreach
The Contacts section gives you full visibility into each person your telli agent is trying to reach or has already interacted with.
## Add or Search Contacts
At the top, you can:
* Add new contacts manually
* Search existing contacts using filters or contact IDs
## Contact Status
Each contact is labeled with one of the following statuses:
* **New**: The contact has been added but hasn't been called yet
* **Pending**: The agent has attempted to reach the contact, but no successful interaction has occurred yet. The contact will be automatically called again
* **Reached**: The agent successfully reached the contact and had a conversation
* **Closed**: The contact is marked as done and will not be called again
You can filter by these statuses using the dropdown at the top left.
## Contacts Table
The contacts table shows your contacts along with their key information. You can customize which columns are visible using the **column picker** — including any [custom properties](/platform/contact-properties) you've defined for your account.
## Contact Details Panel
Clicking on any contact opens their detailed view. The panel is organized into sections:
* **System properties** — Core fields like phone number, email, timezone, and external ID
* **Custom properties** — Any [contact properties](/platform/contact-properties) you've defined, displayed with type-appropriate inputs (date pickers, dropdowns, checkboxes, etc.)
* **Call attempt history** — Full timeline of interactions with this contact
You can edit any property value directly in the panel and save your changes.
## Call Attempt History
Scroll down in the contact detail view to see the Call Attempt History. For each attempt, you'll find:
* Timestamp of the call
* Call direction (e.g. outgoing)
* Result (e.g. completed, failed, not reached)
This lets you trace the full communication history with one contact — the call details can be found in the [Conversation History](/platform/call-history) section.
## CSV Import
CSV import lets you add or update contacts from the Contacts page without using the API:
* Upload a `.csv` file
* Map every column to a telli system field, a custom property, or **Do not import**
* Review how many rows will create contacts, update existing contacts, or be skipped
* Import the file
* Optionally schedule calls for all successfully imported contacts
Limits:
* Maximum file size: `10MB`
* Maximum rows per file: `100,000`
Your CSV file must include a header row. These system fields are available in the import wizard:
| Column name | Description | Required |
| -------------- | ------------------------------------------------------------------------------------------ | -------- |
| `external_id` | Your unique identifier for the contact in your own system | Yes |
| `first_name` | Contact's first name | Yes |
| `last_name` | Contact's last name | Yes |
| `phone_number` | Contact's phone number in an [accepted format](/phone-number-format), for example `+4917…` | Yes |
| `email` | Contact's email address | No |
| `timezone` | IANA timezone, for example `Europe/Berlin` | No |
| `external_url` | Link to the contact in your CRM or another external system | No |
`external_id` is the primary import field. If you still use older CSV files with `external_contact_id`, telli can recognize that header during column mapping.
Here's an example of a valid CSV file:
```csv theme={null}
external_id,first_name,last_name,phone_number,email,timezone,lead_type
ID10001,Sarah,Smith,+491724222923,sarah.smith@test.com,Europe/Berlin,reactivated
ID10002,Michael,Scott,+4917682494115,test@test.com,America/New_York,new_lead
```
### Custom Properties in CSV
If you've defined [contact properties](./contact-properties), you can use them directly in CSV import:
* **Download the template** to get a CSV with all your custom properties already included as headers
* **Template headers use `Label (key)`** for custom properties, for example `Lead Type (lead_type)`
* **Automatic mapping** matches custom properties by key, label, or the `Label (key)` header format
* **Manual mapping** lets you assign any CSV column to an existing custom property
* **Create properties in the wizard** if a matching property does not exist yet
When you create a new property in the import wizard, these property types are available:
* Text
* Number
* Boolean
* Date
* Date & Time
* Phone Number
* Email
To import into an existing **Select** or **Multi-Select** property, map the CSV column to that property during the mapping step. For multi-select properties, separate multiple values in a single cell with commas.
### External ID
The external ID is a unique identifier from your system, such as a CRM lead ID or a database primary key.
telli uses this value to decide whether to create a new contact or update an existing one:
* If the external ID does not exist yet, telli creates a new contact
* If the external ID already exists, telli updates the existing contact instead of creating a duplicate
* If the same external ID appears multiple times in the same CSV, telli keeps one row for that external ID and uses the last valid row in the file
Rows with invalid data are skipped and shown as row-level errors during import.
### Scheduling Calls
After the import finishes, you can add all successfully imported contacts to the auto dialer. This step is optional.
* **Select an agent**: Choose which agent should call these contacts
* **Optional outbound number override**: If your account has multiple outbound numbers, you can choose which one to use
* **Schedule imported contacts in bulk**: telli schedules all successfully imported contacts from this import
* **Auto dialer behavior still applies**: If the selected agent's auto dialer is disabled, contacts are queued, but no calls are placed until it is enabled
## Deleting Contacts and Personal Data
Deleting a contact removes all personal data telli stores about that person, not only the contact record. Use it to fulfil deletion requests under the GDPR or when you no longer need a contact. Deletion is available on every plan and does not require the [data retention](/platform/data-retention) feature.
You can delete contacts in four ways:
* **Single contact**: Open the contact in the details panel and click **Delete Contact**
* **Several contacts at once**: Select the contacts in the table and choose **Delete** in the selection bar
* **Charlie**: Ask [Charlie](/platform/charlie) to delete an exact contact, then confirm the deletion in the app
* **API**: Call [Delete Contact](/v2/endpoint/delete-contact)
All four ways run the same deletion process and delete the same data.
### What gets deleted
| Data | What telli does |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Contact record | Name, phone number, email, salutation, gender, timezone, external ID and external URL, custom properties, and all other contact data are removed. An anonymized placeholder remains so that call statistics stay consistent. |
| Call recordings | All audio files of every call with the contact are deleted from telli's storage. |
| Transcripts | Raw and refined transcripts, transcript excerpts stored in the call details, and transcripts kept for speech-recognition quality review are deleted. |
| Call analysis | Summaries, call outcomes, collected data, call scores, notes, feedback comments, transfer targets, and dynamic variables of every call are deleted. |
| Call records | Phone numbers and all other personal data on every call with the contact are removed. An anonymized call record with technical metadata such as duration, status, timestamps, and agent remains for reporting and billing. |
| SMS conversations | Message contents of every conversation with the contact are removed, and the contact's phone number is removed from the conversation. |
| Workflow runs | Running workflows triggered by the contact or its calls are stopped, and the stored contact and call data of all runs is deleted. |
| Auto dialer | Active enrollments of the contact are canceled. |
| Telephony provider | telli deletes the call and message records its telephony provider holds for calls and SMS with the contact. |
Deletion is irreversible. telli never deletes records in your CRM or other connected systems; it only removes the CRM ID and URL stored in telli. Deleted contacts and their calls no longer appear in the telli app or API.
### Before you delete
* A contact can't be deleted while a call with it is in progress or is still being processed after it ended. Wait for the call to finish, then try again.
* Owners and admins can also delete the recording of a single call in the [Conversation History](/platform/call-history) without deleting the contact.
* To delete contacts, call data, recordings, or transcripts automatically after a set time, configure [data retention](/platform/data-retention). Data retention runs the same deletion process, is available on Enterprise plans, and is not required for manual deletion.
# Data Retention
Source: https://docs.telli.com/platform/data-retention
Delete contacts and call data automatically after a set time
Data retention deletes contacts and call data automatically once they reach an age you define. It runs the same deletion process as deleting a contact by hand, so the data it removes is the same. See [Deleting Contacts and Personal Data](/platform/contacts#deleting-contacts-and-personal-data) for the full list.
Data retention is available on Enterprise plans. Deleting contacts manually in the app, through Charlie, or through the API is available on every plan and does not require data retention. If data retention is not part of your plan, use **Contact Sales** on the Organization page to add it.
## Configure rules
Open [Settings > Organization](https://app.telli.com/_/settings/organization) and scroll to **Data retention**. Each rule has a switch and a retention period between 1 and 730 days.
### Contacts
| Rule | Deletes a contact when |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| **Created** | The contact was created more than the set number of days ago. |
| **Last contacted** | The contact's last call started more than the set number of days ago. Contacts that have never been called are not affected. |
If both rules are enabled, a contact is deleted as soon as either rule applies. Deleting a contact also deletes the personal data of all its calls.
### Calls
Call rules count from the moment the call was placed and apply to every call in your account, whether or not the contact still exists.
| Rule | Deletes |
| ----------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Transcripts** | The transcript of the call. |
| **Recordings** | The audio recording of the call. |
| **All call data** | All personal data of the call, including transcript, recording, and analysis. An anonymized call record remains. |
If **All call data** has a shorter or equal retention period than **Recordings** or **Transcripts**, it deletes them first and those rules have no additional effect.
## Save and confirm
Click **Save Changes** to review the pending rules. Because deleted data cannot be recovered, telli asks you to type a confirmation phrase before the rules take effect. Deleting data also reduces the call history Charlie can draw on for insights.
## How deletion runs
* telli checks all enabled rules once a day and deletes everything that has passed its retention period.
* Contacts in active calls are not deleted.
* Data is deleted in batches, so a large backlog can take more than one run to clear.
* Deletion is irreversible. telli never deletes records in your CRM or other connected systems.
# Knowledge Base
Source: https://docs.telli.com/platform/knowledge-base
Enrich your AI agent with the expertise and context it needs by including all of your knowledge sources
A knowledge base is a collection of your company's documents that you share with your AI agent. When you upload these files, your agent can search through them to find relevant information and answer customer questions accurately—based on **your approved documentation**, not guesses or outdated information from the internet.
### What can you include?
* **Product Guides** — Manuals, specifications, and how-to documentation
* **FAQs & Support** — Common questions and troubleshooting articles
* **Policies & Pricing** — Return policies, terms of service, and pricing sheets
* **Internal Procedures** — Guidelines and reference material your team uses
***
## Create Your First Knowledge Base
### 1. Navigate to Knowledge Base
Head over to the **Knowledge Base** tab in your dashboard.
### 2. Upload Your Documents
Enter a name for your Knowledge Base and upload your documents.
### 3. Wait for Processing
Once uploaded, we automatically process your documents and make them searchable by your AI agent. This usually takes just a few moments.
### 4. Assign to Your Agent
Once processing is complete, assign the Knowledge Base to your AI agent in **Agent Settings**.
***
## Updating Your Knowledge Base
You can update your Knowledge Base at any time by clicking on it from the Knowledge Base tab. From there you can:
* Edit the name of the Knowledge Base
* Add new documents
* Remove existing documents
> **Note:** Every time you update your Knowledge Base, we automatically re-process the documents to ensure your AI agent always has access to the latest information.
***
## How Your Agent Uses the Knowledge Base
During a conversation, your AI agent can search through the Knowledge Base to find relevant information. Think of it like an employee checking their notes or looking something up in a handbook.
You can guide how your agent communicates this to customers through your agent's prompt. For example:
| Scenario | Example prompt guidance |
| -------------------------- | -------------------------------------------------------- |
| Looking up product details | *"Let me quickly check our product documentation..."* |
| Finding policy information | *"One moment while I look that up in our guidelines..."* |
| Checking procedures | *"Let me have a look at my notes..."* |
This makes the conversation feel more natural and sets expectations for the customer while the agent retrieves the information.
***
## Supported File Types
| Format | Extension | Best For |
| -------- | --------- | ----------------------------------------------- |
| PDF | `.pdf` | Product manuals, brochures, formal policies |
| Word | `.docx` | Internal guides, procedures, editable documents |
| Text | `.txt` | Simple lists, quick references, FAQs |
| Markdown | `.md` | Structured documentation, technical guides |
***
Start with your most frequently asked customer questions. Make sure the answers to those are in your uploaded documents for the best results.
## Current Limits
| Resource | Limit |
| ------------------------------ | ------------------------------ |
| Knowledge Base per agent | 1 |
| Documents per Knowledge Base | 5 |
| Maximum file size per document | 20 MB |
| Supported formats | `.pdf`, `.docx`, `.txt`, `.md` |
**Storage:** Each Knowledge Base can hold up to \~1,500 pages worth of content (based on single-spaced A4 pages in 12pt font)—plenty of room for most documentation needs.
These limits may change as we continue to improve the platform.
# Live Monitoring
Source: https://docs.telli.com/platform/live-monitoring
Real-time call overview of your contacts
The Live Monitoring view helps you track your Voice AI agent's calling activity in real time. At the top, you'll see any active calls currently happening — this allows you to monitor ongoing interactions instantly.
Below that, the Scheduled Calls section **shows all upcoming calls** the AI agent is about to make. Each entry includes:
* Time & Date of the scheduled call
* Customer name & phone number
* Call status (e.g., "Pending")
* Attempt count, showing how many times the agent has already tried calling
This section is especially useful for **ensuring important follow-ups** are being handled without delay.
# Performance Dashboard
Source: https://docs.telli.com/platform/performance-dashboard
Real-time performance metrics for your telli Agent
The Performance Dashboard gives you a clear snapshot of how your telli Agent is performing in real time. It helps you track efficiency, optimize scripts, and measure impact.
You can view performance across your entire account or drill down to the agent level for more detailed insights.
## Key Metrics
Here's what each metric means:
* **Contacts Reached**: Number of successful conversations where your customer interacted with the telli agent.
* **Minutes Used**: Total time your agent spent on calls this week. Helps you track consumption against your plan.
* **Average Call Duration**: Average length of conversations. A higher duration can indicate more complex or engaging conversations.
* **Appointment Rate**: Percentage of calls that led to a successful booking with your human agent.
* **Success Rate**: Broader success metric based on your specific use case (e.g. qualification complete, info submitted). You can define your own success metrics in the [Call Analysis](/deep-dives/call-analysis) section.
* **Total Calls**: Total outbound/inbound calls initiated by the Voice AI agent.
## Timeframes & Agent Performance
All numbers reflect either weekly or monthly averages, depending on what you select at the top of the dashboard. The graphs below breaks these metrics down day by day, giving you a clearer view of performance patterns throughout the selected period.
On the bottom, you can also filter results by individual agent to see a detailed breakdown of their performance.
# Phone Numbers
Source: https://docs.telli.com/platform/phone-numbers
Buy a telli number, connect your own number, or forward calls to your agent
You can buy a number from telli, forward your current business number to a telli number, or connect your own number through SIP.
## Choose your setup
The fastest setup. Use a telli-managed number for incoming and outgoing calls. Some countries require business verification.
Keep your public business number and forward incoming calls to a telli number. You configure the forwarding with your phone provider or PBX.
Connect an existing number and phone system for incoming and outgoing calls. You need SIP trunk access from your provider.
**Call forwarding** sends calls from your current provider to telli. [Call
transfer](/deep-dives/call-transfer) sends an active call from the telli agent
to a person or team.
## Buy a telli number
### Before you start
Depending on the country and number type, you may need an approved business verification. Prepare the following information:
* Your legal business name, registration number, and registered address
* A current business registration document
* The name and contact details of an authorized representative
* The country, number type, and local area code that you need
For a German business, a commercial register extract or a business registration document is usually accepted. The company name, registration number, and address must match the data that you enter in telli.
A local number can require an address in the area covered by its prefix. Do
not use a PO box. Requirements differ by country and number type; the
verification form shows the required documents for your selection.
### Complete business verification
Go to **Phone numbers**, select **Verifications**, and stay on the **Regulatory Compliance** tab.
Select **Add Verification**, then choose the country and number type. Enter a clear internal name, such as `DE Local - Berlin office`.
Enter the legal business data and the authorized representative exactly as shown in the registration document.
Upload the documents requested in the form. Submit the verification and track its status on the **Regulatory Compliance** tab.
The form takes about five minutes when your documents are ready. Approval often takes one or two business days, but it can take longer if the provider needs more information.
### Purchase the number
Go to **Phone numbers** and select **New Number**.
Select the country and number type. Local numbers support voice. Available mobile numbers can also support SMS.
If required, select an approved business verification. For a German local number, enter the area code without the leading `0`, for example `30` for Berlin.
Select **Continue**, choose an available number, review the details, and confirm the purchase.
Open the new number. Assign the agent that answers incoming calls. For outgoing calls, use the global pool, reserve the number for one agent, or turn outbound use off.
Number availability and capabilities depend on the country and number type.
Check the **Voice** and **SMS** badges in the phone number list.
To show your verified business name on outbound calls from a telli-managed number, set up [Branded Calling](./branded-calling).
## Keep your current business number
A common setup is to keep your existing public number and forward incoming calls to a telli number. Your existing number stays with your current provider. Outgoing calls from your existing phone system are not changed.
Choose when telli should answer:
| Forwarding rule | Result |
| -------------------------- | -------------------------------------------------------- |
| **Always** | Every incoming call goes directly to the telli agent. |
| **No answer** | The telli agent answers when your team does not answer. |
| **Busy** | The telli agent answers when the line is busy. |
| **Outside business hours** | A schedule in your provider or PBX sends calls to telli. |
### Common mobile forwarding codes
Many mobile networks support the following GSM MMI codes. Replace `` with the full telli number, including the `+` and country code. Enter the code in the phone app and press the call button.
| Forwarding rule | Activate | Deactivate or delete |
| --------------- | --------------------- | -------------------- |
| **Always** | `**21*#` | `##21#` |
| **No answer** | `**61*#` | `##61#` |
| **Unreachable** | `**62*#` | `##62#` |
| **Busy** | `**67*#` | `##67#` |
Codes and their effects can differ by provider, plan, and device. Fixed-line
services and PBXs often use different codes. Some codes can also overwrite
or remove existing voicemail forwarding. Check your provider's instructions
before using them.
For provider examples, see the [Telekom mobile codes](https://www.telekom.de/hilfe/mobilfunk/telefonie-nachrichten/steuercodes) and [Vodafone mobile help](https://www.vodafone.de/hilfe/mobiles-telefonieren-surfen.html).
### Configure forwarding outside telli
Buy a telli number and assign an inbound agent. Copy the full number in [E.164 format](/phone-number-format), including the leading `+` and country code.
Create the forwarding rule in your provider portal, router, or PBX. For example, a FRITZ!Box usually places this under **Telephony > Call Handling > Call Diversion**. Menu names can differ by version.
Ask your provider or PBX administrator to pass the original caller number. This setting can be called **CLIP no screening**, **original caller ID**, or **P-Asserted-Identity**.
Call your existing number from another phone. For a no-answer rule, do not answer the original line. Confirm that the correct telli agent answers and that the call appears in **Conversations**.
Call forwarding is configured outside telli. Your provider can charge for the
forwarded call leg. Check the price, supported forwarding rules, caller ID
behavior, and maximum ring time with the provider.
### Provider checklist
Before you go live, confirm these items with your phone provider or PBX administrator:
* Which forwarding rules are supported: always, no answer, busy, and time based
* Whether the original caller ID reaches telli
* Whether voicemail or an IVR answers before the forwarding rule starts
* Whether forwarded calls have additional costs or concurrency limits
* How to disable the rule quickly and how to prevent forwarding loops
If no call appears in **Conversations**, the call did not reach telli. Check the forwarding rule with your provider. If the call appears but the wrong agent answers, check the inbound assignment on the telli number.
## Connect your existing number with SIP
Forwarding changes incoming calls only. A custom SIP trunk connects your existing number and provider to telli for incoming and outgoing calls.
Custom SIP trunks work alongside telli-provided numbers. You can use either
option or both.
### Configure both sides of the connection
To import a SIP number, configure both sides:
* In telli, add the phone number, provider termination URI, SIP credentials,
and transport protocol. These settings let telli place outgoing calls through
your provider.
* In your SIP provider, route incoming calls for that number to telli. Provider
field names differ, but the destination is always:
```text theme={null}
sip:axg8odfh9dw.sip.livekit.cloud
```
You need the following information in telli:
| Field | Description | Example |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| **Phone Number** | Your phone number in [E.164 format](/phone-number-format) | `+14155551234` |
| **Termination URI** | Provider hostname for outgoing calls | `pstn.provider.com` |
| **Auth Username** | SIP authentication username | `user123` |
| **Auth Password** | SIP authentication password | `securepass456` |
| **Transport Protocol** | SIP transport protocol. Supported values are `TCP`, `UDP`, and `TLS`. The default is `TCP`. | `TCP` |
| **From User** | Optional. What outgoing calls send as the SIP From user: `Phone number` or `Auth username`. Some providers, for example CallOne, reject calls unless the From user is the auth username. | `Phone number` |
| **From Host** | Optional. Host of the SIP From header for outgoing calls. Leave empty for the telli default. Required when the From user is the auth username. | `sip.provider.com` |
Do not include the `sip:` prefix in the Termination URI. Enter only the
hostname or IP address. The incoming destination in your SIP provider does
use the `sip:` prefix.
Choose the transport protocol that matches your provider's trunk settings. If your provider does not specify one, keep the default `TCP`.
If outgoing calls fail with `403 Forbidden - Invalid Domain`, set **From Host**
to the domain your provider expects, usually the termination host. If they fail
with `403 Username in From Field required`, set **From User** to `Auth username`.
Both fields are under **Optional Settings**.
[Cold transfers](/cookbooks/call-transfers/overview) also need your provider to
permit SIP REFER on the trunk. Most providers disable this by default, and some
split it across more than one setting. Warm transfers place a separate outgoing
call and work without it.
### Add a custom SIP trunk
Open **Phone Numbers** in your telli dashboard.
Select **Import Number**.
Enter your phone number, SIP credentials, and transport protocol.
Select **Import Number**, then test incoming and outgoing calls.
### Provider examples
Use these examples to map provider settings to the telli fields. Each setup includes incoming routing to telli and the outgoing termination settings that you enter in telli.
#### Twilio Elastic SIP Trunking
In the Twilio Console, create an Elastic SIP Trunk or open the trunk used by your imported number.
Open **Origination** and add `sip:axg8odfh9dw.sip.livekit.cloud` as the Origination URI.
Open **Termination** and note the Termination URI, for example `your-trunk.pstn.twilio.com` or `your-trunk.pstn.frankfurt.twilio.com`. Under **Authentication > Credentials**, create a username and password.
Make sure that your phone number is assigned to this trunk.
For cold transfers, open **General settings** and enable **Call Transfer (SIP REFER)** and the optional **Call Transfers to the PSTN via your Trunk**. Twilio disables both by default.
In telli, enter your phone number, the Twilio Termination URI without `sip:`, the Twilio credentials, and the transport protocol configured for your trunk.
#### Telnyx
In Telnyx, create a SIP connection and select the **FQDN** connection type.
Under **Authentication and Routing**, add `axg8odfh9dw.sip.livekit.cloud` as an FQDN and set **DNS Record Type** to **SRV**. Do not include the `sip:` prefix in this field.
Set both **Destination Number Format** and **Origination Number Format** to **+E.164**.
Configure credential-based authentication with a username and password.
Make sure that your phone number uses this Telnyx SIP connection.
In telli, enter your phone number, `sip.telnyx.com` as the Termination URI without `sip:`, the Telnyx credentials, and the transport protocol configured for your SIP connection.
Telnyx permits cold transfers by default. There is no call transfer setting to enable on the SIP connection.
#### CallOne
CallOne authenticates outgoing calls on the SIP From user and rejects INVITEs that carry the phone number there. The From host must be your CallOne SIP domain.
In the CallOne portal, route incoming calls for your number to `sip:axg8odfh9dw.sip.livekit.cloud`.
In telli, enter your phone number, your CallOne SIP domain as the Termination URI, for example `1234.voip.callone.de`, the SIP account username and password, and the transport protocol configured for your account.
Under **Optional Settings**, set **From User** to `Auth username` and **From Host** to the same domain as the Termination URI.
For country-specific regulatory requirements, see the [Twilio phone number guidelines](https://www.twilio.com/en-us/guidelines/regulatory).
# Settings
Source: https://docs.telli.com/platform/settings
Manage your telli account settings and preferences
## Profile Page
On the Profile page, you can manage your first and last name, view your email address, choose your display language, and configure your theme.
## Organization Page
On the Organization page, you can manage the organization name and logo and view the ID and concurrency limit. You can also configure unmatched inbound calls, manage compliance, call recordings, transcript saving, and data retention (automatic data deletion).
Call transcript settings and [data retention](/platform/data-retention) are only available on Enterprise plans.
## Team Page
On the Team page, you can search members, invite new members, change roles, and resend or cancel pending invitations.
## Billing Page
On the Billing page, you can view your plan, usage, and charges. Organization owners can manage subscriptions, payment methods, invoices, and Charlie or Workflow add-ons.
## Integrations Page
On the Integrations page, you can search and filter available integrations, then connect or manage CRM, calendar, messaging, and other services.
## Developer Page
On the Developer page, you can view or rotate your API key and configure call-event webhooks.
## Contact Properties Page
On the Contact Properties page, you can search, create, edit, and delete custom fields for contacts.
# SMS
Source: https://docs.telli.com/platform/sms
Send SMS messages from agents and workflows in telli
SMS lets you follow up with contacts from the same phone-number setup you use for calls. Use it when an agent needs to share a link during a conversation, or when a workflow should send an automatic follow-up after a call or contact event.
## Before you start
You need:
* A mobile SMS-capable phone number in telli. Check **Telephony** > **Phone Numbers** and look for the SMS capability badge.
* A contact with a valid phone number.
* Consent to message the recipient and a message that follows local messaging rules.
SMS to US recipients is not supported right now. Sends to US phone numbers are recorded as failed.
If you do not have an SMS-capable mobile number, buy a mobile number from [Phone Numbers](./phone-numbers). Imported numbers, local numbers, and voice-only numbers do not support SMS.
## Send SMS from an agent
Use an agent SMS tool when the agent should send a configured message during a live call, such as a booking link, payment link, or confirmation message.
Open the agent you want to edit, then go to the agent tools section in the builder sidebar.
Under **SMS**, click **Add SMS tool**.
Give the message a label, such as `booking_link` or `confirmation_sms`. Labels must start with a letter or underscore and can contain letters, numbers, and underscores.
Use **Use current phone call** when the SMS should come from the number used for the call. Select a fallback SMS-capable number in case the call number cannot send SMS. Use **Manually select number** when the message should always come from a specific number. Use **Use sender ID** to show an approved brand name instead of a number; see [Alphanumeric sender IDs](#alphanumeric-sender-ids).
Add the SMS body. You can insert available variables in the message so each send can include contact or call details.
Add the SMS tool reference to the prompt, for example `@sendSms:booking_link`, and explain when the agent should send it.
Fixed-template tools send the saved message with its variables. For generated SMS messages, **Message instructions** defines what the agent should write from the conversation, with a maximum of 1,000 characters. The recipient is always the caller. Charlie can configure this mode through the agent settings; you can then edit the instructions in the tool editor.
## Send SMS from a workflow
Use the **Send SMS** workflow action when telli should message a contact automatically.
Go to **Workflows**, then create a workflow or open an existing draft.
Select the event that should start the workflow. SMS works especially well after **Call ended** triggers.
Add a **Send SMS** action block after the trigger or after a condition.
Use the calling number with a fallback for call-based workflows, manually select an SMS-capable number, or use an approved [alphanumeric sender ID](#alphanumeric-sender-ids).
**To** defaults to the contact phone number. You can select another contact property of type **Phone number**, a collected-data field in a call-based workflow, or a fixed number. Recipients must resolve to valid [E.164 phone numbers](../phone-number-format#fields-that-require-strict-e164). The message remains associated with the workflow contact.
Write the body and insert workflow variables when you need contact fields, call outcomes, collected data, or values from earlier blocks.
Publish the workflow, then run it manually against a recent call before enabling automatic sends.
See [Workflows](./workflows) for the full workflow publishing and run-monitoring flow.
Outbound SMS cost 12 ct each.
## Alphanumeric sender IDs
Instead of a phone number, SMS can show your brand name as the sender, for example `telli`. Sender IDs are account-approved: request one under **Telephony** > **Verifications** > **SMS Sender IDs**, and telli reviews it before it can be used. You must be authorized to use the name; requests that impersonate third-party brands are rejected.
Once a sender ID is approved, pick **Use sender ID** as the **From** option in the agent SMS tool or the workflow **Send SMS** action.
Sender IDs behave differently from phone numbers:
* Recipients cannot reply to messages sent from a sender ID.
* Some carriers replace the sender ID with a phone number.
* Delivery is limited to the supported destination countries below. Sends to other destinations are recorded as failed. Countries where the sender ID would require carrier pre-registration are not supported.
| Country | Sender ID delivery |
| ---------------------------------------- | ------------------------------------- |
| Afghanistan | Not supported (registration required) |
| Albania | Supported |
| Algeria | Not supported (registration required) |
| American Samoa | Supported |
| Andorra | Supported |
| Angola | Supported |
| Anguilla | Supported |
| Antigua & Barbuda | Supported |
| Argentina | Supported |
| Armenia | Not supported (registration required) |
| Aruba | Supported |
| Australia | Not supported (registration required) |
| Austria | Not supported (registration required) |
| Azerbaijan | Supported |
| Bahamas | Not supported |
| Bahrain | Supported |
| Bangladesh | Not supported (registration required) |
| Barbados | Supported |
| Belarus | Not supported (registration required) |
| Belgium | Not supported |
| Belize | Supported |
| Benin | Not supported (registration required) |
| Bermuda | Supported |
| Bhutan | Supported |
| Bolivia | Supported |
| Bosnia and Herzegovina | Supported |
| Botswana | Supported |
| Brazil | Not supported (registration required) |
| Brunei | Supported |
| Bulgaria | Supported |
| Burkina Faso | Supported |
| Burundi | Supported |
| Cambodia | Not supported (registration required) |
| Cameroon | Not supported (registration required) |
| Canada | Not supported |
| Cape Verde | Supported |
| Cayman Islands (UK) | Not supported |
| Central African Republic | Supported |
| Chad | Supported |
| Chile | Not supported |
| China \* | Not supported |
| Colombia | Not supported |
| Comoros | Supported |
| Congo (Republic of the Congo) | Not supported (registration required) |
| Congo (Democratic Republic of the Congo) | Supported |
| Cook Islands | Supported |
| Costa Rica | Not supported |
| Croatia | Supported |
| Cuba | Not supported (registration required) |
| Cyprus | Supported |
| Czech Republic | Not supported (registration required) |
| Denmark | Supported |
| Djibouti | Supported |
| Dominica | Supported |
| Dominican Republic | Not supported |
| Ecuador | Not supported |
| Egypt | Not supported (registration required) |
| El Salvador | Not supported (registration required) |
| Equatorial Guinea | Supported |
| Estonia | Supported |
| Ethiopia | Not supported (registration required) |
| Falkland Islands | Supported |
| Faroe Islands | Supported |
| Fiji | Supported |
| Finland | Supported |
| France | Not supported (registration required) |
| French Guiana | Not supported |
| French Polynesia | Supported |
| Gabon | Supported |
| Gambia | Supported |
| Georgia | Supported |
| Germany | Supported |
| Ghana | Not supported (registration required) |
| Gibraltar | Supported |
| Greece | Supported |
| Greenland | Supported |
| Grenada | Supported |
| Guadeloupe & Martinique | Supported |
| Guam | Not supported |
| Guatemala | Not supported (registration required) |
| Guernsey | Supported |
| Guinea | Not supported (registration required) |
| Guinea-Bissau | Not supported (registration required) |
| Guyana | Supported |
| Haiti | Supported |
| Honduras | Not supported (registration required) |
| Hong Kong | Not supported (registration required) |
| Hungary | Not supported |
| Iceland | Supported |
| India | Not supported (registration required) |
| Indonesia | Not supported (registration required) |
| Iran | Not supported |
| Iraq | Supported |
| Ireland | Not supported (registration required) |
| Isle of Man | Supported |
| Israel | Supported |
| Italy | Supported |
| Ivory Coast (Côte d'Ivoire) | Not supported (registration required) |
| Jamaica | Supported |
| Japan | Supported |
| Jersey | Supported |
| Jordan | Not supported (registration required) |
| Kazakhstan | Not supported (registration required) |
| Kenya | Not supported (registration required) |
| Kosovo | Supported |
| Kuwait | Not supported (registration required) |
| Kyrgyzstan | Supported |
| Laos (Lao People's Democratic Republic) | Supported |
| Latvia | Supported |
| Lebanon | Supported |
| Lesotho | Supported |
| Liberia | Not supported (registration required) |
| Libya | Supported |
| Liechtenstein | Supported |
| Lithuania | Supported |
| Luxembourg | Supported |
| Macau (PRC) | Supported |
| Macedonia | Supported |
| Madagascar | Supported |
| Malawi | Supported |
| Malaysia | Not supported |
| Maldives | Supported |
| Mali | Supported |
| Malta | Supported |
| Martinique | Supported |
| Mauritania | Supported |
| Mauritius | Supported |
| Mayotte | Supported |
| Mexico | Not supported (registration required) |
| Moldova | Supported |
| Monaco | Supported |
| Mongolia | Supported |
| Montenegro | Supported |
| Montserrat | Supported |
| Morocco | Not supported (registration required) |
| Mozambique | Not supported (registration required) |
| Myanmar | Not supported (registration required) |
| Namibia | Supported |
| Nauru | Not supported |
| Nepal | Not supported (registration required) |
| Netherlands | Supported |
| Netherlands Antilles | Supported |
| New Caledonia | Supported |
| New Zealand | Not supported |
| Nicaragua | Not supported (registration required) |
| Niger | Supported |
| Nigeria | Not supported (registration required) |
| Norway | Supported |
| Oman | Not supported (registration required) |
| Pakistan | Supported |
| Palestine | Supported |
| Panama | Not supported |
| Papua New Guinea | Supported |
| Paraguay | Not supported |
| Peru | Supported |
| Philippines | Not supported (registration required) |
| Poland | Supported |
| Portugal | Supported |
| Puerto Rico | Not supported |
| Qatar | Not supported (registration required) |
| Réunion | Supported |
| Romania | Supported |
| Russia | Not supported (registration required) |
| Rwanda | Not supported (registration required) |
| Samoa | Supported |
| San Marino | Supported |
| Sao Tome and Principe | Supported |
| Saudi Arabia | Not supported (registration required) |
| Senegal | Supported |
| Serbia | Supported |
| Seychelles | Supported |
| Sierra Leone | Supported |
| Singapore | Not supported (registration required) |
| Slovakia | Supported |
| Slovenia | Supported |
| Solomon Islands | Supported |
| Somalia | Supported |
| South Africa | Not supported |
| South Korea | Not supported |
| South Sudan | Not supported (registration required) |
| Spain | Not supported (registration required) |
| Sri Lanka | Not supported (registration required) |
| St. Kitts and Nevis | Supported |
| St. Lucia | Supported |
| Saint Vincent and the Grenadines | Supported |
| Sudan | Not supported (registration required) |
| Suriname | Supported |
| Swaziland | Not supported (registration required) |
| Sweden | Supported |
| Switzerland | Supported |
| Syria | Not supported |
| Taiwan | Not supported |
| Tajikistan | Supported |
| Tanzania | Not supported (registration required) |
| Thailand | Not supported (registration required) |
| Timor-Leste (East Timor) | Not supported (registration required) |
| Togo | Supported |
| Tonga | Supported |
| Trinidad and Tobago | Supported |
| Tunisia | Supported |
| Turkey | Not supported (registration required) |
| Turkmenistan | Supported |
| Turks and Caicos Islands | Supported |
| Uganda | Not supported (registration required) |
| Ukraine | Supported |
| United Arab Emirates | Not supported (registration required) |
| United Kingdom | Supported |
| United States | Not supported |
| Uruguay | Not supported |
| Uzbekistan | Supported |
| Vanuatu | Supported |
| Venezuela | Not supported (registration required) |
| Vietnam | Not supported (registration required) |
| Virgin Islands (British Virgin Islands) | Supported |
| Yemen | Supported |
| Zambia | Not supported (registration required) |
| Zimbabwe | Not supported (registration required) |
Source: [Twilio — International support for Alphanumeric Sender ID](https://help.twilio.com/articles/223133767-International-support-for-Alphanumeric-Sender-ID), retrieved August 13, 2026. "Registration required" destinations count as not supported in telli.
## Review SMS activity
SMS conversations appear in **Conversations** alongside calls when SMS is enabled for your account. Open a conversation to see the message history and delivery state.
SMS also appears on the contact activity timeline, so you can review follow-ups next to the contact's calls and scheduled activity.
Common delivery states are:
| Status | Meaning |
| :------------ | :---------------------------------------------------------------------- |
| `queued` | telli sent the message to the SMS provider and it is waiting to be sent |
| `sent` | The provider accepted and sent the message |
| `delivered` | The carrier reported delivery to the recipient |
| `undelivered` | The carrier could not deliver the message |
| `failed` | The send failed before delivery |
Inbound replies from known phone numbers are added to the matching SMS conversation. Replies from unknown numbers create a new contact from the sender number.
## Limitations
* SMS supports plain text messages. Do not use it for media attachments.
* US-recipient SMS is currently blocked for compliance reasons.
* SMS requires a mobile phone number bought in telli with SMS capability.
* The message body cannot be empty.
# Workflows
Source: https://docs.telli.com/platform/workflows
Automate actions across calls, contacts, and integrations with visual workflows in telli
Workflows automate repeatable work across calls, contacts, and integrations. Start from a trigger, add actions and conditions in the visual builder, and keep contact records, CRMs, and external systems up to date automatically. The steps below use **Call ended** as the example trigger.
## Create a workflow
Go to **Workflows** in the telli dashboard. The list shows your workflows, run counts, triggers, statuses, and last update times.
Click **Create Workflow** to open the creation dialog.
Give the workflow a clear name and optional description. Use a name that describes the result, such as "Send call summary to CRM" or "Update lead status after call".
Choose the event that should start the workflow. For this guide, select **Call ended** to evaluate the workflow after a completed call.
Fill in the settings required by the trigger. For **Call ended**, select the agent and the call statuses that should start this workflow.
Use the **+** button on the canvas to add action and condition blocks after the trigger.
Click **Publish** when the trigger and required blocks are configured. Publishing creates the version telli can use for new workflow runs.
Turn the workflow **Enabled**. Disabled workflows keep their draft and published versions, but do not run automatically.
## Choose which ended calls start the workflow
The **Call ended** trigger uses your selected call statuses as an OR filter. An ended call starts the workflow once when it matches any selected status.
| Trigger option | Status available in conditions | Has follow-up |
| :--------------------------- | :----------------------------- | :------------ |
| **Connected → No follow-up** | **Connected** | `false` |
| **Connected → Follow-up** | **Connected** | `true` |
| **Not connected** | **Not connected** | `false` |
| **Voicemail** | **Voicemail** | `false` |
| **Failed** | **Failed** | `false` |
After selecting the statuses that may enter the workflow, use **Status** or **Has follow-up** in an **If / else** or **Switch** block to route them to different actions.
Unknown and unfinished calls do not start a call-ended workflow. Failed calls start it only when **Failed** is selected.
## Add workflow blocks
Blocks define what the workflow does after the trigger. Add them from the canvas, configure each block in the side panel, and use conditions when different call results should follow different paths.
Use a **Delay** block to pause before the next step. Set a total duration of at least 10 seconds. Combined delays along any workflow path cannot exceed 30 days. You can leave a delay unconfigured in a draft, but you must set a valid duration before publishing.
Action blocks perform work outside the workflow branch itself:
| Block | Use it to |
| :--------------------------- | :------------------------------------------------------------ |
| **HTTP request** | Send call, contact, and workflow data to an external endpoint |
| **Update contact** | Update contact fields or custom contact properties in telli |
| **Send SMS** | Send a text message to a selected phone number |
| **Send WhatsApp** | Send an approved WhatsApp template to a selected phone number |
| Salesforce **Create record** | Create a Salesforce record when Salesforce is connected |
| Salesforce **Update record** | Update a Salesforce record when Salesforce is connected |
| HubSpot **Create record** | Create a HubSpot record when HubSpot is connected |
| HubSpot **Update record** | Update a HubSpot record when HubSpot is connected |
Condition blocks decide which path the workflow should follow:
| Block | Use it to |
| :------------ | :-------------------------------------------------------------- |
| **If / else** | Split the workflow into a true branch and a false branch |
| **Switch** | Route the workflow through multiple branches based on one value |
Conditions are useful when only some calls should update a property, send a webhook, or sync to Salesforce. For example, compare **Status** with **Not connected** or **Voicemail**, or use **Has follow-up** to separate connected calls with a scheduled follow-up.
Workflow blocks can use values from the trigger and from earlier blocks in the same run.
Common sources include:
* Trigger data, such as the completed call for **Call ended**
* Contact fields and contact properties
* Call metadata such as status, follow-up state, direction, duration, and end reason
* The full plain-text call transcript through `call.transcript`
* The conversation link in the telli app through `call.conversationUrl`
* Collected data from the agent
* Call outcomes and analysis outputs
This lets you send structured context to external systems or update telli contact data based on what happened during the call.
In **Call ended** workflows, select **Call Metadata → Conversation link** or use `{{call.conversationUrl}}` in a text field to include a link to the call that triggered the workflow. To open the link, you must sign in to telli with access to that account.
`call.transcript` is available to **Call ended** workflows. It uses one `Agent:` or `User:` line per transcript entry and prefers the refined transcript when available. Calls processed while transcript storage is disabled have no transcript, so the variable resolves without a value. When transcript retention deletes a call transcript, telli removes it from stored workflow snapshots and traces, and stops active runs that depend on it. Data already sent to external systems is unaffected.
## Publish, enable, and edit
Workflow edits are saved as a draft while you work. Publishing turns the current draft into the version telli uses for new runs.
Use the draft while you are building or changing a workflow. If the builder highlights incomplete or broken blocks, configure those blocks before publishing.
A published version is the workflow version used for new automatic runs. Use **Version history** to preview previous versions.
Use **Discard draft** when you want to remove unpublished changes and return the draft to the latest published version.
Use the **Enabled** switch to control automatic execution. A workflow must be published before it can be enabled.
## Test and monitor runs
Run a workflow manually before relying on automatic execution. Manual runs use the published workflow and let you test it against a real completed call.
Open the workflow you want to test.
Select the **Runs** tab in the workflow builder.
Click **Run manually**. This action is available after the workflow is published and a trigger agent is selected.
Select one of the completed calls from the trigger agent.
Click **Run workflow** to create the run.
Open the run details to see which blocks ran, which branches were used, and where a failure happened if a block did not complete.
The **Runs** tab groups the history by your local day. The status icon shows whether the workflow completed, failed, or remains in progress. The first line names the trigger event, then shows the **Manual** or **Triggered** origin and exact local time. When snapshot context is available, the second line identifies the contact or call with the saved contact name, phone number or email, plus the call duration.
The **All time** metrics include every retained run, including runs not loaded in the current list. The history loads up to 50 runs at a time. Scroll down or click **Load more** to fetch older runs, then open any row to review its full trace.
When a workflow reaches a delay, run details show the completed blocks and mark the delay **In progress**. Progress updates at the next delay or when the run finishes.
## Common examples
Use **Call ended** with an **HTTP request** block when another system needs call details after a completed conversation.
This is useful for CRMs, data warehouses, or automation platforms that should receive call summaries, contact fields, call outcomes, or collected data.
To send a recording link, select **Call Metadata → Recording URL** in a body field, header, or query parameter.
Use **Update contact** with call outcomes or collected data to keep contact records current.
For example, route qualified leads through an **If / else** branch and update a custom property such as "Lead Status" or "Interested Product".
Use **Send SMS** when a completed call should trigger a text follow-up, such as a booking link, payment link, reminder, or confirmation.
Choose an SMS-capable sender number and select **To**: the contact phone number, another contact property with the phone number type, collected data, or a fixed phone number. Use workflow variables to personalize the message. See [SMS](./sms) for setup requirements and delivery status details.
Use **Send WhatsApp** when a completed call should trigger a WhatsApp template message, such as an appointment reminder or confirmation.
Choose an active WhatsApp Business phone number and a recipient, select an approved Meta template, and map template fields to workflow data. See [WhatsApp Business](../integrations/whatsapp) for setup requirements and delivery status details.
When Salesforce is connected, use Salesforce blocks to create or update records with data from the completed call.
A common pattern is to use call outcomes to decide what should be written back. For example, update a Salesforce field when an outcome such as "Appointment Booked" or "Qualified Lead" is true, and use conditions when only certain calls should sync to Salesforce.
When HubSpot is connected, use HubSpot blocks to create or update records with data from the completed call.
As with Salesforce, use call outcomes and conditions to decide what should be written back to HubSpot. See [HubSpot](../integrations/hubspot) for connection setup and write-back details.
Use a Salesforce **Create record** block with type `Task` to log a completed telli call back into Salesforce as an activity.
Set these fields:
| Salesforce field | Value |
| :--------------- | :-------------------------------------------------------------------- |
| `Task Subtype` | `Call` |
| `Type` | `Call` |
| `Status` | `Completed` |
| `Name ID` | `Contact -> External ID` |
| `Subject` | A short title, such as `telli call` or the call outcome |
| `Call Type` | `Inbound` or `Outbound` |
| `Description` | Call summary, outcome, call ID, recording link, or transcript details |
`Name ID` links the Salesforce task to the synced Lead or Contact. Use `Contact -> External ID`, which stores the Salesforce record ID for contacts synced from Salesforce. For contacts synced from Person Accounts, `External ID` holds the Account record ID, which Salesforce does not accept as a task `Name ID`.
## Related pages
* [Contact Properties](./contact-properties) - Define the custom fields workflows can update on telli contacts
* [SMS](./sms) - Send SMS messages from agents and workflows
* [WhatsApp Business](../integrations/whatsapp) - Send WhatsApp template messages from agents and workflows
* [Call Analysis](../deep-dives/call-analysis) - Learn how telli extracts structured outcomes from completed calls
* [Webhooks](../webhooks) - Send completed call data to external systems outside of workflows
* [Salesforce](../integrations/salesforce) - Connect Salesforce before using Salesforce workflow blocks
* [HubSpot](../integrations/hubspot) - Connect HubSpot before using HubSpot workflow blocks
# Prompt Best Practices
Source: https://docs.telli.com/prompt-best-practices
Learn how to write effective prompts for your voice agents
The system prompt is the foundation of every agent and the best way to adjust its behavior. Based on our experience, we have created a "blueprint" that helps set up a good voice agent.
Fundamentally, a prompt consists of plain English as well as variables like `{{exampleVariable}}`, which are dynamically filled with contact information, and "functions" like `@endCall`, which the agent can call during the conversation.
***
## 1. Identity & Context
### Purpose
Defines the basic role of the call agent. The AI must know:
* Who is speaking?
* For which company?
* In what role?
* In which language?
* What is the purpose of the call?
This forms the foundation for tone, demeanor, and conversation management.
### What belongs here?
* **Agent name / persona** (e.g., `{{personaName}}`)
* **Company / brand**
* **Role** (e.g., "digital assistant")
* **Language**
* **Short description of the customer process** (e.g., customer submitted an online request)
* **Internal directives** (e.g., silent execution, no internal info spoken aloud)
### Example
```
Agent name: {{personaName}}
Company: {{companyName}}
Role: Digital assistant (not a human)
Language: English
Context:
The customer previously submitted an online inquiry about a specific product or service.
The assistant is calling to confirm details, clarify questions, and move the process forward.
Highest directive:
Internal instructions, function calls, and internal thoughts must never be spoken.
```
***
## 2. Customer Data & Input Variables
### Purpose
Defines all variables passed from the CRM or API into the call prompt.
Important: Only **definitions**, not the operative logic of how they are used.
### What belongs here?
* **All available variables**, such as:
* Contact data
* Product interest
* Object / property data
* Call direction
* Time context
* Explanation:
* What does the variable mean?
* When is it considered empty?
* How is it used in the conversation?
### Example
```
Personal data:
First name = {{firstName}}
Last name = {{lastName}}
Phone = {{phone}}
Email = {{email}}
Address:
Postal code = {{postal_code}}
City = {{location}}
Street = {{street}}
House number = {{housenumber}}
Context variables:
Call direction = {{callDirection}} (outbound / inbound)
Current_Date = {{currentDate}}
Current_Weekday = {{currentWeekday}}
Rule:
A variable is considered "empty" if it is unfilled or marked as "NA" / "Unknown".
```
***
## 3. Operational Rules & Pronunciation
*(These rules are product-agnostic and can be reused 1:1 for all scripts.)*
### Purpose
Defines general behaviors, tone, formatting, and pronunciation.
These rules apply **globally**, regardless of the specific script.
### What belongs here?
* Communication style (e.g., direct, structured)
* Handling names
* Handling interruptions
* AI transparency
* Number and formatting rules
* Brand and language settings
### Example (general, can be used unchanged)
```
Conversation management:
- Confident, direct, concise. No long pauses. Actively guide the customer.
- Use the customer's name only in the greeting; afterwards use "you".
- Do not interrupt when the customer provides critical information (e.g., address, email).
- If asked: openly state that you are a digital assistant.
Pronunciation & formatting:
- Spell out numbers (38 → "thirty-eight").
- Postal codes digit by digit ("nine zero two one zero").
- Phone numbers also digit by digit.
- Email: Spell letters before the @ ("J-O-H-N…"), speak the domain normally.
- Dates written out ("fifteenth of February twenty twenty-five").
Ending the call:
- Never end the call unilaterally.
- First say goodbye, wait for a reaction, then terminate the call.
```
***
## 4. Conversation Guide (Script Structure)
### Purpose
The central section.
This contains **the actual flow** of how an outbound call is conducted.
The script is always:
* **Numbered**
* **Linear**
* **Clearly structured**
* **With defined IF/ELSE sections**
* **Without unnecessary variants**
### What belongs here?
* Greeting & reference to the inquiry
* Qualification questions
* Product / property questions
* Timeframe questions
* Data gathering / confirmation (without technical validation details)
* Closing communication
* Farewell
### Example (general outline)
```
1. Greeting
- Short introduction
- Reference to the inquiry
2. Qualification
- Ownership/responsibility question
- Ask relevant key details (e.g., consumption, property type)
3. Additional detail questions
- e.g., roof type, year built, contract status – depending on product
4. Timeframe
- When could the project be implemented?
5. Data verification
- Confirm or request contact data and address
6. Closing
- Briefly explain next steps (e.g., callback by a partner)
- Get customer confirmation
7. Farewell
- Friendly closing phrase
- "Goodbye"
```
***
## 5. Objection Handling
### Purpose
Defines how typical objections are answered.
This section is **always separate**, so the main script stays clean.
### What belongs here?
* Thematic categories:
* Time/availability
* Interest
* Process questions
* Identity/person
* Technical uncertainties
* Other cases
* For each objection:
* **Trigger** ("I don't have time right now")
* **Standard response** (short & word-for-word)
* Note: Return to the script afterward
### Example
```
Time & availability:
"Can you call again later?"
-> "This will be really quick — we'd be done in two minutes."
Interest:
"This topic isn't relevant to me right now."
-> "No problem. Would you still like to get some non-binding information so you have an overview?"
Process:
"I don't want to be called, I prefer to call myself."
-> "Of course, you will also receive all the information by email. If that works for you, simply get in touch whenever it suits you."
```
***
## 6. Instructions: How to Write a Good Script
### Purpose
Provides clear rules for creating new call scripts.
### 6.1 Numbering is mandatory
A good script is **fully numbered**:
* 1
* 2
* 3
* …
With sub-points (2.1, 2.2) if needed.
The AI follows linear structures much more reliably.
### 6.2 Keep the main section as simple as possible
* Short, clear sentences
* No variants, no synonyms
* No unnecessary safety phrasing
* Objection handling **not** in the main flow
* Only ask absolutely necessary questions
### 6.3 Separate logic clearly
* Variables → Section 2
* Global rules → Section 3
* Flow → Section 4
* Objections → Section 5
### 6.4 Building blocks instead of continuous text
Every sentence must be written so the AI can say it **exactly as-is**.
### 6.5 Examples help the AI
Every section should include **at least one example** showing how something should be structured.
***
## Template Structure
Here's the recommended structure for a complete agent prompt:
```
# 1. Identity & Context
[Agent name, company, role, language, context]
# 2. Customer Data & Input Variables
[All variables with definitions]
# 3. Operational Rules & Pronunciation
[Communication style, handling rules, formatting]
# 4. Conversation Guide
[Numbered script structure]
# 5. Objection Handling
[Category-organized objections with responses]
```
***
## Best Practices Summary
1. **Structure is key** - Use numbered sections consistently
2. **Separation of concerns** - Keep variables, rules, flow, and objections in separate sections
3. **Be explicit** - The AI will follow what you write literally
4. **Use examples** - Show the AI what good looks like
5. **Keep it simple** - Avoid unnecessary complexity in the main flow
6. **Test iteratively** - Refine based on actual agent behavior
# Add Contact
Source: https://docs.telli.com/v1/endpoint/add-contact
openapi.json POST /v1/add-contact
Adds a new contact to the system
This endpoint is deprecated. Use [Create Contact](/v2/endpoint/create-contact) instead. See the [Migration Guide](/v2-migration-guide) for details.
# Add Contacts (Batch)
Source: https://docs.telli.com/v1/endpoint/add-contacts-batch
openapi.json POST /v1/add-contacts-batch
Adds multiple contacts in a single request. Limited to 1000 contacts per request.
This endpoint is deprecated. A V2 replacement is not yet available. See the [Migration Guide](/v2-migration-guide) for details.
# Delete Contact
Source: https://docs.telli.com/v1/endpoint/delete-contact
openapi.json DELETE /v1/delete-contact/{contact_id}
Permanently deletes a contact and all personal data telli stores for it, including the recordings, transcripts, and analyses of its calls. This action cannot be undone. If you want to stop calling a contact, use the `v1/remove-from-auto-dialer` endpoint instead.
This endpoint is deprecated. Use [Delete Contact](/v2/endpoint/delete-contact) instead. See the [Migration Guide](/v2-migration-guide) for details.
This endpoint runs the same deletion process as the V2 endpoint. See [Deleting Contacts and Personal Data](/platform/contacts#deleting-contacts-and-personal-data) for what is deleted and what remains.
# Delete Phone Number
Source: https://docs.telli.com/v1/endpoint/delete-phone-number
openapi.json DELETE /v1/phone-numbers/{id}
Schedules a phone number for deletion. The number remains active for callbacks for 30 days and can be restored during this period.
# Get Call
Source: https://docs.telli.com/v1/endpoint/get-call
openapi.json GET /v1/get-call/{id}
Retrieves detailed information about a specific call by its ID, including call metadata, transcript, analysis, and the associated contact.
## Call status mapping
Do not use `call_status` for new integrations. It is deprecated and returned only for backward compatibility. Use `state`, `status`, and `follow_up` instead.
| `call_status` | `state` | `status` | `follow_up` |
| ------------- | ------------- | --------------- | ----------- |
| `INITIATED` | `queued` | `null` | `null` |
| `RINGING` | `ringing` | `null` | `null` |
| `IN_PROGRESS` | `in_progress` | `null` | `null` |
| `IN_PROGRESS` | `processing` | `null` | `null` |
| `COMPLETED` | `ended` | `connected` | `null` |
| `ANSWERED` | `ended` | `connected` | set |
| `NOT_REACHED` | `ended` | `not_connected` | `null` |
| `VOICEMAIL` | `ended` | `voicemail` | `null` |
| `ERROR` | `ended` | `failed` | `null` |
# Get Contact
Source: https://docs.telli.com/v1/endpoint/get-contact
openapi.json GET /v1/get-contact/{contactId}
Retrieves detailed contact information including current call status and history
This endpoint is deprecated. Use [Get Contact](/v2/endpoint/get-contact) instead. See the [Migration Guide](/v2-migration-guide) for details.
# Get Contact by External ID
Source: https://docs.telli.com/v1/endpoint/get-contact-by-external-id
openapi.json GET /v1/get-contact-by-external-id/{external_contact_id}
Retrieves detailed contact information including current call status and history using external contact id as an identifier
This endpoint is deprecated. Use [Get Contact by External ID](/v2/endpoint/get-contact-by-external-id) instead. See the [Migration Guide](/v2-migration-guide) for details.
# Get Contacts (Batch)
Source: https://docs.telli.com/v1/endpoint/get-contacts-batch
openapi.json POST /v1/get-contacts-batch
Gets multiple contacts in a single request. Limited to 1000 contacts per request.
This endpoint is deprecated. Use [List Contacts](/v2/endpoint/list-contacts) for paginated retrieval instead. See the [Migration Guide](/v2-migration-guide) for details.
# Import Phone Number
Source: https://docs.telli.com/v1/endpoint/import-phone-number
openapi.json POST /v1/phone-numbers/import
Imports a phone number from your own SIP trunk provider. This allows you to use existing phone numbers with telli by configuring custom SIP termination settings.
# Initiate Call
Source: https://docs.telli.com/v1/endpoint/initiate-call
openapi.json POST /v1/initiate-call
[Not recommended] Initiates an immediate call to a contact. Will call even outside business hours, which is why we recommend using schedule-call instead. If contact cannot be reached and Auto Dialer is enabled, telli will continue trying according to Auto Dialer settings.
# List Calls
Source: https://docs.telli.com/v1/endpoint/list-calls
openapi.json GET /v1/list-calls
Returns a paginated list of calls for the authenticated account, ordered by `triggered_at` descending. Optionally filter by `contact_id` to return only calls for a single contact, or by `agent_id` to return only calls for a single agent. `contact_id` and `agent_id` cannot be used together. Each item is a lean call payload: the response does not include a separate top-level `contact` object, and the `external_contact_id` and `contact_details` fields are omitted from each call (unlike [Get Call](/v1/endpoint/get-call)). Use the Contact endpoints if you need contact details.
## Call status mapping
Do not use `call_status` for new integrations. It is deprecated and returned only for backward compatibility. Use `state`, `status`, and `follow_up` instead.
| `call_status` | `state` | `status` | `follow_up` |
| -------------- | ------------- | --------------- | -------------- |
| `INITIATED` | `queued` | `null` | `null` |
| `RINGING` | `ringing` | `null` | `null` |
| `IN_PROGRESS` | `in_progress` | `null` | `null` |
| Not applicable | `processing` | `null` | Not applicable |
| `COMPLETED` | `ended` | `connected` | `null` |
| `ANSWERED` | `ended` | `connected` | set |
| `NOT_REACHED` | `ended` | `not_connected` | `null` |
| `VOICEMAIL` | `ended` | `voicemail` | `null` |
| `ERROR` | `ended` | `failed` | `null` |
# List Phone Numbers
Source: https://docs.telli.com/v1/endpoint/list-phone-numbers
openapi.json GET /v1/phone-numbers
Retrieves all active phone numbers associated with the account
# Remove from Auto Dialer
Source: https://docs.telli.com/v1/endpoint/remove-from-dialer
openapi.json POST /v1/remove-from-auto-dialer
Removes a contact from the auto dialer queue
# Remove from Auto Dialer (Batch)
Source: https://docs.telli.com/v1/endpoint/remove-from-dialer-batch
openapi.json POST /v1/remove-from-auto-dialer-batch
Removes multiple contacts from the auto dialer queue. Limited to 50 contacts per request.
# Replace Phone Number
Source: https://docs.telli.com/v1/endpoint/replace-phone-number
openapi.json POST /v1/phone-numbers/{id}/replace
Replaces an existing phone number with a new one with the same configuration. The old number is scheduled for deletion but remains active for callbacks for 30 days.
# Schedule Call
Source: https://docs.telli.com/v1/endpoint/schedule-call
openapi.json POST /v1/schedule-call
Schedules a call at the earliest opportunity within the dialer window. May occur immediately or on the next business day depending on settings. Requires the auto dialer to be enabled.
# Schedule Calls (Batch)
Source: https://docs.telli.com/v1/endpoint/schedule-calls-batch
openapi.json POST /v1/schedule-calls-batch
Schedules multiple calls in a single request. Limited to 50 contacts per request.
# Update Contact
Source: https://docs.telli.com/v1/endpoint/update-contact
openapi.json PATCH /v1/update-contact
Updates an existing contact. Only provided fields will be updated, others remain unchanged.
This endpoint is deprecated. Use [Update Contact](/v2/endpoint/update-contact) instead. See the [Migration Guide](/v2-migration-guide) for details.
# Update Contacts (Batch)
Source: https://docs.telli.com/v1/endpoint/update-contacts-batch
openapi.json PATCH /v1/update-contacts-batch
Updates multiple contacts in a single request. Limited to 1000 contacts per request. Only provided fields will be updated, others remain unchanged.
This endpoint is deprecated. A V2 replacement is not yet available. See the [Migration Guide](/v2-migration-guide) for details.
# Migration Guide
Source: https://docs.telli.com/v2-migration-guide
Migrate from the deprecated contact endpoints to the current API for contacts and contact properties
The telli V2 API is a new RESTful API for managing contacts and contact properties. It coexists alongside the V1 API — both remain fully operational. This guide walks you through the differences and how to migrate your integration.
## Prerequisites
* A telli account with API access
* An API key from your telli dashboard (Settings > Developer)
## What's changed
The V2 API introduces several improvements over V1:
* **Typed contact properties** — Structured, validated properties replace untyped dynamic variables (see [Contact Properties](/platform/contact-properties) for background)
* **Cursor-based pagination** — Efficient pagination for listing contacts
* **Structured errors** — Consistent error responses with HTTP status codes and error codes
### Authentication
Authentication is unchanged. Use the same API key with the same `Authorization` header:
```bash theme={null}
Authorization: Bearer
```
The base URL is the same — V2 endpoints are available under the `/v2/` prefix.
***
## Endpoint mapping
| V1 Endpoint | V2 Endpoint | Notes |
| ------------------------------------------------ | ---------------------------------------- | ---------------------------------------- |
| `POST /v1/add-contact` | `POST /v2/contacts` | Returns `201` with full contact object |
| `GET /v1/get-contact/:contactId` | `GET /v2/contacts/{id}` | Returns typed response with `properties` |
| `GET /v1/get-contact-by-external-id/:externalId` | `GET /v2/external/contacts/{externalId}` | Same behavior, new path |
| `PATCH /v1/update-contact` | `PATCH /v2/contacts/{id}` | Contact ID moves from body to URL path |
| `DELETE /v1/delete-contact/:contact_id` | `DELETE /v2/contacts/{id}` | Returns `204` (no body) |
| — | `GET /v2/contacts` | **New.** Cursor-paginated contact list |
| `POST /v1/add-contacts-batch` | — | Not yet available in V2 |
| `PATCH /v1/update-contacts-batch` | — | Not yet available in V2 |
| `POST /v1/get-contacts-batch` | — | Not yet available in V2 |
***
## Field mapping
Request and response fields have moved from snake\_case to camelCase:
| V1 Field | V2 Field | Notes |
| ----------------------- | ---------------------- | ------------------------------------------------------------------------------------- |
| `external_contact_id` | `externalId` | |
| `external_url` | `externalUrl` | |
| `first_name` | `firstName` | |
| `last_name` | `lastName` | |
| `phone_number` | `phoneNumber` | |
| `email` | `email` | Unchanged |
| `salutation` | `salutation` | Unchanged |
| `timezone` | `timezoneIana` | Renamed for clarity |
| `dynamic_variables` | `properties` | See [Migrating dynamic variables](#migrating-dynamic-variables-to-contact-properties) |
| `contact_details` | `properties` | See [Migrating dynamic variables](#migrating-dynamic-variables-to-contact-properties) |
| `contact_id` (response) | `id` (response) | |
| `created_at` (response) | `createdAt` (response) | ISO 8601 datetime string |
***
## Response format
V2 responses include a `type` field and return enriched contact property data.
```json V1 Response theme={null}
{
"contact_id": "b3f1a2c4-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
"external_contact_id": "crm-123",
"external_url": null,
"first_name": "Max",
"last_name": "Mustermann",
"phone_number": "+4915112345678",
"email": "max@example.com",
"salutation": null,
"timezone": "Europe/Berlin",
"contact_details": {
"appointment_date": "2026-03-15",
"interest_level": "high"
},
"created_at": "2026-02-06T10:00:00.000Z",
"status": "new",
"call_attempts": 0,
"next_call_at": null,
"in_call_since": null,
"reached_at": null
}
```
```json V2 Response theme={null}
{
"id": "b3f1a2c4-5d6e-7f8a-9b0c-1d2e3f4a5b6c",
"type": "Contact",
"externalId": "crm-123",
"externalUrl": null,
"firstName": "Max",
"lastName": "Mustermann",
"phoneNumber": "+4915112345678",
"email": "max@example.com",
"salutation": null,
"timezoneIana": "Europe/Berlin",
"properties": [
{
"key": "appointment_date",
"value": "2026-03-15",
"dataType": "date",
"label": "Appointment Date"
},
{
"key": "interest_level",
"value": "high",
"dataType": "select",
"label": "Interest Level",
"options": [
{ "value": "low", "label": "Low" },
{ "value": "medium", "label": "Medium" },
{ "value": "high", "label": "High" }
]
}
],
"createdAt": "2026-02-06T10:00:00.000Z",
"updatedAt": "2026-02-06T10:00:00.000Z"
}
```
Key differences in the response:
* `contact_id` is now `id`
* `contact_details` (flat key-value object) is now `properties` (array of typed, enriched objects)
* Each property in the response includes its `dataType`, `label`, and `options` (for select types)
* The `type` field identifies the resource type (`"Contact"`)
* Call-related fields (`status`, `call_attempts`, `next_call_at`, `in_call_since`, `reached_at`) are not part of the V2 contacts response
***
## Migrating dynamic variables to contact properties
This is the most significant change between V1 and V2. In V1, you could attach arbitrary key-value data to contacts via `dynamic_variables` or `contact_details` without any prior setup. In V2, you first define a **property schema** with a human-readable key and then use that key when setting values.
For a full overview of what contact properties are and how to manage them in the telli UI, see [Contact Properties](/platform/contact-properties).
### How it works
1. **Define a property** — Create a property definition with a key, data type, label, and optional constraints via the API (or through the telli UI)
2. **Choose a property key** — You define a human-readable, URL-safe key (e.g., `appointment_date`) when creating the property
3. **Use the key on contacts** — When creating or updating contacts, pass properties as `[{key, value}]` pairs using your chosen keys
### Example: before and after
Suppose you were storing these dynamic variables on contacts in V1:
```json theme={null}
{
"dynamic_variables": {
"appointment_date": "2026-03-15",
"interest_level": "high",
"notes": "Interested in premium plan"
}
}
```
To migrate to V2, first define each as a typed property:
```bash theme={null}
# Create a date property for appointment dates
curl -X POST https://api.telli.com/v2/properties/contacts \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"key": "appointment_date",
"dataType": "date",
"label": "Appointment Date"
}'
# Response: { "key": "appointment_date", "dataType": "date", ... }
# Create a select property for interest level
curl -X POST https://api.telli.com/v2/properties/contacts \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"key": "interest_level",
"dataType": "select",
"label": "Interest Level",
"options": [
{ "value": "low", "label": "Low" },
{ "value": "medium", "label": "Medium" },
{ "value": "high", "label": "High" }
]
}'
# Response: { "key": "interest_level", "dataType": "select", ... }
# Create a text property for notes
curl -X POST https://api.telli.com/v2/properties/contacts \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"key": "notes",
"dataType": "string",
"label": "Notes"
}'
# Response: { "key": "notes", "dataType": "string", ... }
```
Then use those keys when creating or updating contacts:
```json theme={null}
{
"properties": [
{ "key": "appointment_date", "value": "2026-03-15" },
{ "key": "interest_level", "value": "high" },
{ "key": "notes", "value": "Interested in premium plan" }
]
}
```
### Available property types
| API Data Type | Description | Example Value |
| -------------- | --------------------------------------------------------------------------------- | ------------------------ |
| `string` | Free-text string | `"Enterprise"` |
| `number` | Numeric value | `42` |
| `boolean` | True or false | `true` |
| `date` | Calendar date (YYYY-MM-DD) | `"2026-03-15"` |
| `datetime` | Date with time (ISO 8601) | `"2026-03-15T14:30:00Z"` |
| `select` | Single choice from predefined options | `"gold"` |
| `multi_select` | Multiple choices from predefined options | `["german", "english"]` |
| `phone_number` | Phone number in [strict E.164 format](/phone-number-format), leading `+` required | `"+4915112345678"` |
| `email` | Email address | `"sarah@example.com"` |
### System properties
Some contact fields are represented as **system properties** in V2. These are always present and cannot be modified or deleted through the properties API:
| Key | Data Type | Label |
| -------------- | ------------- | ------------ |
| `externalId` | string | External ID |
| `firstName` | string | First Name |
| `lastName` | string | Last Name |
| `phoneNumber` | phone\_number | Phone Number |
| `email` | email | Email |
| `timezoneIana` | string | Timezone |
| `externalUrl` | string | External URL |
System properties are set directly as top-level fields on the contact (e.g., `firstName`, `email`), not through the `properties` array.
***
## Operation-by-operation migration
### Create a contact
```bash V1 theme={null}
curl -X POST https://api.telli.com/v1/add-contact \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"external_contact_id": "crm-123",
"first_name": "Max",
"last_name": "Mustermann",
"phone_number": "+4915112345678",
"email": "max@example.com",
"timezone": "Europe/Berlin",
"dynamic_variables": {
"appointment_date": "2026-03-15",
"interest_level": "high"
}
}'
```
```bash V2 theme={null}
curl -X POST https://api.telli.com/v2/contacts \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"externalId": "crm-123",
"firstName": "Max",
"lastName": "Mustermann",
"phoneNumber": "+4915112345678",
"email": "max@example.com",
"timezoneIana": "Europe/Berlin",
"properties": [
{ "key": "appointment_date", "value": "2026-03-15" },
{ "key": "interest_level", "value": "high" }
]
}'
```
**V1** returns `{ "contact_id": "..." }`. **V2** returns `201` with the full contact object including enriched properties.
### Get a contact by ID
```bash V1 theme={null}
curl https://api.telli.com/v1/get-contact/ \
-H "Authorization: Bearer "
```
```bash V2 theme={null}
curl https://api.telli.com/v2/contacts/ \
-H "Authorization: Bearer "
```
### Get a contact by external ID
```bash V1 theme={null}
curl https://api.telli.com/v1/get-contact-by-external-id/crm-123 \
-H "Authorization: Bearer "
```
```bash V2 theme={null}
curl https://api.telli.com/v2/external/contacts/crm-123 \
-H "Authorization: Bearer "
```
### List contacts
This endpoint is **new in V2** — there is no V1 equivalent. It returns a paginated list of contacts using cursor-based pagination.
```bash theme={null}
curl "https://api.telli.com/v2/contacts?limit=10" \
-H "Authorization: Bearer "
```
Response:
```json theme={null}
{
"type": "ContactCollection",
"data": [
{ "id": "...", "type": "Contact", "firstName": "Max", ... }
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJjcmVhdGVkQXQiOi..."
},
"meta": {
"limit": 10,
"count": 10,
"total": 142
}
}
```
To fetch the next page, pass the `endCursor` value as the `cursor` query parameter:
```bash theme={null}
curl "https://api.telli.com/v2/contacts?limit=10&cursor=eyJjcmVhdGVkQXQiOi..." \
-H "Authorization: Bearer "
```
### Update a contact
In V1, you pass the `contact_id` in the request body. In V2, the contact ID is part of the URL path.
```bash V1 theme={null}
curl -X PATCH https://api.telli.com/v1/update-contact \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"contact_id": "",
"first_name": "Maximilian",
"dynamic_variables": {
"interest_level": "medium"
}
}'
```
```bash V2 theme={null}
curl -X PATCH https://api.telli.com/v2/contacts/ \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"firstName": "Maximilian",
"properties": [
{ "key": "interest_level", "value": "medium" }
]
}'
```
**Important difference in update behavior:**
* **V1** replaces the entire `dynamic_variables` object. If you only send `{"interest_level": "medium"}`, all other dynamic variables are lost.
* **V2** merges properties. Only the keys you include are updated — all other existing properties are preserved. To clear a property, set its value to `null`.
### Delete a contact
```bash V1 theme={null}
curl -X DELETE https://api.telli.com/v1/delete-contact/ \
-H "Authorization: Bearer "
```
```bash V2 theme={null}
curl -X DELETE https://api.telli.com/v2/contacts/ \
-H "Authorization: Bearer "
```
**V1** returns `{ "message": "Contact deleted successfully", "contact_id": "..." }`. **V2** returns `204 No Content` with an empty response body.
***
## Contact Properties API
V2 introduces a dedicated API for managing property definitions. You only need to create property definitions once per account — they then apply to all contacts.
For managing properties through the telli UI instead, see [Contact Properties](/platform/contact-properties).
### List all property definitions
```bash theme={null}
curl https://api.telli.com/v2/properties/contacts \
-H "Authorization: Bearer "
```
Returns both system properties and your custom properties:
```json theme={null}
{
"type": "ContactPropertyList",
"data": [
{
"type": "ContactProperty",
"key": "firstName",
"dataType": "string",
"source": "system",
"label": "First Name",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
},
{
"type": "ContactProperty",
"key": "appointment_date",
"dataType": "date",
"source": "user",
"label": "Appointment Date",
"createdAt": "2026-02-06T10:00:00.000Z",
"updatedAt": "2026-02-06T10:00:00.000Z"
}
]
}
```
### Get a single property definition
```bash theme={null}
curl https://api.telli.com/v2/properties/contacts/appointment_date \
-H "Authorization: Bearer "
```
### Create a property definition
```bash theme={null}
curl -X POST https://api.telli.com/v2/properties/contacts \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"key": "customer_tier",
"dataType": "select",
"label": "Customer Tier",
"options": [
{ "value": "free", "label": "Free" },
{ "value": "pro", "label": "Pro" },
{ "value": "enterprise", "label": "Enterprise" }
]
}'
```
### Update a property definition
You can update the label, description, and add new options (for select types). You cannot change the data type or remove existing options.
```bash theme={null}
curl -X PATCH https://api.telli.com/v2/properties/contacts/appointment_date \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"label": "Appointment Date (Updated)",
"description": "The next scheduled appointment date"
}'
```
***
## Key differences to be aware of
1. **Properties require prior definition** — You must create a property definition before you can use it on contacts. Unknown property keys are rejected with a `422` validation error.
2. **Property keys are user-defined** — You choose a human-readable, URL-safe key (e.g., `appointment_date`) when creating a property. Keys must be unique within your account.
3. **Update merges properties** — In V2, updating a contact's properties only affects the keys you include. Existing properties are preserved. Set a value to `null` to clear a specific property. In V1, sending `dynamic_variables` replaced the entire object.
4. **Values are validated** — V2 validates property values against their data type and constraints (e.g., a `date` property rejects `"not-a-date"`, a `select` property rejects values not in the options list). V1 accepted any value.
5. **Batch endpoints are not yet available in V2** — If you rely on batch operations (`add-contacts-batch`, `update-contacts-batch`, `get-contacts-batch`), continue using the V1 endpoints for now.
6. **V2 returns enriched properties** — GET responses include the `dataType`, `label`, and `options` for each property value, so you don't need a separate lookup to interpret property data.
# Create Contact
Source: https://docs.telli.com/v2/endpoint/create-contact
openapi-v2.json POST /v2/contacts
Creates a new contact. Properties are validated against the account's property schema.
# Create Contact Property
Source: https://docs.telli.com/v2/endpoint/create-contact-property
openapi-v2.json POST /v2/properties/contacts
Creates a new contact property for the authenticated account.
# Delete Contact
Source: https://docs.telli.com/v2/endpoint/delete-contact
openapi-v2.json DELETE /v2/contacts/{id}
Deletes a contact and all personal data telli stores for it, including the recordings, transcripts, and analyses of its calls. The deletion cannot be undone.
Deleting a contact removes all personal data telli stores about the person, including the recordings, transcripts, and analyses of its calls. See [Deleting Contacts and Personal Data](/platform/contacts#deleting-contacts-and-personal-data) for what is deleted and what remains.
# Get Agent
Source: https://docs.telli.com/v2/endpoint/get-agent
openapi-v2.json GET /v2/agents/{id}
Returns a single agent by ID.
# Get Contact
Source: https://docs.telli.com/v2/endpoint/get-contact
openapi-v2.json GET /v2/contacts/{id}
Returns a single contact by ID.
# Get Contact by External ID
Source: https://docs.telli.com/v2/endpoint/get-contact-by-external-id
openapi-v2.json GET /v2/external/contacts/{externalId}
Returns a single contact by its external ID.
# Get Contact Property
Source: https://docs.telli.com/v2/endpoint/get-contact-property
openapi-v2.json GET /v2/properties/contacts/{key}
Returns a single contact property by key.
# List Agents
Source: https://docs.telli.com/v2/endpoint/list-agents
openapi-v2.json GET /v2/agents
Returns a paginated list of agents for the authenticated account.
# List Contact Properties
Source: https://docs.telli.com/v2/endpoint/list-contact-properties
openapi-v2.json GET /v2/properties/contacts
Returns all contact properties for the authenticated account.
# List Contacts
Source: https://docs.telli.com/v2/endpoint/list-contacts
openapi-v2.json GET /v2/contacts
Returns a paginated list of contacts for the authenticated account.
# Update Contact
Source: https://docs.telli.com/v2/endpoint/update-contact
openapi-v2.json PATCH /v2/contacts/{id}
Updates an existing contact. Properties are merged with existing ones after validation.
# Update Contact Property
Source: https://docs.telli.com/v2/endpoint/update-contact-property
openapi-v2.json PATCH /v2/properties/contacts/{key}
Updates an existing contact property by key.
# Replace Contact
Source: https://docs.telli.com/v2/endpoint/update-contact-put
openapi-v2.json PUT /v2/contacts/{id}
Fully replaces an existing contact. All omitted optional fields are reset to their default values (null). Properties are replaced entirely rather than merged.
# Webhooks
Source: https://docs.telli.com/webhooks
Receive call outcomes, transcripts, and analysis in your own systems as soon as they happen
Webhooks push data from telli to your systems in real time. When something happens on a call, telli sends a POST request to the endpoint you configure, so you can update CRM records, qualify leads, and trigger follow-ups without polling the API.
One endpoint can receive every event type, so most integrations need only a single URL.
## Events
| Event | Sent when |
| --------------------------------------------------------------------------- | -------------------------------------------------------------- |
| [`call_ended`](/webhooks/events/call-ended) | A call reaches a terminal state and analysis has run |
| [`call_rescheduled`](/webhooks/events/call-rescheduled) | The scheduler assigns or updates a call loop's later call time |
| [`auto_dialer_status_changed`](/webhooks/events/auto-dialer-status-changed) | A contact enters or exits the auto dialer |
| [`contact_status_changed`](/webhooks/events/contact-status-changed) | A contact's status changes. Deprecated, removed July 1, 2027 |
Each event page documents its full payload, field by field.
## Prerequisites
* A telli account with API access
* An endpoint URL that accepts POST requests, either from your own service or from an automation platform such as [Zapier](/integrations/zapier), [Make](/integrations/make), or [n8n](/integrations/n8n)
## Add an endpoint
In your own service or automation platform, create a URL that accepts POST requests and copy it.
In telli, go to **Settings > Developer** and click **Configure** under **Webhook configuration**.
Click **Add Endpoint**, paste your URL, and click **Add**.
Enable the events this endpoint should receive. Leaving all of them enabled is fine — your handler can branch on the `event` field.
Place a test call from your telli account, then confirm the message was delivered in the webhook portal and that your system reacted as expected.
## Acknowledge messages
Return any 2xx status code (200–299) within 15 seconds to mark a message as processed. Anything else — an error status, a timeout, a dropped connection — counts as a failure and the message is retried.
If your processing takes longer than 15 seconds, acknowledge the message first and do the work asynchronously.
Disable CSRF protection on your endpoint, otherwise webhook POST requests are rejected before your handler runs.
## Verify signatures
Every message is signed so you can confirm it came from telli and not from someone else posting to your URL. Verification is optional but recommended in production. See Svix's explanation of [why you should verify webhooks](https://docs.svix.com/receiving/verifying-payloads/why).
Your signing secret is in the webhook portal, under **Webhook configuration** in the telli dashboard.
```javascript theme={null}
import { Webhook } from "svix";
// Copy this from Webhook configuration in the telli dashboard
const secret = "whsec_GET_THIS_FROM_THE_DASHBOARD";
// These headers arrive with every webhook message
const headers = {
"svix-id": "msg_p5jXN8AQM9LWM0D4loKWxJek",
"svix-timestamp": "1614265330",
"svix-signature": "v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=",
};
// The raw, unparsed request body
const body = '{"test": 2432232314}';
const wh = new Webhook(secret);
// Throws on failure, returns the verified payload on success
const payload = wh.verify(body, headers);
```
Svix's [verification documentation](https://docs.svix.com/receiving/verifying-payloads/how) has equivalent examples for Python, Go, Java, PHP, and other languages.
## Retries
Failed deliveries are retried automatically with exponential backoff. Each delay starts after the preceding attempt fails:
| Attempt | Delay after previous failure |
| ------- | ---------------------------- |
| 1 | Immediately |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 5 hours |
| 7 | 10 hours |
| 8 | 10 hours |
A message that fails three times before succeeding arrives roughly 35 minutes and 5 seconds after the first attempt. If an endpoint is removed or disabled, its pending delivery attempts stop.
You can also retry any message manually from the webhook portal, or use **Recover** to replay all failed messages from a given date.
# Auto dialer status changed
Source: https://docs.telli.com/webhooks/events/auto-dialer-status-changed
openapi-v2.json webhook auto_dialer_status_changed
Payload telli sends when a contact enters or exits the auto dialer
Use this event to track whether telli is still trying to reach a contact. It is the supported replacement for [`contact_status_changed`](/webhooks/events/contact-status-changed).
## Check the current status
The contact APIs return the current auto-dialer enrollment state, while this webhook reports a change to that state:
| Surface | Status field |
| -------------- | -------------------- |
| V2 Contact API | `autoDialerStatus` |
| V1 Contact API | `auto_dialer_status` |
| This webhook | `auto_dialer.status` |
All three fields use `in_dialer` and `not_in_dialer`. The API fields are current snapshots. Webhook events additionally include `auto_dialer.updated_at` and, when a contact exits the dialer, `auto_dialer.reason`.
## Exit reasons
`auto_dialer.reason` is present only when `auto_dialer.status` is `not_in_dialer`:
| Reason | Meaning |
| ------------------------------ | ---------------------------------------------------------------------------- |
| `last_call_was_connected` | The last call connected, so no further attempts are needed |
| `scheduler_gave_up_interval` | The interval dialing plan ran out of attempts |
| `scheduler_gave_up_smart` | The smart dialing plan reached its stop condition |
| `call_transferred` | The inbound callback was transferred |
| `manually_removed_from_dialer` | Someone removed the contact from the dialer |
| `no_next_opportunity` | No valid future time to call was found |
| `sip_error` | The phone network returned an error we do not retry |
| `call_failed` | The call still failed after an automatic retry |
| `dialing_unavailable` | The contact, account, outbound number, or auto dialer is no longer available |
`last_call_was_connected` and `call_transferred` are success cases; the rest mean telli stopped without reaching the contact.
When an inbound callback is forwarded, telli completes the active dialing loop for the same contact and agent, including a newly scheduled loop. Its pending outbound call is removed. The contact's next-call time is recalculated from any remaining unfinished loops. Use the auto-dialer status to determine whether the contact is still enrolled; a next-call timestamp alone does not confirm enrollment.
See [Auto dialer](/deep-dives/auto-dialer) for how dialing plans decide when to stop.
# Call ended
Source: https://docs.telli.com/webhooks/events/call-ended
openapi-v2.json webhook call_ended
Payload telli sends when a call reaches a terminal state
## Call outcome
Three fields describe what happened, and they are the ones to build on:
* `state` — where the call is in its lifecycle
* `status` — how it ended, once it has ended
* `follow_up` — the follow-up telli scheduled, if any
`call_status` is deprecated and will be removed. Use `state`, `status`, and `follow_up` instead.
The legacy field maps onto the new ones like this:
| `call_status` | `state` | `status` | `follow_up` |
| ------------- | ------------- | --------------- | ----------- |
| `INITIATED` | `queued` | `null` | `null` |
| `RINGING` | `ringing` | `null` | `null` |
| `IN_PROGRESS` | `in_progress` | `null` | `null` |
| `IN_PROGRESS` | `processing` | `null` | `null` |
| `COMPLETED` | `ended` | `connected` | `null` |
| `ANSWERED` | `ended` | `connected` | set |
| `NOT_REACHED` | `ended` | `not_connected` | `null` |
| `VOICEMAIL` | `ended` | `voicemail` | `null` |
| `ERROR` | `ended` | `failed` | `null` |
`call_status` is lossy in both directions: `IN_PROGRESS` cannot tell `in_progress` from `processing`, and `COMPLETED` and `ANSWERED` differ only by whether a follow-up was scheduled.
## Analysis fields
The payload carries two kinds of analysis, and **their shapes differ**.
`call_analysis` holds telli's built-in analysis. Every entry has a boolean `value`; entries that support supporting detail also carry `details`:
```json theme={null}
{
"appointment": {
"value": true,
"details": "2025-02-18T15:30:00Z"
}
}
```
`call_outcome` holds the [custom analysis fields](/deep-dives/call-analysis) you configure in telli. Entries are keyed by field name and carry the extracted `value` plus the schema it was validated against:
```json theme={null}
{
"custom_lost_reason": {
"value": "PRODUCT_TOO_EXPENSIVE",
"fieldSchema": {
"type": ["string", "null"],
"enum": ["CUSTOMER_NOT_INTERESTED", "CUSTOMER_PREVIOUSLY_CONTACTED", "PRODUCT_TOO_EXPENSIVE"]
}
}
}
```
A `call_outcome` entry may also include `reason`, explaining why the agent chose that value, and `error` when extraction failed.
## Collected data
When the agent has [Collect Data](/deep-dives/collected-data) tasks, `collected_data` reports what was gathered and confirmed during the call:
```json theme={null}
{
"email": {
"status": "confirmed",
"value": "user@example.com"
},
"delivery_address": {
"status": "confirmed",
"value": "Rosenthaler Straße 51, 10178 Berlin, Germany",
"latitude": 52.5253059,
"longitude": 13.4043936
},
"case_number": {
"status": "declined",
"value": null
}
}
```
Only `confirmed` entries carry a value you should trust. Address entries also include `latitude` and `longitude` when address validation produced coordinates; other task types omit these fields. When no Collect Data tasks are configured or none were triggered, `collected_data` is empty or `null` — handle both.
## Appointments
`call.appointments` contains bookings recorded during the call, including their start time (`starts_at`, in UTC), status, hosts, and provider identifiers. It is empty when none were recorded. Failed booking attempts are excluded.
`booked` describes booking creation, not customer confirmation. `pending` means booking completion or provider confirmation is still required; `unknown` means the integration reported no status. Later cancellations or rescheduling at the provider are not reflected.
`call.booked_slot_for` is deprecated and remains available for compatibility. Use `call.appointments` for booking details. Older calls can have `call.booked_slot_for` set while `call.appointments` is empty.
## Recording
`recording_url` links to the audio of the call, or is `null` when recording is disabled or unavailable. The link expires 1 hour after the webhook is sent, so download the recording right away if you need to keep it.
## Retry attempts
Automatic retries within one scheduled sequence share a `loop_id`, and `attempt` counts up within it. Scheduling the same contact again starts a new loop, so use `contact_id` to track a contact across sequences.
# Call rescheduled
Source: https://docs.telli.com/webhooks/events/call-rescheduled
openapi-v2.json webhook call_rescheduled
Receive the later call time selected by the scheduler
The `call_rescheduled` event is sent whenever the scheduler decides not to place a call immediately and persists a later time instead. This includes the first scheduling decision for a newly created call loop as well as later changes to an existing schedule.
The payload identifies the assigned agent and includes the contact as it stood after the new schedule was persisted. `call.next_call_at` and `contact.next_call_at` contain the selected ISO 8601 timestamp. `call.to_number` is the contact's phone number.
# Contact status changed
Source: https://docs.telli.com/webhooks/events/contact-status-changed
openapi-v2.json webhook contact_status_changed
Deprecated payload telli sends when a contact's status changes
This event is deprecated and will be removed on **July 1, 2027**. Use [`auto_dialer_status_changed`](/webhooks/events/auto-dialer-status-changed) to track whether telli is still trying to reach a contact, and why it stopped.
## Contact statuses
| Status | Meaning |
| --------- | -------------------------------------------------------------------------------- |
| `new` | The contact has not been called yet |
| `pending` | The contact is in the dialer and telli is trying to reach them |
| `closed` | The dialer was exhausted; no further attempts will be made |
| `reached` | The contact was reached and had a conversation; no further attempts will be made |
## Migrating
`auto_dialer_status_changed` covers the same ground with more detail. `pending` corresponds to `in_dialer`, while `closed` and `reached` both correspond to `not_in_dialer` — and the [exit reason](/webhooks/events/auto-dialer-status-changed#exit-reasons) tells you which of the two happened, plus why.
# Auto-dialer & Calling Strategy
Source: https://docs.telli.com/cookbooks/auto-dialer/overview
Reach out to contacts automatically. Pick a strategy, retry intensity, and dialer windows that fit your campaign.
The auto-dialer is telli's built-in system for automatically calling contacts until they're reached. Once you add a contact to the dialer, telli will keep trying, spacing calls intelligently, respecting calling hours, and stopping when the person answers or when your configured limits are hit.
Think of it as a persistent, polite assistant that never forgets to follow up.
***
## When to Enable It
You enable the auto-dialer **per agent** in the Agent Builder under **Call Control → Auto-Dialer**. Flip the toggle on, and every contact assigned to that agent enters the calling loop automatically.
* Repeatedly calls contacts who haven't been reached yet
* Respects your dialer windows (calling hours) and timezone settings
* Tracks every attempt and adjusts timing based on your chosen strategy
* Stops automatically when the contact is reached or limits are exceeded
* It doesn't retry inbound calls. If the first interaction was inbound, the dialer won't retry
* It doesn't call contacts who are already on an active call
* It doesn't override "call me later" requests from contacts. It respects those
* It doesn't replace your agent's conversational logic. It only controls *when* and *how often* calls happen
***
## Smart Calling vs. Defined Intervals
You have two dialing strategies to choose from:
The system automatically spaces out call attempts based on how many days have passed. Early on, it calls more frequently. Over time, it backs off gradually. No manual configuration of intervals needed.
**When to use:**
* General outreach, sales - it adapts automatically
* Long-running nurture campaigns - with Conservative intensity
You specify exact wait times between attempts. For example, "retry after 20 minutes, then 60 minutes, then 150 minutes." The number of intervals you define is the total number of retry attempts. Once the list is exhausted, the system stops.
**When to use:**
* Urgent, time-sensitive callbacks - precise control over timing
* Short-burst campaigns (same day) - with tight spacing
***
## Smart Calling Intensity
Smart Calling has three intensity modes that control how aggressively the system follows up:
**Best for: hot leads, time-sensitive outreach** where reaching the contact quickly matters most.
* **Day 1:** Up to **3 attempts**, roughly 1 hour apart
* **Days 2–10:** **2 calls per day** (morning + afternoon, or afternoon + evening)
* **Day 11+:** **1 call per day**
* Never drops to weekly
**Best for: general-purpose outreach** - a balanced approach without being pushy.
* **Day 1:** Up to **3 attempts**
* **Days 2–3:** **2 calls per day**
* **Days 4–10:** **1 call per day**
* **Day 11+:** **1 call per week** (picks a random weekday)
**Best for: nurture campaigns, follow-ups, or sensitive contacts** where you want to stay in touch without overwhelming them.
* **Day 1:** Up to **2 attempts**
* **Days 2–3:** **1 call per day**
* **Day 4+:** **1 call per week**
Within each phase, the system randomizes the exact call time within your dialer window so calls don't all land at the same minute.
***
## Max Retry Days and Max Attempts
These are your **stop conditions** - safety nets that prevent the dialer from calling forever. You must set at least one (and can set both):
* **Max retry days** - The system stops trying after this many calendar days since the first attempt. Default is **7 days**. Range: 0–365.
* **Max attempts** - The system stops after this many total call attempts. Range: 1–100.
**When both are set, whichever limit is hit first wins.** For example, "stop after 14 days or 15 attempts, whichever comes first."
For **Defined Intervals**, the max attempts is implicitly the number of intervals you've defined - no separate setting needed.
***
## Dialer Windows
Each weekday gets its own schedule:
* **Enabled/disabled** toggle - disable a day entirely (e.g., no calls on weekends)
* **Time window** - start and end time (e.g., `09:00` to `18:00`)
**Default:** Monday–Friday, 9:00 AM to 6:00 PM. Weekends disabled.
You can set dialer windows at two levels:
1. **Account level** (Settings → Dialer) - applies to all agents by default
2. **Agent level** (Agent Builder → Call Control) - overrides the account default for that specific agent
### No-Call Days
You can also add **no-call dates** - specific days where no calls should go out (holidays, company events, etc.). These are set at the account level with an optional label (e.g., "Christmas Day").
### How Timezone Overrides Work
**The dialer windows are interpreted in the contact's timezone, not yours.**
The system resolves the timezone in this order:
1. **Contact's timezone** - if set and valid, this is used
2. **Account's timezone** - fallback when the contact has no timezone set
So if your account is in Berlin (`Europe/Berlin`) and a contact is in New York (`America/New_York`), the 9:00–18:00 window means 9 AM to 6 PM *New York time* for that contact. This ensures you're never calling someone at 3 AM their time.
If a scheduled call falls outside the window, the system automatically pushes it to the next available slot with a small random buffer (up to 15 minutes) so all your calls don't fire at exactly 9:00 AM.
***
## What Counts as "Reached"
This is the single most important concept: **A contact is "reached" only when the call status is `COMPLETED`** - meaning they answered, had an actual conversation with the agent, and the call ended normally.
Everything else triggers a retry:
| Status | What Happens |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Completed** | Contact reached. Loop ends. Contact status → `reached`. |
| **Voicemail** | Not reached. System retries. (Your agent can optionally leave a voicemail message.) |
| **Not Reached** | Phone rang, no answer. System retries. |
| **Answered** | Contact answered but asked to be called back later ("call me later"). System schedules a callback. |
| **Error** | Technical issue. System retries - but gives up after **2 consecutive errors** to avoid hammering a broken number. |
| **Ringing / Initiated** | Call started but didn't connect. System retries. |
### Permanent Failures (No Retry)
Certain SIP error codes cause the system to **immediately give up** on a contact - these indicate the number itself is invalid:
* **404** - Number not found
* **482** - Loop detected
* **483** - Too many hops
* **484** - Address incomplete
***
## Manual Calls and the Call Loop
You can trigger a call manually at any time from the UI or API. Here's how it interacts with the auto-dialer:
* The manual call fires immediately, **bypassing the dialer window**
* The existing call loop is updated (agent, call details, etc.)
* After the manual call, the loop continues as normal based on the outcome. If the contact wasn't reached, the dialer picks back up with its regular schedule
* A new call loop is created
* The call fires immediately
* If the auto-dialer is active for that agent, retries will follow the configured strategy
In short: manual calls never conflict with the dialer. They slot right in.
***
## Common Patterns
**Respond fast, follow up aggressively**
* **Strategy:** Smart Calling
* **Intensity:** Aggressive
* **Max retry days:** 3–5
* **Max attempts:** 10–15
* **Why:** You want to reach them while interest is high. Multiple calls on day 1, twice daily for the next week.
**Balanced persistence**
* **Strategy:** Smart Calling
* **Intensity:** Moderate (default)
* **Max retry days:** 7–14
* **Max attempts:** 10–20
* **Why:** Balanced approach. Persistent but not annoying. Good for appointment confirmations, onboarding calls.
**Stay in touch over weeks**
* **Strategy:** Smart Calling
* **Intensity:** Conservative
* **Max retry days:** 30–60
* **Max attempts:** 8–12
* **Why:** Light touch over a long period. One call per week after the first few days. Great for re-engagement or long sales cycles.
**Time-sensitive callbacks**
* **Strategy:** Defined Intervals
* **Intervals:** `[15, 30, 60, 120]` (in minutes)
* **Why:** You need to reach them today. Four attempts over \~3.5 hours, then stop. No multi-day follow-up.
**Scheduled touchpoints**
* **Strategy:** Defined Intervals
* **Intervals:** `[1440, 60]` (24 hours, then 1 hour before)
* **Why:** Call the day before, then an hour before. Two precise touchpoints.
***
## Good to Know
* When you change dialer settings on an agent or account (strategy, intensity, windows), the system automatically recalculates the schedule for all active loops. You don't need to restart anything - changes take effect within seconds.
* Only **one active call loop per contact** is allowed at any time.
* The dialer scheduler runs every **10 seconds**, so there's minimal delay between a scheduled time and the actual call.
* Account-level changes to dialer windows apply to **all agents** that use the account default - a confirmation dialog reminds you of this in the UI.
# Connect your calendar
Source: https://docs.telli.com/cookbooks/calendar-integration/overview
Connect a calendar provider so your telli agent can check availability and book appointments during calls.
When you connect a calendar, your voice AI agent can:
* **Check availability** See open time slots in real-time during calls
* **Book appointments** Schedule meetings without any manual intervention
* **Collect information** Gather booking details from callers or your CRM
Each agent can have its own calendar connection, so different workflows can book different meeting types or route to different teams.
telli supports five main calendar integrations:
| **Provider** | **Best For** |
| ----------------------- | -------------------------------------------------------------- |
| **Calendly** | Teams already using Calendly with complex booking forms |
| **Zeeg** | Teams using Zeeg scheduling pages and custom invitee questions |
| **Cal.com** | Teams using Cal.com for scheduling |
| **HubSpot Meetings** | HubSpot users who book through HubSpot |
| **Custom Calendar API** | Companies with their own scheduling system |
***
## Getting Started
1. Log in to the telli app
2. Navigate to **Agents** in the sidebar
3. Select the agent you want to configure
4. Scroll to the **Calendar Integration** section
Click the **Select Integration Type** dropdown to see available options:
* **Calendly** For Calendly users
* **Zeeg** For Zeeg users
* **Cal.com** For Cal.com users
* **HubSpot** For HubSpot Meetings users
* **Generic Calendar** For custom API connections
***
## Provider Setup
**When to Use**
* Your team already uses Calendly
* You have custom booking questions on your event types
* You want to automatically populate form fields from caller data
* Different agents should book different meeting types
### Fields Required
| **Field** | **Description** |
| ------------------ | ------------------------------------------------------ |
| **API Key** | Your Calendly API key |
| **Event Type URI** | The Calendly event type to use (e.g., `johndoe/30min`) |
| **External URL** | (Optional) Your Calendly booking page URL |
| **Booking Fields** | Mappings for custom questions on your event type |
### Booking Fields
You can map how each question on your Calendly form gets answered:
| **Source Type** | **How It Works** | **When to Use** |
| -------------------- | --------------------------------------------------- | --------------------------------------------------- |
| **Constant** | Fixed value every time | Company name, internal notes, team name |
| **Contact Property** | Pulled from the caller's contact record in your CRM | Email, phone, company, first/last name |
| **System Variable** | Resolved from internal data | Contact ID, timezone, account tier |
| **LLM Parameter** | Agent asks the caller during the call | Meeting topic, preferred language, special requests |
Every required custom question on your Calendly event type must have a mapping configured, or the booking will fail. Optional questions without a mapping are simply skipped.
### Setup Steps
### Finding Your Event Type URI
Your Calendly event link looks like:
```text theme={null}
https://calendly.com/johndoe/30min-call
```
The URI is: `johndoe/30min-call`
**When to Use**
* Your team already uses Zeeg
* You want to book against existing Zeeg scheduling pages
* You have custom invitee questions on your scheduling pages
* Different agents should book different meeting types
### Fields Required
| **Field** | **Description** |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| **API Key** | Your Zeeg API token (created in [Account Settings → API](https://app.zeeg.me/account/settings/api-access)) |
| **Scheduling Page** | The Zeeg scheduling page the agent should use for availability and booking |
| **Duration** | The meeting length, picked from the durations your scheduling page exposes |
| **Location** | Where the meeting happens, picked from the supported locations on your scheduling page |
| **Booking Fields** | Mappings for the custom invitee questions on your scheduling page |
### Booking Fields
You can map how each custom invitee question on your Zeeg scheduling page gets answered:
| **Source Type** | **How It Works** | **When to Use** |
| -------------------- | --------------------------------------------------- | --------------------------------------------------- |
| **Constant** | Fixed value every time | Company name, internal notes, team name |
| **Contact Property** | Pulled from the caller's contact record in your CRM | Email, phone, company, first/last name |
| **System Variable** | Resolved from internal data | Contact ID, timezone, account tier |
| **LLM Parameter** | Agent asks the caller during the call | Meeting topic, preferred language, special requests |
Every required custom question on your Zeeg scheduling page must have a mapping configured, or the booking will fail. Optional questions without a mapping are simply skipped.
Zeeg's availability and booking API endpoints require an active paid Zeeg subscription.
### Setup Steps
### Creating Your Zeeg API Token
1. Sign in to Zeeg and open [Account Settings → API](https://app.zeeg.me/account/settings/api-access)
2. Create a new token with these scopes:
* `events:read` read scheduling pages and their custom invitee questions
* `timetable` read available time slots for the selected scheduling page
* `booking` create the booked Zeeg event during the call
3. Copy the token and paste it into the API Key field in telli
**When to Use**
* Your team uses Cal.com for scheduling
* You need agent-specific routing (different agents → different event types)
* You want the flexibility of Cal.com's open-source platform
### Fields Required
| **Field** | **Description** |
| -------------- | ----------------------------- |
| **API Key** | Your Cal.com API key |
| **Event Type** | The Cal.com event type to use |
### Setup Steps
### Finding Your Event Type
Your Cal.com event link looks like:
```text theme={null}
https://cal.com/johndoe/30min
```
The event type is: `30min`
**When to Use**
* Your team uses HubSpot for sales and scheduling
* You want bookings to automatically create HubSpot records
* You need meetings tied directly to your HubSpot CRM
### Fields Required
| **Field** | **Description** |
| --------------------- | -------------------------------------- |
| **Access Token** | Your HubSpot private app access token |
| **Meeting Link Slug** | The slug from your HubSpot meeting URL |
| **Meeting Duration** | Duration in minutes (e.g., `30`) |
### Setup Steps
### Finding Your Meeting Link Slug
1. Go to HubSpot's [Meetings Scheduler](https://meetings.hubspot.com/)
2. Open the meeting you want to use
3. Copy the meeting URL it looks like:
```text theme={null}
https://meetings.hubspot.com/johndoe/30min-call
```
4. The **slug** is everything after `meetings.hubspot.com/`: `johndoe/30min-call`
### Creating a HubSpot Private App
1. In HubSpot, go to **Settings** → **Integrations** → **Private Apps**
2. Click **Create a private app**
3. Give it a name (e.g., `telli Integration`)
4. Go to the **Scopes** tab
5. Add these scopes:
* `crm.objects.contacts.write`
* `crm.schemas.contacts.write`
* `scheduler.meetings.meeting-link.read`
* `tickets`
6. Click **Create**
7. Copy the access token (shown once save it securely)
8. Use this token in telli
**When to Use**
* You have your own scheduling system
* You need full control over the booking logic
* You're not using any of the supported third-party tools
* You want to integrate with an internal booking system
### Fields Required
| **Field** | **Description** |
| ------------------------ | ---------------------------------------------------- |
| **Available Slots URL** | Your API endpoint to fetch available appointments |
| **Book Appointment URL** | (Optional) Your API endpoint to book a selected slot |
### Setup Steps
### Implementation Notes
* Use **UTC timestamps** in ISO 8601 format
* Return **HTTP 200** responses with success/failure in the body
* Make sure your endpoints are **publicly reachable** by telli
* The `start_iso` field is used as the slot identifier for bookings
#### Get Available Slots
**Request:**
```json theme={null}
POST https://your-api.com/available
{
"contact_id": "telli contact identifier",
"external_contact_id": "your internal contact ID",
"contact_details": {
"email": "caller@example.com",
"name": "John Doe"
}
}
```
**Response:**
```json theme={null}
{
"available": [
{
"start_iso": "2024-01-02T14:00:00.000Z",
"end_iso": "2024-01-02T14:30:00.000Z"
},
{
"start_iso": "2024-01-02T15:00:00.000Z",
"end_iso": "2024-01-02T15:30:00.000Z"
}
]
}
```
#### Book Appointment
**Request:**
```json theme={null}
POST https://your-api.com/book
{
"contact_id": "telli contact identifier",
"external_contact_id": "your internal contact ID",
"start_iso": "2024-01-02T14:00:00.000Z",
"contact_details": {
"email": "caller@example.com",
"name": "John Doe"
}
}
```
**Success Response:**
```json theme={null}
{
"status": "success"
}
```
**Failure Response:**
```json theme={null}
{
"status": "failed",
"reason": "Appointment slot is no longer available"
}
```
Requests from telli include a signature header you can verify:
```javascript theme={null}
const crypto = require("crypto");
function verifyRequest(payload, signature, apiKey) {
const expectedSignature = crypto
.createHmac("sha256", apiKey)
.update(JSON.stringify(payload))
.digest("hex");
return signature === expectedSignature;
}
```
Check the `x-telli-signature` header on incoming requests.
***
Each agent can have its own calendar connection. This is powerful for:
* Different teams with different calendars
* Different sales motions (demo calls vs. discovery vs. support)
* Routing to specific reps or departments
* A/B testing different scheduling flows
Simply configure the calendar integration separately for each agent.
Once connected, your agent can handle scheduling conversations like this:
* **Caller:** "I'd like to schedule a demo"
* **Agent:** "Let me check availability" → calls your calendar
* **Agent:** "I have Tuesday at 2pm or Wednesday at 10am which works?"
* **Caller:** "Tuesday please"
* **Agent:** Books the slot and confirms the appointment
The entire interaction is hands-free.
| **Issue** | **Solution** |
| ------------------------ | ------------------------------------------------------------ |
| Booking fails | Check that all required booking fields are mapped (Calendly) |
| No available slots shown | Verify your API credentials and endpoints are correct |
| HubSpot booking fails | Ensure your private app has all required scopes |
| Authentication errors | For custom APIs, verify your signature verification logic |
# Call Outcomes
Source: https://docs.telli.com/cookbooks/call-outcomes/overview
Automatically extract structured information from every call your telli agent handles.
Call outcomes let you **automatically extract structured information from every call** your telli agent handles. After each call ends, telli analyzes the full conversation transcript and pulls out the specific data points you've defined. No manual work required.
## Quick Start
Navigate to your agent and scroll to the **Custom Call Outcomes** section
Create your first outcome
* **Name:** Short label (e.g., "Appointment Booked")
* **Type:** Boolean, Text, Number, Category, or Multi Category
* **Instructions:** Clear description of what to extract from the transcript
Your agent will now extract this outcome from every new call
***
## Add call outcomes with Charlie
You don't have to set outcomes up by hand. [Charlie](/cookbooks/charlie/overview), the AI assistant in the Agent Builder, can create and refine them for you. Describe what you'd like to track and Charlie sets it up in your agent's draft for you to review.
* Add a new outcome (yes/no, number, text, category, or multi-category)
* Refine the instruction so the extraction picks up the right cases
* Turn on the "reason" toggle so each value comes with a short explanation
* Turn a yes/no outcome into a dashboard metric
***
Think of call outcomes as giving telli a checklist of questions to answer about every call:
* *"Did the caller book an appointment?"* → **Yes / No**
* *"What was the caller interested in?"* → **Free text answer**
* *"How satisfied was the caller?"* → **Positive / Neutral / Negative**
* *"How many units did the caller want to order?"* → **A number**
* *"Which topics did the caller mention?"* → **One or more options from your list**
These answers are automatically extracted and available on every call record, ready to filter, export, and act on.
* **Review calls at a glance:** Open any call and immediately see the key takeaways on the Outcome tab without listening to the recording
* **Filter your call list:** Find all calls where an appointment was booked or a specific product was mentioned
* **Trigger email notifications:** Set up conditional email alerts using boolean, category, or multi-category outcomes (e.g., *"Only email me when the caller booked an appointment"*)
* **Export to CSV:** Call outcomes are included when you export your contacts, so you can use them in spreadsheets or import them into other tools
* **Track metrics on your dashboard:** Boolean (yes/no) outcomes can appear as analytics on your dashboard so you can track conversion rates over time
* **Receive via webhook:** If you use webhooks, call outcomes are included in the **`call_ended`** event payload so you can integrate them into your own systems
| Type | What it returns | Example |
| -------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| **Boolean (Yes/No)** | Yes or No answers | "Did the caller schedule an appointment?" |
| **Text** | Free text extracted from the conversation | "Summarize what product the caller was interested in." |
| **Number** | A numeric value | "How many units did the caller want to order?" |
| **Category** | One option from a set of predefined choices you define | "What was the caller's sentiment?" with options: Positive, Neutral, Negative |
| **Multi Category** | Multiple options from a set of predefined choices you define | "Which topics did the caller mention?" with options: Pricing, Features, Support, Integration |
Boolean outcomes can be displayed as metrics on your dashboard to track conversion rates over time.
***
When creating a call outcome, you'll configure these fields:
| **Field** | **Description** |
| :----------------------- | :----------------------------------------------------------------------------------------------------------------- |
| **Name** | Short label for this outcome (e.g., "Appointment Booked", "Caller Sentiment") |
| **Type** | Boolean, Text, Number, Category, or Multi Category |
| **Instructions** | Clear description of what telli should look for in the transcript. Write it like you're explaining it to a person. |
| **Include Reason** | (Optional) Toggle this on if you also want a short explanation of *why* telli chose that answer |
| **Include in Analytics** | (Boolean type only) Toggle this on to have this outcome show up as a metric on your dashboard |
The outcome type is **locked after you first save it**. You can update the instructions, but you can't change a boolean to a category later. You'd need to create a new outcome.
* Outcomes are generated **after the call ends**, based on the full conversation transcript
* Each outcome is extracted independently, so you can add as many as you need
* You can update outcome instructions at any time, and the changes will apply to new calls going forward
* There are also **built-in outcomes** that telli generates automatically on every call (like a summary, whether a dialogue occurred, and whether a transfer or voicemail was detected). These are separate from your custom ones and always active
Custom call outcomes only apply to calls made after you configure them. They won't be retroactively applied to previous calls.
# Call Transfers
Source: https://docs.telli.com/cookbooks/call-transfers/overview
Configure cold or warm call transfers to fixed destinations or Phone Number contact properties.
Warm transfers are temporarily unavailable for Duo agents. Cold transfers remain available. Saved warm-transfer destinations are skipped during calls.
Call transfers let your AI agent hand a live call to a human. You configure one or more transfer rules per agent, and each rule can use a fixed destination or a Phone Number property from the contact. The AI decides when to use each rule based on your prompt. telli supports two types: **cold** (instant handoff) and **warm** (the AI briefs the human first).
***
## Cold vs. Warm Transfer
The caller is connected directly via SIP REFER. Fast, zero delay, and the only type that supports SIP URI targets and custom SIP headers.
**Downside:** If the destination doesn't answer, the caller is stuck hearing ringing and the AI cannot return. No context is passed to the human.
On a [custom SIP trunk](/platform/phone-numbers#connect-your-existing-number-with-sip), your provider must permit SIP REFER on the trunk. Warm transfers work without it.
The caller is placed on hold while the AI calls the human in a separate session, a briefing agent summarizes the conversation, and the human confirms before the caller is connected. The human gets full context, and if they decline, don't answer, or hit voicemail, the AI returns to the caller gracefully.
**Downside:** Slower (caller on hold), and phone numbers only—no SIP URIs.
### When to Pick Which
| Scenario | Recommended |
| ------------------------------------------------ | --------------------------------------------------- |
| Transfer to a call center queue (always staffed) | **Cold**—fast, and someone will always pick up |
| Transfer to a specific person who may be busy | **Warm**—AI returns gracefully if they don't answer |
| You need to pass call context to the human | **Warm**—the briefing agent summarizes everything |
| PBX/SIP integration with custom routing | **Cold**—supports SIP URIs and custom headers |
| Transfer to a voicemail-heavy destination | **Warm**—detects voicemail and returns to caller |
***
## Warm Transfer Briefing
The briefing is what the AI tells the human before connecting the caller. In **Auto mode** (default) the AI summarizes who the caller is, what they called about, what was discussed, and what they need, then asks "Are you ready to take the call?"
You can supply a **custom briefing prompt** instead. It replaces the default instructions, but the system still appends the full conversation history, instructs the agent to ask permission before connecting, and provides the `connect_to_customer`, `wait_for_supervisor_to_return`, `voicemail_detected`, and `supervisor_unavailable` tools. Briefing prompts support contact variables like `{{contact.name}}`.
During the briefing the human can **accept** (caller connected), **ask for a moment** (the AI stays silent and checks back after 30 seconds, mentioning that the caller is still holding), **say they're unavailable** (AI returns and offers alternatives), or **not answer** (AI returns once the ring timeout expires: 5–120s when configured, 5 minutes when left empty).
***
## DTMF and SIP Headers
**Post-dial DTMF**—Send touch-tones after the destination answers, for IVR menus or extensions. Supports `0-9`, `A-D`, `*`, `#`, and `w` (0.5s pause). Example: `123w45#`. Supports contact variables (`1w{{contact.extension}}#`). Available for both cold and warm.
**Custom SIP headers (cold only)**—Attach headers to the REFER request for call correlation, routing, or analytics. telli always includes `X-Telli-Call-Id` automatically so you can match the transfer back to the original call. Add your own name/value pairs under the "Advanced" section of a cold destination; values support contact variables. Not available for warm transfers.
***
## Limited Hours
Each destination can have its own optional schedule: enable/disable per day, time windows per day, and a timezone (e.g. `Europe/Berlin`). You can also block specific dates (holidays), inheriting from account-level no-call dates or disabling per destination.
When someone requests a transfer outside the configured hours:
* **Auto (recommended)**—The AI informs the caller transfers aren't available, optionally including the schedule and next available slot. You can toggle the schedule details off.
* **Manual message**—The AI speaks an exact message you provide, verbatim, with no improvisation.
***
## Routing Logic in the Prompt
The transfer tool gives the AI the ability to transfer; your prompt tells it when. A typical multi-destination pattern:
```
## Transfer Routing
- Pricing, quotes, or purchase questions → @transferCall:sales
- Technical issues, bugs, or error messages → @transferCall:support
- Billing disputes or invoice questions → @transferCall:billing
- Explicit request for a manager → @transferCall:manager
## When NOT to Transfer
- Do not transfer if you can answer the question yourself
- Only transfer for complex issues you cannot resolve
- Always confirm with the caller before transferring
```
Common rules also include ending the call when the question is answered, using `@scheduleCallback` when the destination is unavailable, and summarizing what you've tried before transferring as a last resort.
### Route each contact to a different destination
Use a contact property when each contact needs a different transfer destination.
Create a [contact property](../../platform/contact-properties) with the **Phone Number** type.
Set a valid [E.164](../../phone-number-format) value with a leading `+` for each contact. Populate it in the telli app, by CSV import, through the [Create Contact](../../v2/endpoint/create-contact) or [Update Contact](../../v2/endpoint/update-contact) API, or from the [Contact Lookup Webhook](../../contact-lookup-webhook).
In the Agent Builder, open **Tools → Transfers**, select **Add transfer tool...**, and choose the property in **Transfer target**.
[Place a test call](../../en/get-started/test-your-agent) for a contact that has the property, trigger the transfer rule, and confirm that the expected destination rings.
See [Call Transfer](../../deep-dives/call-transfer#contact-specific-destinations) for runtime behavior and limitations.
***
## Troubleshooting
| Issue | Cold | Warm |
| ------------------------------ | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Destination doesn't answer** | Caller hears ringing indefinitely; AI cannot return (SIP limitation) | Cancels after the configured ring timeout (range 5–120s, empty means 5 minutes); AI returns to caller |
| **Voicemail on destination** | No detection; caller connected to voicemail | Briefing agent detects within 10–30s and returns to caller |
**Recommendation:** If your destination might not answer or go to voicemail, always use warm transfer and set a reasonable max ringing duration (30–60s recommended).
# Charlie
Source: https://docs.telli.com/cookbooks/charlie/overview
Edit your agent's prompt with help from Charlie, telli's built-in AI assistant in the Agent Builder.
[Charlie](/platform/charlie) is the AI assistant inside telli. You tell Charlie what you'd like to change, it drafts the edit, and you decide what to keep. You'll find Charlie in the chat panel on the left of the Builder. **Anything you might ask a human prompt writer to do, you can ask Charlie.**
New to Charlie? The [Get Started tutorial](/en/get-started/agent-editor-charlie) walks you through your first session step by step. This cookbook goes deeper: what Charlie can do in each area, how to phrase requests, and when to edit directly instead.
## What Charlie can help with
Charlie can help across multiple areas of your agent.
Tell Charlie what to change in the agent's prompt and it drafts the edit for you.
* Rewrite or restructure a specific section
* Apply a tone change across the whole prompt, like making the agent friendlier or more confident
* Insert the right variable or tool reference without having to look up the syntax
* Adapt the prompt based on a transcript or example you share
Charlie can change how your agent handles calls, not just what it says.
* Turn voicemail detection on or off, or change the message it leaves
* Switch call recording on or off, or set up a consent message
* Add or update a transfer destination
* Change the maximum call length
* Switch the voice or language
* Adjust an agent's auto-dialer strategy and dialing windows
* Propose changes to global Auto Dialer defaults for all inheriting agents
Have Charlie set up the data points telli should extract from every call.
* Add a new outcome (yes/no, number, text, or a category from a list of options)
* Refine the instruction so the extraction picks up the right cases
* Turn on the "reason" toggle so you get a short explanation alongside each value
* Turn a yes/no outcome into a dashboard metric
Charlie can search past calls and bring real conversations into the chat.
* Find calls that match specific criteria (date range, outcome, transcript text, tool errors)
* Pull up the full details of a single call, including the transcript and outcomes
* Compare the agent's setup at the time of a past call to your current draft, so you can see what's changed
* Read the upvotes and downvotes your team left on past calls
## Global Auto Dialer defaults
Ask Charlie in the telli app to change the default strategy, dialing window, no-call dates, or timezone for your account. Charlie reads the current settings and presents only the changed sections with Current and Proposed values.
Opening **Review changes** loads the current settings and compares them with the proposal. Nothing is saved until you select **Apply changes**. Select **Keep current settings** to keep your settings. Apply updates only the proposed fields and preserves omitted fields. Lists, including retry intervals and no-call dates, are replaced as a unit; switching calling strategy replaces that strategy's configuration. If another edit changes the same field after review opens, the last save wins. The server still checks feature access, Auto Dialer enablement, and the validity of the merged settings. Completed cards show the proposed values. A removed retry limit appears as **No limit**.
Global defaults affect agents that inherit account settings. They are separate from agent drafts and cannot be changed through Slack or MCP.
## How to talk to Charlie
Charlie understands both specific instructions and general feedback. Use whichever fits the change you have in mind.
Useful when you know exactly what you want changed.
> "Shorten the greeting and add a goodbye line at the end."
> "Replace the word 'reservation' with 'booking' everywhere in the prompt."
> "Add a step asking the caller for their order number before transferring."
Useful when you want Charlie to figure out the change for you.
> "The agent isn't friendly enough. Make it ask more personal questions."
> "This section feels too long. Tighten it up."
> "Make the agent sound more confident when handling objections."
If you're not happy with what Charlie did, reject the edits and try again with a clearer instruction. You can also roll back to a checkpoint to get back to a previous version.
## When to use Charlie vs edit directly
Charlie isn't a replacement for editing the prompt by hand. Some changes are faster to do yourself.
| Use Charlie when... | Edit directly when... |
| :--------------------------------------------------------- | :------------------------------------ |
| You're restructuring or rewording a section | You know the exact words you want |
| You want a general improvement applied across the prompt | You're making a small surgical tweak |
| You're working from a transcript or example document | You're changing a single line or word |
| You want help finding the right variable or tool reference | You're confident with the syntax |
## Best practices
* **Bring real call transcripts into the chat** so Charlie can refine the agent based on conversations that actually happened, not just imagined cases.
* **Use checkpoints when experimenting.** It's faster to roll back than to manually undo a stack of changes.
* **Accept changes a few at a time** if you're not sure about all of them. Reject the rest and ask Charlie to try again with more guidance.
* **Start with Charlie on new prompts.** Have it draft a structure for you, then refine the details by hand.
## Good to know
* Charlie's edits appear as inline diffs — accept or reject each one on its own, or apply them all at once. Checkpoints are created automatically, so you can roll back to any earlier state in one click.
* The chat accepts **file uploads** (a transcript, a script document, a sample message) and **voice input** for longer instructions.
* Charlie never publishes your agent. Changes stay as a draft until you click Publish.
* Charlie can also access and edit other agents if you ask it to. This is great for applying changes to multiple agents at once
* The chat remembers what you said earlier in the same session, so you can keep iterating without re-explaining context.
# Collect Data
Source: https://docs.telli.com/cookbooks/collect-data/overview
Let your agent gather specific information from callers with built-in validation and confirmation.
Collect Data is temporarily unavailable for Duo agents. Your agent can ask for information in the conversation, but the structured collection tasks do not run.
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.
***
## 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) |
**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.
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.
Custom prompts are **appended**, not a replacement-the core validation and confirmation flow always runs regardless.
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)" |
You can combine multiple constraints, but **Exact Length** and **Min/Max Length** are mutually exclusive.
***
## Integration & Prompt
Collected data appears in the `call_ended` webhook under `collected_data` (or `null` if no tasks were triggered):
```json theme={null}
{
"collected_data": {
"email": {
"status": "confirmed",
"value": "john.smith@example.com"
},
"delivery_address": {
"status": "confirmed",
"value": "Rosenthaler Straße 51, 10178 Berlin, Germany",
"latitude": 52.5253059,
"longitude": 13.4043936
},
"case_number": {
"status": "declined",
"value": null
}
}
}
```
Address tasks include `latitude` and `longitude` when address validation produced coordinates. Other task types omit these fields.
**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.
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.
```
***
## 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 |
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).
***
## 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).
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}}` |
```
[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."]
```
***
## 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.
# Context & Customer Data & Tool Calls
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/context-customer-data-tool-calls
All variables should be clearly defined in a centralized section so the agent has full clarity on what values are available and how they should be used.
### Available Variable Examples
| **Item** | Description |
| :------------------------- | :---------------------- |
| **First Name** | `{{firstName}}` |
| **Last Name** | `{{lastName}}` |
| **Language** | `{{language}}` |
| **Current Product** | `{{product}}` |
| **City** | `{{city}}` |
| **Customer ID** | `{{customerId}}` |
| **Contract Number** | `{{contractId}}` |
| **Current Contract Value** | `{{currentTotalPrice}}` |
### Tips for Using Variables
Do **not** build decision logic directly around raw custom variables (e.g., `{{letter_received}} == "Ja"`). If a variable is unknown, empty, or `0`, the agent has no instruction on how to interpret it and may not know which path to follow. Instead, clearly define what each variable represents in natural language and explicitly instruct the agent what to do in each scenario (e.g., "If the customer has received the letter and has insurance, follow path 6.2.a").
**What not to do:**
Before proceeding, review the available customer data and choose the appropriate path based on:
`{{letter_received}} == "Yes"` and `{{rsv_present}} == "Yes"` → Path 6.2
`{{letter_received}} == "Yes"` and `{{rsv_present}} == "No"` → Path 6.2
**What to do:**
*Define all variables:*
* Letter received: `{{letter_received}}`
* Legal protection insurance (RSV): `{{rsv_present}}`
*Use definitions of variables in natural language:*
Letter received == "Yes" and Legal protection insurance (RSV) == "Yes" → Path 6.2
Letter received == "Yes" and Legal protection insurance (RSV) == "No" → Path 6.2
### Tool Calls
Tool calls send HTTP requests and allow the agent to retrieve or update information during a call for example, fetching data from a meeting in a calendar.
In the prompt, you should define **when** the agent should use a tool call, but not **how** the tool call itself is executed. The technical setup and configuration of the tool call must be handled separately in the agent settings.
Within the system prompt, it is important to clearly instruct the agent under which circumstances each tool should be triggered. This can be done either by using built-in tool calls such as `@endCall` or `@callMeLater`, or by defining custom tool calls in advance.
Duo's base prompts handle call endings and caller requests for a pause. You do not need to repeat these rules in Speaking or Thinking. The caller must explicitly ask to end the call before Duo uses `@endCall`; task completion alone does not permit it.
**Examples:**
| **Tool** | Action | Example Usage Rules | Example |
| :------------------------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- |
| **`@endCall`** | Agent can end the call | For Duo, end the call only if the caller explicitly asks. For other agents, end the call once the conversation is fully concluded. | Caller: "Please end the call." |
| **`@callMeLater`** | Agent schedules another call with the user at a later point in time | Always ask the customer first if they would like a callback. Never call the tool before asking. **When to use:** Only when explicitly referenced in a scenario. **When NOT to use:** Never for expert callbacks. Never if an expert/colleague callback has already been promised. If the words "expert," "colleague," or "transfer" were used during the conversation, this tool is strictly forbidden. | The user is busy and wants to be called back later |
| **`@waitForUserToReturn`** | Wait for the user to continue the conversation | Use when giving instructions and waiting for the customer to complete an action, or when the customer explicitly asks you to wait or says they need a moment. | Agent: "Plug the cable into the power outlet." User: "One moment please, I'll be right back." |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
### Prompt Examples
```
Context
Conversations with restaurant owners or decision-makers to introduce Taco Company's franchise opportunity and schedule a meeting with a franchise development manager.
Available Variables
Definition:
personaName = {{personaName}}
firstName = {{firstName}}
lastName = {{lastName}}
email = {{email}}
phoneNumber = {{phoneNumber}}
language = {{language}}
callDirection = {{callDirection}}
currentDate = {{currentDate}}
currentTime = {{currentTime}}
currentWeekday = {{currentWeekday}}
```
```
Customer data
Today's date: {{currentDate}}
Today's weekday: {{currentWeekday}}
Current time: {{currentTime}}
Phone number (caller): {{phoneNumber}}
Background & important info
Company: Animal Doctor is a veterinary clinic providing general consultations, check-ups, vaccinations, diagnostics, and treatment for a variety of animals.
Client goal: Schedule appointments for different animals and collect key data: animal name, species, owner name, email address, and the problem/concern.
Critical information:
Emergencies: If the caller believes it's an emergency, prioritise immediate appointment booking. If no immediate availability, advise contacting the nearest emergency veterinary hospital and offer to capture details for follow-up.
Vaccinations and routine care: For routine visits, gather details and suggest the next available non-urgent slot.
Medication refills: Capture details and forward to the clinical team if an appointment is not immediately required.
Pricing: If asked, give a gentle estimate range if available; otherwise, note that final costs depend on examination and treatment. Offer a short consultation to provide clearer guidance.
```
# Global Rules & Behavior
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/global-rules-behavior
Define global rules that apply consistently throughout the entire duration of the call, ensuring the agent maintains stable behavior and tone from start to finish.
### Global Rule Examples
| **Item** | Description |
| :------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Main Goal** | Your primary goal is to understand the user's spoken requests, even if the speech-to-text transcription contains errors. Responses will be converted into speech via TTS. |
| **Silent Transcript Correction** | Silently correct likely transcription errors. Focus on intended meaning, not literal wording. If a word sounds like another in context, infer and correct it. |
| **Keep Responses Short** | Give short, direct answers unless the user explicitly requests a more detailed explanation. |
| **Prioritize Clarity & Natural Speech** | Always prioritize clarity and accuracy. Conversations should sound human. In some cases, informal grammar is okay. |
| **Time-Dependent Questions** | If asked something time-related, use `{{currentTime}}`, `{{currentDate}}`, and `{{currentWeekday}}`. Never invent dates. |
| **If You Don't Understand** | If you do not understand the request, respond with: "I am sorry I did not understand that." |
| **Follow Main Script Flow** | Follow the main conversation flow. Answer side questions briefly and return to the next logical step. |
| **Use Quoted Phrases Exactly** | Stick to the spoken suggestions marked with quotation marks (""). |
| **Virtual Assistant Disclosure** | If asked, openly explain that you are a virtual assistant. |
| **Natural Spoken Output** | Your statements will be spoken aloud, so they must sound natural. |
| **No Appointment Booking** | Do not book specific service appointments. Forward internally and only ask for general availability, not exact time slots. |
| **Limited Name Usage** | Use the customer's name only selectively and no more than three times per call. |
| **Never Ask Name Twice** | Never ask for the customer's name twice. If they already confirmed, proceed directly to the call reason. |
| **Avoid Repeating Questions** | Explicitly avoid repeating the same questions or phrases. For example, do not use "How does that sound?" more than once rephrase instead. |
| **Keep the Call Moving** | Avoid repetition and guide the conversation quickly through the script using short phrasing. |
| **No Immediate Phrase Duplication** | Never use the same phrase twice in a row (e.g., not "All good, all good…"). |
| **Explanations Max Two Sentences** | When explaining, use a maximum of two sentences at a time and avoid giving too many instructions at once. |
| **Asking Questions** | When you ask a question, always wait for the customer's answer instead of continuing immediately. |
| **"Step by Step" Limit** | Use "Step by Step" at most once per conversation. |
| **Answering Customer Questions** | Before answering, check if the knowledge is available in the prompt. If not, use `@searchKnowledgeBase`. Never invent explanations outside the prompt or KB response. Stay strictly focused on the topic. Do not assist with unrelated topics. |
| **Tool Handling (`@...`)** | References prefixed with `@` are internal tool calls. They must never be spoken or shown. Only communicate results naturally (e.g., "I'll check that for you…"). |
| **Handling Instruction Blocks** | Instruction text not in quotation marks should not be spoken aloud only dialogue-ready text blocks that make sense in conversation. |
| **Silence During Setup Instructions** | If you give setup instructions and the customer does not respond, always execute `@waitForUserToReturn`. |
| **callMeLater Pre-Check** | Before calling `@callMeLater`, check whether an expert callback has already been promised. If yes, do not use `@callMeLater`. Instead, confirm verbally and end via the expert transfer goodbye flow. |
| **Special Situations as Interrupts** | Handle special cases briefly as an interrupt, then return to the main flow using an explicit reference (e.g., "Return to 5.2"). For cases that must end the call (wrong person, language barrier, etc.), route directly to a call end. For "are you a real person?" questions: answer transparently, then continue in the current flow. |
| **Iterate Through Options with Validation** | For each option, first do an internal validity check (e.g., whether a variable like `{{firstJobName}}` is present/valid). If valid: present briefly and ask for interest. If not valid: skip silently and move to the next option. After iterating: summarize interest and route explicitly (e.g., → Go to 5.9 Closing). |
| **Internal Checks (Silent Decision Points)** | Use "Internally check: …" to indicate the agent should make a decision silently without saying it out loud. Example: Internally check whether the user showed interest in at least one option. If yes: proceed to the next step. If no: close out politely. |
### Behavior Examples
| **Item** | Description |
| :-------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Build Trust** | Show competence and empathy. |
| **Motivate the Customer** | Encourage the customer to proceed with giving a mandate or uploading the official letter by referring to the evaluation results, deadlines, and financing options. |
| **Gather Additional Information** | Clarify any special case details that were not captured in the form data. |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
### Prompt Examples
```
Rules
Your main goal is to understand the caller's intent and secure a meeting. Keep replies short and clear; your voice will be converted to speech.
Silently correct likely transcription errors and focus on intended meaning.
If asked a time-dependent question, use {{currentTime}}, {{currentDate}} and {{currentWeekday}}. Do not invent dates.
If you do not understand, say: "I'm sorry, I didn't catch that."
Follow the main flow below. Answer side questions briefly and return to the next logical step.
If asked, openly explain you are a virtual assistant.
Do not promise financial figures beyond what you state in this script. Offer a meeting for detailed information.
Do not discuss legal or contract terms on the call. Reserve details for the meeting.
Do not pressure. If not a fit or no interest, politely close or schedule a later follow-up.
If service-like questions arise (support, complaints), acknowledge and recommend contacting Taco Company through the website contact form; then return to the meeting objective if appropriate.
```
```
Rules
Your main goal is to understand the caller's request, even if the speech-to-text transcript contains errors. Your answers will be converted to speech, so your output must be plain, unformatted text.
Silently correct likely transcription errors. Focus on the intended meaning, not the literal words.
Keep replies short and direct unless the caller asks for more detail.
You are an agent manage the conversation flow and stay on topic. Follow the main conversation path (script). Briefly handle side questions or special situations, then return to the next logical step in the main flow.
You are not a human. If asked, be transparent: for example, say, "I'm a virtual assistant speaking on behalf of Animal Doctor to help you."
Be empathetic and calm, especially if an animal is unwell or the caller is distressed. Acknowledge concerns and never pressure the caller.
If you don't understand the caller, respond with: "Sorry, I didn't quite catch that. Could you say that again, please?"
If asked time-dependent questions, use today's date and weekday {{currentDate}}, {{currentWeekday}} to provide the most up-to-date information.
Use a friendly, natural tone and, once you have it, address the caller by their first name. Avoid using titles and use the name sparingly after the greeting.
Conversation start & intent clarification
Because many calls will be appointment-related, always first ask the caller's specific reason for calling regardless of any existing data.
If it is clearly a new appointment request or enquiry, proceed with new-customer qualification (section 2) and then schedule (section 5).
If it's an existing-customer matter (follow-up, change, status), capture details and pass to the team (section 3).
If it's an urgent or emergency situation, respond with empathy, capture details, and prioritise immediate scheduling or advice to contact an emergency clinic if out of hours (section 4 for exclusions if needed), then proceed to appointment (section 5) or sign-off (section 6).
Conversation goals
Build trust & empathy: Keep the caller calm, show understanding, and ensure they feel supported.
Efficient & goal-oriented: In under 5 minutes, capture the necessary details (animal name, species, owner name, contact details, problem) and secure an appointment or call-back.
Communicate value: Emphasise that the appointment ensures a vet can assess the animal promptly and advise on next steps.
```
# Identity & Core Task
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/identity-core-task
First, define the fundamental properties of the agent, such as its primary task, the language it communicates in, and how it should introduce itself.
### Core Property Examples
| **Item** | Description |
| :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Agent Name** | Emma |
| **Company** | Company Name |
| **Role** | Digital welcome assistant (AI-based, not a human) |
| **Specialization** | Traffic law matters, especially fine/penalty proceedings |
| **Primary Task** | Outbound call to new customers who registered due to a traffic violation. The goal is to build trust, motivate the customer to give a mandate or upload the official letter, gather additional case information, and explain the next steps. |
| **Language** | You always talk English. If the user switches to another language, remind them friendly that you are only able to talk in English. |
| **Call Direction** | Inbound / Outbound |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
### Prompt Examples
```
- Agent name: {{personaName}}
- Company: Taco Company
- Role: AI franchise development adviser for outbound and inbound conversations
- Language: English (always speak English)
- Specialisation: Taco Company franchise opportunity and partner onboarding
- Primary task: Identify qualified restaurant owners, understand their situation and interest, and secure a meeting to discuss becoming a Taco Company franchise partner.
```
```
- Agent name: {{personaName}}
- Company: Animal Doctor
- Role: Virtual assistant (AI-powered inbound/outbound veterinary reception agent not a human designed to listen with empathy, capture requests safely and efficiently, answer questions, and qualify and route when needed.)
- Language: English
- Specialisation: Agent in the Veterinarian sector and qualification of new enquiries
- Primary task: Capture and qualify appointment requests, collect details (animal name, species, owner name, email, problem), and schedule an appointment or arrange a call-back with the veterinary team.
- Call direction: Inbound
```
# Main Conversation Script
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/main-conversation-script
In the main script, it is essential to clearly define what the agent should say and how each scenario should be handled. Every situation must follow a structured response logic so the agent always knows exactly how to proceed.
The script should be well organized, with clearly connected sections that guide the conversation smoothly from one step to the next. Overall, it should be designed as a decision tree, ensuring that each response logically leads to the appropriate next action.
**Most important to remember:**
* Always write the prompt using clear decision-tree logic and explicitly reference the next step or path after each section.
* Clearly define the inbound and the outbound case.
* Minimize the agent's room for interpretation by providing clear guidance for every possible direction the conversation may take.
### Introduction
The introduction should clearly establish who the agent is speaking with, explain the purpose of the call, and determine whether there is initial interest.
It is also highly beneficial to use **\{\{variables}}** to create a more natural and personalized interaction. In addition, incorporating **conditional statements** provides the agent with clear guidance on when to take which action, ensuring the conversation follows a structured and predictable flow.
```
1. Outbound: "Hi {{firstName}}, {{personaName}} calling from Taco Company. Are you the owner or decision-maker for your restaurant?"
- If this is the right person → continue with 2. Situation setting.
- If not the right person → "Thanks. May I speak with the owner or the person who handles expansion and partnerships?" If unavailable, use the Callback Prompt.
- If they ask why you're calling → proceed with the short intro.
- If they are busy → use the Callback Prompt.
- If they have no interest → use the No-Interest Prompt.
```
### Situation and Pitch
The next step focuses on qualifying the prospect by guiding the conversation from situational context, to needs discovery, to value positioning, and ultimately toward securing a meeting while addressing potential objections along the way.
Here as well, incorporating **\{\{variables}}** helps create a more personalized and natural dialogue. Additionally, using **conditional statements** and clearly referencing possible objections ensures the agent has structured guidance on how to respond in different scenarios and how to move the conversation forward effectively.
```
2. Situation setting
If unfamiliar with Taco Company: "No problem we're a fast-growing taco brand focused on simple operations, strong product quality, and local market support. May I ask, are you currently exploring new revenue streams or brand partnerships for your location?"
If yes → continue with 3. Short intro and needs discovery.
If no → ask: "Understood. What's your top priority for the next few months increasing footfall, delivery growth, or simplifying operations?" Then continue with 3.
If they know Taco Company: "Great I'll keep this brief and specific for you." Continue with 3.
3. Short intro and needs discovery
Confirm the challenge → tease the solution → ask two fit questions before proposing a meeting.
"We work with independent operators to add a proven, high-demand taco concept with streamlined kitchen workflows and strong marketing playbooks. A short meeting can show whether it fits."
Fit questions (use conversationally; pick two to three):
"How would you describe your current food mix mainly dine-in, takeaway, or delivery heavy?"
"What matters most for you right now higher ticket average, or new dayparts?"
"Do you have available kitchen capacity or a plan to expand your menu offering this year?"
"How do you handle marketing primarily local channels, social, or third-party platforms?"
Interim summary: "So, boosting {{firstName}}'s business through {{theirPriority}} is key, and you have {{capacity/interest}} to explore new offerings did I get that right?"
If yes → proceed to 4. Franchise value snapshot.
If no → clarify once, then proceed or use the No-Interest Prompt.
4. Franchise value snapshot
Present briefly: confirm the problem, promise the outcome, then features as proof.
"Perfect. Taco Company focuses on a tight, profitable menu, consistent sourcing, and proven training so teams ramp quickly. Partners benefit from launch support, local-store marketing playbooks, and ongoing coaching. The goal is predictable execution and stronger margins from a category customers love."
Close the snapshot: "If we're aligned on next steps, a quick meeting to review the model, investment range, and support would make sense, right?"
If interested → go to 5. Meeting offer.
If undecided → handle objections briefly, then return to 5.
```
### Meeting
The next step is to offer the prospect a meeting, provided that qualification and objection handling have been successful. This process can be automated by integrating a calendar directly within the agent settings, allowing the agent to schedule the meeting (see more in Tool Calls).
```
5. Meeting offer
"I can set up a short franchise discovery call fifteen to twenty minutes with our franchise development manager. Would today at {{currentTime}} work for you, or would tomorrow {{currentWeekday == 'Friday' ? 'morning' : 'afternoon'}} be better?"
If they accept now: "Great. I'll book the discovery call."
If they prefer another time: Offer two specific options using the twenty-four-hour clock and confirm.
If they want materials first: "Happy to send an overview after we pencil in a time that way your questions are answered efficiently. Shall we lock a time and I'll e-mail the deck?"
```
### Objection Handling
It is important to equip the agent with clear objection handling strategies. This ensures the agent can respond confidently and guide the conversation towards scheduling a meeting. Important here is that the objections are referenced in the Situation and Pitch section to make sure that the agent uses them correctly.
```
6. Objection handling
Use acknowledge → reframe → micro-commitment → question.
No time: "I understand. Let's do a quick fifteen-minute overview so you can decide fast. Would seventeen thirty today or ten hundred tomorrow work?"
Want numbers first: "Absolutely. Investment ranges and unit economics are best explained with context. A short call ensures accuracy for your situation. Shall we schedule for tomorrow at eleven hundred?"
Already have a concept: "Great to hear. Many partners add tacos to drive incremental visits and delivery. Worth a fifteen-minute check to see if our streamlined ops fit your kitchen?"
Concerned about complexity: "Valid point. Our model focuses on a compact menu and training that reduces operational friction. Open to a quick call to walk through the workflow?"
Return to the meeting question after addressing the concern.
```
### Confirmation
In the scheduling and confirmation stage, the agent can use the calendar tool call to book a meeting directly. During this step, the agent is also able to collect and confirm all relevant contact details, such as the email address and phone number, ensuring the appointment is scheduled accurately and all necessary information is properly documented.
```
7. Scheduling and confirmation
"Perfect let's confirm the slot. I have {{currentWeekday}} at fifteen hundred or sixteen thirty. Which works better?"
Once chosen: "Booked. To send a calendar invite, could you share your best e-mail address?"
E-mail handling: Confirm by spelling the local part with NATO phonetics if needed. "Thank you I'll send the invite and an overview right away."
```
### Conversation Start & Clarify Intent
This section outlines the **opening and routing process for an inbound call** and explains how the agent should determine the caller's intent and direct the conversation.
```
- Inbound Agent: "Hi, this is {{personaName}} from Animal Doctor. How can I help you today?"
- Listen, then briefly summarise to confirm.
- If general question: Answer briefly. If more action is needed, capture the request and forward. → Go to 6. Sign-off.
- If new appointment/enquiry:
- Agent (transition): "Understood. To get you the right appointment quickly, I'll ask a few short questions."
- → Go to 2. New-customer qualification.
- If existing-customer matter:
- Agent (transition): "Okay, I understand this is about an existing case. I'll note the details and pass them to the team."
- → Go to 3. Handling existing-customer requests.
- If the caller has no time: → See No-time script.
- If not interested/wrong number: → See Not-interested script.
```
### New-Customer Qualification
This section describes the **new-customer qualification and information-gathering process** before booking an appointment.
```
- Needs assessment: "What's the animal's name, and what kind of animal is it?"
- Problem summary: "Could you briefly describe what's going on? Any symptoms or concerns?"
- Urgency check: "When did this start, and how urgent does it feel to you right now?"
- Owner details: "What's your full name, and what's the best email address for confirmation?"
- If spelling email: If an underscore is part of the address, type _ and not the word "underscore".
- Contact number: "Is {{phoneNumber}} the best number to reach you, or is there another preferred number?"
- Relevance check: Confirm Animal Doctor can handle the species/issue. If not → set boundaries and, if possible, suggest alternatives. → Go to 4. Excluding non-covered requests if needed.
- Next step: If suitable, arrange an appointment or a call-back. → Go to 5. Appointment or call-back arrangement.
```
### Handling Existing-Customer Requests
This section explains how the agent handles **existing-customer requests**.
```
- Agent: "To assign this correctly, what is the animal's name and your last name? Do you have a reference, like a previous visit date or case note?"
- Listen, summarise, and capture missing info (desired outcome, timing, medication details if relevant).
- Agent: "Thank you. I've noted everything and will forward this to the veterinary team now. They'll get back to you after reviewing it."
- → Go to 6. Sign-off.
```
### Excluding Non-Covered Requests
This section explains how to handle **requests that fall outside the clinic's scope or available services**.
```
- If the request is outside scope or we have no suitable service:
- Agent: "Thanks for getting in touch. We don't cover this specific case at the moment. The best contact would be a specialist clinic. If you'd like, I can take your details and note that you're seeking support so the team can advise alternatives."
- → Go to 6. Sign-off.
```
### Appointment or Call-Back Arrangement
This section outlines the **appointment booking process**, including how to handle availability and exceptions.
```
1. Value statement: "I can arrange a short appointment so a vet can assess your animal and advise next steps."
2. Find a time → getAvailableSlots(): "When would suit you best?" If the caller names preferences (day, morning/afternoon), follow them and propose two options.
3. Book → bookSlot(): After the caller chooses, book and confirm: "Your appointment is scheduled for [date, time]. You'll receive a confirmation email."
4. Multiple animals: If booking more than one, secure back-to-back slots and confirm details for each.
- Technical issues: If slot lookup fails, say: "It looks like there's a small technical issue right now. No worries we'll reach out later by email or phone to arrange a time."
- No available slots: "At the moment our vets are fully booked. I can take your details, and we'll contact you as soon as new times are available. Would that be okay?"
- → Go to 6. Sign-off.
```
### Sign-Off
This section describes the **call closing procedure**.
```
- Final check: "Is there anything else I can help you with today?"
- Friendly farewell: "Thanks for calling Animal Doctor. Take care, and all the best for your pet. Goodbye."
- (Call the endCall function)
```
### Alternative Scenarios
This section explains how to handle **two special call situations: lack of time and not-interested/wrong number cases**.
```
No-time script
- Agent: "No problem, I completely understand. Would you like us to call you back at another time, or would you prefer to get in touch when it suits you?"
- If call-back requested: "All right. Which day and time would be ideal for a quick call-back?" (Note the answer and sign off as in section 6.)
- If they prefer to call back themselves: "All right, I'll note that. Feel free to contact us anytime." (Sign off as in section 6.)
Not-interested or wrong number
- Agent: "I'm sorry about that. This line is for Animal Doctor regarding veterinary appointments and enquiries."
- Agent: "All right, thanks for letting me know. Have a lovely day. Goodbye."
```
# Pronunciation & Formatting
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/pronunciation-formatting
To ensure the conversation sounds natural and authentic, and to incorporate region-specific pronunciation and expressions, define these phonetic rules and localized sayings clearly within this section.
### General Pronunciation Rule Examples
| **Item** | Description |
| :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Spelling Letter by Letter** | Use English phonetics when spelling names or numbers (e.g., "A as in Anton"). |
| **Pronunciation of Prices** | Always pronounce prices naturally in full words. Example: "29.99" → "twenty nine euros and ninety nine cent." |
| **Error Code Pronunciation** | Always pronounce error codes as single digits (e.g., "three - zero - three"). |
| **Time Format** | Always use this time format (e.g., "seven - P - M" for 19:00). |
| **Date / Time Variables** | Use `{{currentDate}}`, `{{currentWeekday}}`, and `{{currentTime}}` in natural English phrasing when relevant. |
| **Email Pronunciation** | Spell the first part of the email address and pronounce the domain normally. Do not send email unless explicitly requested. Pronounce "Email" as "Ih-mail." |
| **Phone Numbers** | Spell phone numbers slowly in English (e.g., "plus four nine …"). |
| **Virtual Agent Transparency** | If asked, briefly explain that you are a virtual AI assistant. |
| **Avoid Repetition** | Avoid repeating the same phrases or words. If the customer does not understand something, rephrase instead. |
| **No Lists** | Present information in natural speech flow without bullet points or numbered lists. Never use formats like "6." |
| **Numbers in Text Form** | Always write numbers (e.g., prices or benefits) in full text form, never as digits (e.g., "thirteen euro" instead of "13 Euro"). |
| **Language Restriction** | Always speak English. If a customer asks to switch to another language, politely decline. |
| **Certain Terminologies** | Always say "Television" instead of "TV." |
| **Forbidden Word** | Never use the word "…" |
| **Use of Customer Name** | Do not address the customer by name too frequently. |
| **Formal Address** | Consistently use the formal "you." |
| **Avoid Certain Phrases** | Do not use expressions like… |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
### Prompt Examples
```
Global Rules
Spelling out: Use the NATO phonetic alphabet in English when spelling names or alphanumeric details.
Time format: Use the twenty-four-hour clock when offering times.
Dates: When speaking, use natural British English (for example, "fifteenth of March two thousand twenty-six").
E-mails: When reading an e-mail aloud, spell only the part before the at symbol.
Phone numbers: Read numbers slowly in English.
Virtual agent: If asked, briefly explain you are a virtual assistant for Taco Company.
Repetition: Avoid repeating identical phrases; rephrase if needed.
Language: Always speak English.
```
```
Conversation style
Human & natural: Friendly, reassuring, and clear.
Clear & precise: Ask short, easy-to-answer questions.
Flexible & situational: Adapt to the caller's answers while keeping the conversation moving.
Minimal name usage: After greeting, use the caller's first name sparingly.
```
# Special Situations / FAQs
Source: https://docs.telli.com/cookbooks/how-to-prompt/prompt-structure/special-situations-faqs
In the Special Situations / FAQ section, provide structured guidelines for handling minor or edge-case questions that the user may raise. Each entry should clearly reference the corresponding scenario or flow to ensure proper routing. This reduces ambiguity and minimizes the risk of the agent generating unsupported or hallucinated responses.
### Special Situation Examples
| **Case** | Trigger | Handling | Logic |
| :----------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Final Cancellation or Return** | Customer wants to cancel the contract and is not going to change their mind (e.g., "I do not want to be a customer anymore", "I want to terminate my contract immediately"). | Inform the customer that cancellation can be done in writing or online via the website under the contract section. | After providing the information, proceed to the Standard Farewell (reference to farewell). |
| **Customer Wants to Call Back Themselves** | Customer states they will call back later (e.g., when they have time). | Inform the customer that they cannot call the assistant directly yet, but that an individual callback can be arranged. | **If no callback desired:** Proceed to Standard Farewell (reference). **If callback desired:** Use tool `@callMeLater`. After confirmation, proceed to Callback Farewell (reference). |
### FAQ
Especially important for inbound calls.
| **Item** | Description |
| :------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| **Expert Callback Availability** | Experts are available Monday through Friday between ten and seventeen o'clock. No expert callbacks can be scheduled outside these hours. |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
### Prompt Examples
```
Special Cases
Different intent (support/service): "I'm sorry to hear that. The quickest way is via the Taco Company website's contact form. Would you still like to schedule a brief franchise call while I have you?"
Permission withdrawal: "Understood, I'll note that immediately. You won't receive franchise updates. Would you like to block only calls or all contact methods?"
Bad sentiment: "I understand let's keep this simple. Shall I arrange a call-back at a better time?"
If you do not know an answer: "I don't have that information to hand. Our franchise manager can cover it in detail. Shall we schedule a quick call?"
Long contract or commitments with other brands: "A short consultation can still be useful; options and timing can be aligned. Shall we book a quick call to explore?"
FAQs and Further Info (for brief spoken answers only)
Concept: Focused taco menu, strong flavour profile, streamlined prep, and ongoing support.
Support: Site launch support, marketing playbooks, ongoing coaching.
Next step: Schedule a discovery call to review investment range, timelines, and operational model.
```
```
FAQs & objection handling
Question: How long is a standard consultation?
Answer: About 15–20 minutes, depending on the case.
Question: What information do you need to book?
Answer: The animal's name, species or breed, your name, your email, and a brief description of the problem.
Question: Can I bring multiple animals?
Answer: Yes, we can book separate back-to-back slots. I'll just need details for each animal.
Question: Do you handle exotics or large animals?
Answer: We can handle many small and common household pets. For certain exotics or large animals, I'll check availability. If it's outside our scope, I'll share alternatives where possible.
Alternative scenarios
No-time script
Agent: "No problem, I completely understand. Would you like us to call you back at another time, or would you prefer to get in touch when it suits you?"
If call-back requested: "All right. Which day and time would be ideal for a quick call-back?" (Note the answer and sign off as in section 6.)
If they prefer to call back themselves: "All right, I'll note that. Feel free to contact us anytime." (Sign off as in section 6.)
Not-interested or wrong number
Agent: "I'm sorry about that. This line is for Animal Doctor regarding veterinary appointments and enquiries."
Agent: "All right, thanks for letting me know. Have a lovely day. Goodbye."
Sample dialogue flow (appointment request)
Agent: "Hi, this is {{personaName}} from Animal Doctor. How can I help you today?"
Caller: "I need to book my dog in."
Agent: "Of course. What's your dog's name, and what breed is it?"
Caller: "Bella, she's a Labrador."
Agent: "Thanks. What seems to be the problem with Bella?"
Caller: "She's limping since yesterday."
Agent: "I'm sorry to hear that. When did it start, and has it gotten worse?"
Caller: "Since last night, a bit worse today."
Agent: "Understood. What's your full name, and what's the best email for confirmation?"
Caller: "Sam Green, sam.green@example.com."
Agent: "And is this number the best to reach you?"
Caller: "Yes."
Agent: "Thank you. I'll find the next available appointment now. Would tomorrow morning or afternoon suit you better?" → proceed to section 5.
```
# What cannot be prompted
Source: https://docs.telli.com/cookbooks/how-to-prompt/what-cannot-be-influenced
Although the system prompt is the core of the agent and plays a crucial role in shaping its behavior, it cannot control every aspect of the system. Below are a few examples of areas that cannot be influenced solely through the prompt.
### Examples
| **Item** | Description |
| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| **Voice (speed, volume, pitch)** | Determined by the TTS provider (Cartesia/ElevenLabs), not by the prompt |
| **Timing / Pauses** | The agent has no sense of time; "wait 2 seconds" is ignored |
| **First Message** | Set in the "First Message" field in the agent settings, not in the system prompt |
| **Silence Handling** | The idle handler is system behavior; the prompt can only trigger `waitForUserToReturn` as a tool call |
| **Call Outcomes** | Extracted from the transcript after the call, not set by the agent during the call |
| **Tool Calls** | The prompt defines when a tool is called; the system automatically provides the current parameter definition to the agent |
*This table shows examples. The list is not exhaustive and can be extended based on your use case.*
# Knowledge Base
Source: https://docs.telli.com/cookbooks/knowledgebase/overview
Give your telli agent access to your own documents so it can answer questions using your specific information during calls.
The knowledge base gives your telli agent access to your own documents so it can **answer questions using your specific information** during calls. Instead of relying only on its system prompt, the agent can search through your uploaded files in real time to find accurate answers.
Think of it as giving your agent a reference manual it can flip through mid-conversation. When a caller asks something that's covered in your documents, the agent will look it up and respond with the right information.
## Quick Start
Go to **Knowledge Base** in the sidebar navigation, click **Create**, give your knowledge base a name, and upload up to 5 files (PDF, Word, TXT, or Markdown).
Your knowledge base will process your documents (usually takes less than a few minutes)
Go to **Agent Settings** → **Knowledge Base** section, select your knowledge base, and save.
***
* **FAQs & company info:** Upload your frequently asked questions so the agent handles common inquiries accurately
* **Product details & pricing:** Give the agent access to your catalog so it can answer specific product questions
* **Policies & procedures:** Upload return policies, terms of service, or internal procedures
* **Service descriptions:** Detailed descriptions of your services that are too long for the system prompt
* **Any reference material:** Anything you'd want a human employee to have on their desk while taking calls
**Example:** You upload your company's FAQ document, product catalog, or pricing sheet. When a caller asks *"What are your opening hours?"* or *"How much does the premium plan cost?"*, your agent searches the knowledge base and gives the correct answer straight from your own documents.
| **Format** | **Extension** |
| :--------- | :------------ |
| PDF | **`.pdf`** |
| Word | **`.docx`** |
| Plain Text | **`.txt`** |
| Markdown | **`.md`** |
| | **Limit** |
| :---------------------------- | :----------------------------------------- |
| **Files per knowledge base** | Up to 5 |
| **File size** | Up to 20 MB per file |
| **Total content** | Approximately 1,500 pages across all files |
| **Knowledge bases per agent** | 1 |
1. **A caller asks a question** that the agent thinks might be answered in the knowledge base
2. **The agent automatically decides** to search the knowledge base. You can also call `@searchKnowledgeBase` to make it clearer for the agent when to use the knowledge base
3. **While searching**, the agent says a short in-progress message you can customize per agent (e.g., *"Let me check that for you"*)
4. **The agent finds relevant information** and responds with an answer based on your documents
5. **The search happens in real time** and typically takes just a couple of seconds
The agent uses two search methods behind the scenes, **semantic search** (understanding the meaning of the question) and **keyword search** (matching specific terms), to find the most relevant information from your documents.
***
## Advanced Knowledge Base Capabilities
Most agents work well with the defaults. If you want to fine-tune how your documents are split and retrieved, expand the **Advanced Configuration** section on a knowledge base to adjust:
| **Setting** | **What it controls** | **Default** | **Range** |
| :------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :---------- |
| **Chunk size** | Maximum characters per chunk when splitting documents. Larger chunks keep more context together, smaller chunks make individual matches more precise. | 1000 | 200 to 8000 |
| **Chunk overlap** | Characters shared between consecutive chunks to preserve context across chunk boundaries. | 200 | 0 to 2000 |
| **Max retrieval results** | Number of chunks the agent retrieves per search. More chunks give the agent more context, but can dilute the final answer. | 10 | 1 to 20 |
Start with the defaults. Only tune these if you notice the agent missing relevant info (try a larger chunk size or more retrieval results) or pulling in irrelevant content (try a smaller chunk size or fewer retrieval results).
***
## Set up your knowledge base with Charlie
You don't have to wire up the knowledge base by hand. Once you've created one, [Charlie](/cookbooks/charlie/overview), the AI assistant in the Agent Builder, can attach it to your agent and adjust its behavior for you. Charlie can't create the knowledge base itself, so create it first under **Knowledge Base** in the sidebar.
* Attach an existing knowledge base to the agent
* Adjust the in-progress message the agent says while it searches
* Tune the advanced configuration (chunk size, overlap, retrieval results)
***
## Best Practices
* **Keep documents focused:** A document about one topic works better than a massive document covering everything.
* **Use clear headings:** Especially in `.md` and `.docx` files, headings help telli break up the content into searchable sections.
* **Be explicit:** Write your documents the way you'd want someone to answer the phone. If the answer to *"What are your hours?"* is in your document, make sure it clearly says something like *"Our opening hours are Monday–Friday, 9am–5pm"*.
* **Update when things change:** If your pricing, policies, or hours change, update the files in your knowledge base so the agent always has current information.
* **Use the right format:** PDFs work great for existing documents. Markdown or plain text is ideal if you're writing content specifically for the agent.
***
## Good to Know
* The knowledge base is processed once when you create or update it, so there's no ongoing delay on every call
* The agent decides on its own when to search the knowledge base. You don't need to configure specific trigger phrases
* If the agent can't find relevant information in the knowledge base, it will fall back to its general knowledge and system prompt
* Processing status is shown on the knowledge base card. You'll see a spinner while it's being processed, and an error indicator if something went wrong
Explicitly instruct the agent when to use `@searchKnowledgeBase` in your prompt to have more control over when the agent is using the KB.
# Tool Calls
Source: https://docs.telli.com/cookbooks/tool-calls/overview
Let your agent perform specific actions during a call using tool calls.
Tool calls let your agent **perform specific actions during a call** like ending the conversation, scheduling a callback, waiting for the customer, or calling an external API.
## Quick Start
Prefix the tool name with `@` to reference it: `@toolName`. Tools that support multiple configured instances `@collect_data` and `@transferCall` take a colon-suffixed identifier picking the specific instance, e.g. `@collect_data:email` or `@transferCall:sales`. The Builder's tools sidebar inserts the correct reference for you.
In your system prompt, clearly explain when the agent should (and shouldn't) use the tool
During calls, the agent will trigger the tool when appropriate the customer never hears the tool name
In the prompt, define **when** the agent should use a tool not how the tool works technically. The technical configuration is handled separately in the agent settings.
***
1. **The agent decides** Based on your prompt instructions, the LLM determines a tool should be called
2. **The tool executes** The tool runs in the background; the customer never sees or hears the tool name
3. **The agent continues** The agent receives the result and continues the conversation naturally
These tools are available to every agent out of the box:
| **Tool** | **What It Does** |
| :------------------------- | :--------------------------------------------- |
| **`@endCall`** | Ends the call |
| **`@callMeLater`** | Schedules a callback at a later time |
| **`@waitForUserToReturn`** | Pauses and waits for the customer to come back |
| **`@searchKnowledgeBase`** | Searches the agent's knowledge base |
Custom tools allow your agent to call external APIs during a conversation for example, looking up an account balance, checking appointment availability, or updating a CRM record.
Custom tools are configured in the **Tools** section of your agent settings. See [Custom HTTP Tools](/custom-tools) for setup instructions.
**How to Prompt Custom Tools**
To help agents clearly understand **when and how to use tool calls**, each tool includes a **"Description"** (Global tool prompt) tab within its configuration page. There is also an LLM Variable prompt which can reference previous tool calls or define format.
A detailed description:
* Clearly explains **when** the tool should be used
* Defines **how** it should be used
* Specifies any important conditions, constraints, or input expectations
* Reduces ambiguity for the agent
Additionally, the description provides important context to the **system prompt**, helping the system better understand the intended use case and triggering logic of the tool.
***
**Define When to Use**
For each tool, clearly define in the system prompt:
**→ When** the agent should use it
**→ When** the agent should **not** use it
**→ What to say** to the customer before/after using it (if anything)
***
**Never Speak Tool Names Out Loud**
Tool references in square brackets `[]` are internal actions. The customer only hears the natural-language result.
**Don't say:** *"I'm now calling the searchKnowledgeBase tool"*
The customer shouldn't hear technical tool names.
**Do say:** *"Let me quickly look that up for you..."*
Keep it natural and conversational.
***
**Place Instructions Close to Usage**
If a tool is related to a specific section of your script (e.g., knowledge base search is related to troubleshooting), put the usage instructions in that section not buried in a generic rules list far away.
***
**Give Concrete Examples**
Don't just say "use the knowledge base with Artikelnummer." Show the agent exactly what a correct query looks like.
**Don't write:** *"Use Artikelnummer when in the installation flow"*
Too vague the agent doesn't know the exact format.
**Do write:** *"When the customer is in the installation flow, always include the article number in your search query. Example: **`fulltext_query: 'Artikelnummer 000012345 Sky Stream Installation kein Bild'`***"
Clear, specific, with a concrete example.
***
**Define Negative Cases**
For every "when to use," also define "when NOT to use." This prevents the agent from over-using or mis-using tools.
| **Tool** | **Action** | **Usage Rules** | **Example Situation** |
| :------------------------- | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------ |
| **`@endCall`** | Agent ends the call | Only after all tasks are completed and the conversation is fully concluded. | The right person is not available |
| **`@callMeLater`** | Agent schedules a callback | **Always** ask the customer first if they want a callback. Never call the tool before asking. Never use for expert callbacks. If the words "expert," "colleague," or "transfer" were used, this tool is **forbidden**. | The user is busy and wants to be called back later |
| **`@waitForUserToReturn`** | Waits for the customer | Use when giving instructions and waiting for the customer to act, or when the customer asks you to wait. | Agent: "Plug in the cable." User: "One moment, I'll be right back." |
| **`@searchKnowledgeBase`** | Searches knowledge base | Use before giving technical instructions. Never use after already giving instructions. | Customer asks a product question not covered in the prompt |
# Variables
Source: https://docs.telli.com/cookbooks/variables/overview
Learn how to use variables to personalize your agent's prompts dynamically.
Variables allow you to **inject dynamic data directly into your agent's prompt**, like the customer's name, phone number, current date, or any custom data you define. This lets your agent personalize conversations and access relevant information without hardcoding it into the prompt.
## Quick Start
Use double curly braces: `{{variableName}}`
Variables like `{{firstName}}`, `{{currentDate}}`, and `{{phoneNumber}}` are always available
Add custom properties to contacts (e.g., `{{contractId}}`, `{{city}}`) and reference them in your prompt
***
* **Personalize greetings:** *"Hi `{{firstName}}`, this is Emma from Telli"*
* **Reference customer data:** *"I see you're calling about contract `{{contractId}}`"*
* **Context-aware routing:** Use customer properties to guide the conversation flow
* **Time-based behavior:** Adjust responses based on `{{currentTime}}` or `{{currentWeekday}}`
* **Pass order details:** Include order numbers, product names, or any custom data from your system
These are standard variables that telli provides automatically for every call. You don't need to configure them, they're always available:
| **Variable** | **Description** |
| :----------------------- | :-------------------------------------- |
| **`{{firstName}}`** | Customer's first name |
| **`{{lastName}}`** | Customer's last name |
| **`{{phoneNumber}}`** | Customer's phone number |
| **`{{email}}`** | Customer's email address |
| **`{{language}}`** | Customer's language |
| **`{{currentDate}}`** | Today's date |
| **`{{currentTime}}`** | Current time |
| **`{{currentWeekday}}`** | Current day of the week |
| **`{{callDirection}}`** | Whether the call is inbound or outbound |
Variables and properties you define yourself when creating contacts in telli. These give you complete flexibility to pass any information to any agent in the account.
| **Variable** | **Example Use Case** |
| :------------------------------ | :-------------------------------- |
| **`{{product}}`** | Customer's current product |
| **`{{city}}`** | Customer's city |
| **`{{customerId}}`** | Customer ID |
| **`{{contractId}}`** | Contract number |
| **`{{currentTotalPrice}}`** | Current contract value |
| **`{{__contact_empfangsart}}`** | Custom field from contact details |
The variable names you use in the prompt must exactly match the names you define in your contact details. If you name a field **`contractId`** in the contact, you must reference it as **`{{contractId}}`** in the prompt, not **`{{contract_id}}`** or **`{{ContractId}}`**.
***
**Define Variables in a Centralized Section**
**Define all variables in a centralized section** so the agent has full clarity on what values are available.
***
**Don't Build Logic Around Raw Values**
If a variable is unknown, empty, or **`0`**, the agent has no instruction on how to interpret it and may not know which path to follow.
Instead, clearly **define what each variable represents in natural language** and explicitly instruct the agent what to do in each scenario.
### What not to do
```
Before proceeding, review the available customer data and choose the path:
{{letter_received}} == "Yes" and {{rsv_present}} == "Yes" → Path 6.2
{{letter_received}} == "Yes" and {{rsv_present}} == "No" → Path 6.3
```
What the agent sees if there is no letter and no RSV:
`0 == "Yes" and 0 == "Yes" → Path 6.2`
The agent sees the raw template syntax and may not know what to do if a value is missing or unexpected.
### What to do
```
# Available customer data
- Letter received: {{letter_received}}
- Legal protection insurance (RSV): {{rsv_present}}
# Routing rules
- If the customer has received the letter AND has legal protection insurance → follow Path 6.2
- If the customer has received the letter but does NOT have legal protection insurance → follow Path 6.3
- If the letter status is unknown or empty → ask the customer whether they have received a letter before proceeding
```
This way the agent understands the meaning behind each variable and knows exactly what to do in every scenario, including when data is missing.
***
**Reference Variables Where They're Used**
For longer prompts, define the variable clearly in a centralized section at the top, then reference it again right before it's actually used. This helps reinforce context and reduces the risk of misapplication.
***
**Use Them in Workflows Too**
The contact properties you set up here also work in workflows. When a workflow runs after a call, you can use the same values to send a webhook, update a contact, or sync to your CRM. You only need to set them up once.
See [Workflows](/cookbooks/workflows/overview) for the full list of places they can show up.
* Variable names are **case-sensitive**. `{{contractId}}` is different from `{{ContractId}}`.
* System default variables are available on every call automatically
* Custom variables only populate if the contact has that property defined
* If a variable is undefined, the agent sees an empty value. Make sure to handle missing data in your prompt logic.
* Variables can be used anywhere in the prompt: in the identity section, conversation script, or rules
To avoid confusion, always test your prompt with both populated and empty variable values to ensure the agent handles all scenarios gracefully.
# Professional Voice Cloning
Source: https://docs.telli.com/cookbooks/voice-cloning/overview
How to create a high-quality professional voice clone for use with your telli agent.
This guide explains how to create a high-quality professional voice clone. Follow each step carefully to ensure optimal results.
## Recording Requirements
| | |
| :------------------- | :------------------------------------------------------------------------------------------- |
| **Recording Length** | 60–90 minutes of continuous speech |
| **Content** | Natural, live monologue about any topic + reading script and characters |
| **Speaker** | Only one speaker no interruptions or overlapping voices |
| **Audio Quality** | Clear, consistent, no background noise or echo. Any setup including newer smartphones works. |
| **Environment** | Quiet space with a microphone |
| **File Format** | `.mp3` or `.wav` |
| **Submission** | Upload to your dedicated Slack channel. If you don't have one, contact the team. |
Your recording environment has a significant impact on voice clone quality. Choose your setup carefully.
**Recommended setups:**
* Phone booth
* Meeting room
* Sound-treated space
Before you start recording, confirm the following:
* At least 60 minutes of recording
* Only one speaker throughout
* Consistent audio quality
* File format is `.mp3` or `.wav`
**Duration:** \~50 minutes
Record natural, unscripted speech. It doesn't matter what you say just keep it natural and tell stories. Do not read from a script.
**Suggested topics:**
* Vacation experiences
* Childhood and upbringing
* Food and preferences
* Hobbies
* Daily routines
Try to talk about positive experiences rather than negative ones. This strongly affects the tone and friendliness of the resulting voice.
This section ensures pronunciation consistency and structured speech coverage.
**General instructions:**
* Speak only the **agent's lines**
* Pause \~1 second after each line
* Read customer text silently do not speak it aloud
* Maintain a **friendly, professional tone**
**Agent:**
"Good day, this is Anna Weber from Müller Immobilien GmbH. Am I speaking with Mr. Mustermann?"
*(Pause)*
**Agent:**
"Exactly. A few days ago, you showed interest via our online listing for the 3-room apartment in Prenzlauer Berg. I wanted to ask if you would have time to schedule an appointment?"
*(Pause)*
**Agent:**
"Great. I still have appointments this week on Wednesday at 4 p.m. or on Friday at 10 a.m. Would either of those times work for you?"
*(Pause)*
**Agent:**
"Friday at 10 a.m. is currently best, because there are more viewings scheduled afterward. If you prefer, we can move the appointment to 11 a.m. – would that be okay?"
*(Pause)*
**Agent:**
"Great, then the appointment is set for Friday, June 6 at 11 a.m. The address is Schönhauser Allee 45, Prenzlauer Berg. I'll send you a confirmation by email shortly. Do you have any other questions about the apartment?"
*(Pause)*
**Agent:**
"The monthly utility costs are approximately 250 euros, including heating and water supply. There isn't a private parking space in the building, but there's a parking garage on the street, where we can help you arrange a spot if you like. Is that okay for you?"
*(Pause)*
**Agent:**
"The deposit amounts to one month's rent. Move-in could be as early as July 1st, if everything works out and you decide to proceed. I'll summarize all of this in the email. If anything is still unclear afterward, you're welcome to call me anytime."
*(Pause)*
**Agent:**
"You're very welcome. See you Friday, Mr. Mustermann. Have a nice day!"
*(Pause)*
**Alphabet**
* "A … B … C"
* "A as in Alpha … Z as in Zulu"
*Tone: Friendly*
***
**Number Sequences**
* 0 to 30
* "1,234,567"
* "4,999.99 €"
*Say once slowly and clearly, once naturally.*
***
**Date & Time**
* "May 28, 2025, 4:30 p.m."
*Say once slowly and clearly, once naturally.*
***
**Phone & ZIP Codes**
* "+49 30 8899 1122"
* "0800 123 45 67"
* "10115"
*Say once slowly and clearly, once naturally.*
***
**Special Characters**
| Symbol | Say |
| :----- | :--------- |
| @ | at |
| # | hashtag |
| / | slash |
| % | percent |
| € | euro |
| & | and |
| - | dash |
| \_ | underscore |
**Checklist before upload:**
* Audio is clear and consistent
* No interruptions or background noise
* Full duration completed
* File is exported as `.mp3` or `.wav`
Try to cut out long pauses in the recording. Agents using the voice can mirror them, which creates unnatural silences during calls.
**Upload:** Submit the file to your dedicated channel of communication.