Actions & Tools
The Actions tab is where you manage everything your agent can do beyond conversation — tools that the agent calls during a live conversation, prefetch hooks that run before the call starts, and post-call actions that fire once the call ends. Retrieval Variables (the data fields the agent extracts from each conversation) live in the same tab.
All tools, actions, and the new pre-fetch hook are available on every plan, including Free.
The Three Phases
Every entry in the Actions tab belongs to one of three lifecycle phases. The Add Tool dropdown groups them so it's clear when each thing fires:
| Phase | When | Examples |
|---|---|---|
| Pre fetch | Before the agent says hello | Look up the caller in your CRM by phone, pull the latest order, fetch a personalised greeting from your backend |
| Live call | During the conversation, triggered by the AI when the moment is right | Forward the call to a human, check calendar availability, transfer to another agent, query an external API for data |
| Post call | After the call ends | Send a summary email, fire an SMS confirmation, push the call payload to your CRM |
The dropdown opens with Pre fetch at the top because every other phase already has plenty of options — the new pre-fetch entry is the most likely thing you're hunting for.
Pre fetch
Pre-fetch hooks let your agent start the call already knowing who's calling. They run in parallel with call setup and inject the response into the agent's system prompt before the very first word is spoken.
When to use it
- Recognise a returning customer by phone number and greet them by name
- Pull the caller's open order, last appointment, or membership tier
- Pre-load business context that depends on which line was dialled
- Inject CRM notes so the agent knows the lead's stage and history
How it works
- The platform resolves the caller's phone (from SIP for inbound, from the dial target for outbound).
- Every active Pre fetch action fires in parallel with a hard 1.5 s per-request timeout and a 2 s overall budget.
- Successful responses are concatenated into the agent's system prompt as named blocks — the agent reads them on its very first turn.
- Failures are silent — a slow or broken endpoint never blocks the greeting. The agent just starts speaking without that block.
Configuration
| Field | Description |
|---|---|
| Name | Required. Internal label and the block name in the prompt — keep it short and descriptive (e.g. crm_lookup, vip_check). |
| API URL | Required. Endpoint to fetch from. Supports {phone}, {direction}, {agent_id}, {user_id}, {call_id} placeholders. |
| HTTP Method | GET is the default and best fit. POST / PUT / PATCH / DELETE also work. |
| Headers | Optional. Static or templated (placeholders work here too). |
| Query Parameters | Optional. Pre-fills with phone={phone} for new pre-fetch actions. |
Available variables
These tokens are substituted into URL, headers, and query/body params at request time:
| Variable | Source | Example value |
|---|---|---|
{phone} | Caller's phone (E.164) — for outbound, the destination number | +431234567890 |
{direction} | inbound or outbound | inbound |
{agent_id} | Internal agent id | 65f1a2b3c4... |
{user_id} | Workspace owner id | 65e0b1c2d3... |
{call_id} | Call id (lets your backend correlate later post-call payload) | 65f1f2c4d5... |
Unknown placeholders are left verbatim — bad templating never crashes a call.
What lands in the prompt
If your endpoint at https://crm.example.com/lookup?phone={phone} returns:
{ "name": "Sarah Johnson", "tier": "Gold", "open_orders": 1 }
The agent's system prompt is appended with an XML-wrapped block named after the action:
<call_context>
<block name="crm_lookup">
{ "name": "Sarah Johnson", "tier": "Gold", "open_orders": 1 }
</block>
</call_context>
You don't need to tell the agent how to use it — the LLM picks up the context naturally. Optionally, mention the pre-fetch in your system prompt: "If <call_context> contains a customer name, greet them by name."
Important constraints
- Phone calls only. Pre-fetch does not run for widget (web) calls — there's no phone number to template against.
- 8 KB cap on response body — anything longer is truncated before injection. The cap protects your prompt token budget and bounds the blast radius of a malicious endpoint.
- No
conditionevaluation — pre-fetch always fires when active. There's no transcription yet to evaluate against.
Use GET with a phone-keyed lookup endpoint. Keep responses small and structured (JSON object with 3-5 fields). The agent doesn't need your full customer record — just the bits that change the conversation.
Live Call Tools
These run during the conversation. The AI decides when to call each tool based on its description and the current dialogue.
Available Tools
| Tool | Purpose | When to use |
|---|---|---|
| Call Forwarding | Transfer to a human operator | Customer asks for a person, complex issues |
| Google Calendar | Check availability and book appointments | Customer wants to schedule a meeting |
| Outlook Calendar | Same, via Microsoft Outlook | Customer wants to schedule a meeting |
| API Tool RAG | Fetch live data from an external API | Need real-time info (orders, stock, account state) |
| Agent Transfer | Transfer to another voice agent | Caller needs a different department or specialist |
| HubSpot CRM | Read/write contacts and deals in HubSpot | Logging the call to HubSpot, looking up a lead |
| MCP servers | Expose tools from any of your registered MCP servers | You run an MCP-compatible tool server and want the agent to use its tools mid-conversation |
Call Forwarding
Transfer calls to a human when specific conditions are met.
| Setting | Description | Example |
|---|---|---|
| Name | Required. Person or department name | "Sales Manager" |
| Forwarding Number | Default phone number to transfer to | "+49 123 456 789" |
| Trigger Condition | When the agent should transfer | "Customer asks for manager or issue cannot be resolved" |
| Conditional Routing Numbers | Condition-to-number mapping for routing | {"billing": "+49 111 222", "technical": "+49 333 444"} |
How it works:
- During a conversation, the AI evaluates the Trigger Condition.
- If conditional routing numbers are set, the matching condition determines which number to call.
- Otherwise, the Forwarding Number is used.
- The agent informs the caller about the transfer.
- The call is forwarded — if no answer, it returns to the agent.
You can add multiple Call Forwarding tools for different departments — one for "Sales" and another for "Technical Support" with different conditions and numbers.
Google Calendar
Connect your Google Calendar so the agent can check availability and book appointments during calls.
Setup:
- Go to Integration → Calendars and connect your Google account first.
- Add the Google Calendar tool in the agent's Actions tab.
- Select the calendar to use.
- Configure your availability settings.
| Setting | Description | Default |
|---|---|---|
| Calendar | Required. Which calendar to use | Your primary calendar |
| Timezone | Timezone for appointments (IANA format) | Auto-detected |
| Work Start Time | Start of working hours | 9:00 AM |
| Work End Time | End of working hours | 6:00 PM |
| Slot Duration | Appointment length in minutes | 30 |
| Working Days | Available days of the week | Monday–Friday |
| Buffer Between Appointments | Buffer between appointments (0–60 min) | 0 |
Supported slot durations: 15, 30, 45, 60, 75, 90, 105, 120 minutes.
Set up your working hours and days accurately — the agent will only offer time slots within your configured availability.
Outlook Calendar
Connect your Outlook Calendar for appointment scheduling during calls. Works the same way as Google Calendar.
Setup:
- Go to Integration → Calendars and connect your Outlook account first.
- Add the Outlook Calendar tool in the agent's Actions tab.
- Select the calendar to use.
- Configure your availability settings.
The settings are identical to Google Calendar (timezone, work hours, slot duration, working days, buffer).
Agent Transfer
Transfer a call to another voice agent on your account. Useful when you have specialised agents for different departments.
| Setting | Description |
|---|---|
| Target Agent | Required. Select which agent to transfer to |
| Trigger Condition | When to transfer (e.g., "Caller asks about technical support") |
Example: A receptionist agent transfers callers to a sales agent when they ask about pricing, or to a support agent when they have a technical issue.
HubSpot CRM
Read and write to your HubSpot CRM during the call. Lets the agent log interactions, look up a contact by phone, or push deal updates without you having to script the API calls.
Setup:
- Go to the Integrations page and connect your HubSpot account.
- Add the HubSpot CRM tool in the agent's Actions tab.
- Select which pipeline and properties the agent should be allowed to touch.
After the tool is added, the AI can match the caller to a HubSpot contact by phone, fetch deal stage, and update fields — all from the live conversation.
MCP servers
Expose tools from any MCP-compatible server you've connected to Hanc.AI. One agent can pull from multiple MCP servers; one MCP server can serve multiple agents.
Setup:
- Connect your MCP server(s) once under Integration → MCP servers. See the dedicated MCP Servers page for full registration steps.
- Add the MCP servers entry to this agent's Actions tab — it's grouped under Live call in the Add Action dropdown.
- Toggle on which of your registered connections this agent should have access to.
- Add a short "When to use it" instruction so the agent knows when to reach for these tools.
The agent re-discovers the tool set from each enabled MCP server at the start of every call, so changes you make server-side appear automatically on the next call. Tools are renamed with the connection label as a prefix so similarly-named tools from different servers don't collide.
API Tool RAG
Connect to external APIs to fetch real-time information during calls — look up orders, check inventory, verify accounts, or access any data available via API.
| Setting | Description | Example |
|---|---|---|
| Name | Required. Tool name | "Order Lookup" |
| Description / When to Use | Required. When to query the API | "Customer asks about order status" |
| API URL | Required. API endpoint. Can include {placeholder} tokens that will be substituted with values from the Body Parameters Schema (see below). | "https://api.yourshop.com/orders/{order_id}" |
| HTTP Method | Required. HTTP method | GET, POST, PUT, DELETE, PATCH |
| Loading Message | What agent says while waiting | "Let me check that for you..." |
| Timeout | Maximum wait time (ms) | 5000 (default) |
| Headers | Static HTTP headers sent with every request | {"Authorization": "Bearer KEY"} |
| Query Parameters | Static query string parameters added to every request | {"apiVersion": "v2"} |
| Body Parameters Schema | Required. JSON Schema describing the arguments the AI should extract from the conversation and pass to the tool. See Writing the Body Parameters Schema. | JSON Schema object |
Always set a Loading Message — silence during API calls feels broken to the caller.
The old "Run on call start" checkbox on API Tool RAG has been replaced by the dedicated Pre fetch entry. Use Pre fetch when you want the data before the conversation begins; use API Tool RAG when the agent should decide during the call whether to fetch.
Writing the Body Parameters Schema
Despite the name, Body Parameters Schema is not a raw request body. It's a JSON Schema describing what the AI should extract from the conversation and pass to your tool. Depending on the HTTP method and the URL template, these values end up in the URL, the query string, or the JSON body:
| HTTP method | Where extracted values go |
|---|---|
URL contains {name} | The matching value is substituted into the URL |
GET, DELETE | Remaining values are appended to the URL as ?key=value |
POST, PUT, PATCH | Remaining values are sent as JSON body |
Minimal structure
{
"type": "object",
"properties": {
"param_name": {
"type": "string",
"description": "What this value is and how the AI should pick it"
}
},
"required": ["param_name"]
}
The root type is always "object". properties lists each argument. required marks which ones the AI must always provide — if the customer hasn't said it yet, the AI will ask before calling the tool.
Field reference
| Field | Purpose |
|---|---|
type | JSON type of the value: "string", "number", "integer", "boolean", "array", "object" |
description | Most important. Tells the AI what the value means, what format to use, and when to provide it. Add examples whenever possible. |
enum | Restricts the value to one of a fixed list. The AI will map natural speech onto the nearest option (e.g. "the blue one" → "blue"). |
minimum, maximum | Numeric bounds. The AI will refuse/clamp out-of-range values. |
default | Value used when the AI doesn't pass this field. Not required, but documents the implicit value. |
format | Validation hint, e.g. "email", "date" (YYYY-MM-DD), "uri". |
Examples
Product search (keyword only):
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Product search keyword — e.g. \"phone\", \"laptop\", \"Apple\", \"Samsung\""
}
},
"required": ["query"]
}
Used with URL https://dummyjson.com/products/search?q={query}&limit=5 and GET: the query value goes into the URL placeholder. Nothing ends up in the body.
Order lookup by ID:
{
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Order ID the customer is asking about, usually 6 to 10 digits. Ask the customer if not provided."
}
},
"required": ["order_id"]
}
Used with URL https://api.example.com/orders/{order_id} and GET.
Booking with multiple required fields:
{
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Product ID returned by a previous search_products call."
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"description": "How many items to reserve. Default 1."
},
"delivery_method": {
"type": "string",
"enum": ["pickup", "home_delivery", "locker"],
"description": "How the customer wants to receive the item."
},
"customer_email": {
"type": "string",
"format": "email",
"description": "Customer's email for order confirmation. Ask if not provided."
}
},
"required": ["product_id", "delivery_method", "customer_email"]
}
Used with POST https://api.example.com/reservations: the four values go into the JSON body. The AI will ask the customer for missing required fields before calling the tool.
Tool with no parameters:
{
"type": "object",
"properties": {}
}
Use this when the endpoint is fully static (e.g. GET /store/hours) and the AI doesn't need to pass anything.
Best practices for voice agents
- Keep the schema flat. Nested objects and arrays work, but the AI can slip when speaking on a phone. Prefer 3–5 top-level fields maximum.
- Always write a
descriptionfor every field. Include examples (e.g. "phone", "laptop") — examples guide the AI more reliably than abstract definitions. - Use
enumwhenever you have a fixed list of values. It removes the risk of the AI inventing values or sending"Electronics"instead of"electronics". - Mark a field
requiredonly if the tool cannot work without it. Everything else is optional, and the AI will skip it when the customer didn't mention it — no awkward extra questions. - Use
snake_casenames and match any{placeholder}tokens in the URL exactly. - Document behavior for missing values in the
description— e.g."Omit if no budget limit","Default 5".
Post-Call Actions
Post-call actions fire once the call has ended and the analysis chain has produced the summary, sentiment, and extracted variables. They consume call data — they don't talk to the customer.
Available Actions
| Action | Purpose |
|---|---|
| Send Email | Email a structured summary to your team or customer |
| Send SMS | Text confirmation to the caller |
| Send WhatsApp | WhatsApp message or template (works inside and outside the 24-hour window) |
| API Call | Push the full call payload to an external API (CRM, webhook, your data warehouse) |
Send Email
| Setting | Description |
|---|---|
| Name | Required. Action identifier |
| Subject | Required. Email subject line |
| Message Body | Required. Email body — can include retrieval variables |
| Trigger Condition | When to send (empty = always) |
| Recipients | Required. Email addresses that always receive the email |
| Conditional Recipients | Condition-to-recipient mapping |
Using variables in email:
New lead from phone call:
Name: {{customer_name}}
Email: {{customer_email}}
Interested in: {{selected_plan}}
Notes: {{call_notes}}
Send SMS
| Setting | Description |
|---|---|
| Name | Required. Action identifier |
| Sender Name | Displayed sender name |
| Message | Required. SMS content (can include variables) |
| Trigger Condition | When to send |
| Recipients | Required. Phone numbers that always receive the SMS |
| Conditional Recipients | Condition-to-number mapping |
Send WhatsApp
WhatsApp messaging on HANC uses pre-approved templates from a central Twilio Content account — you do not paste a Template SID by hand. The action editor shows a dropdown of every template that's currently active and approved, and you pick one. Placeholders inside the template ({{1}}, {{2}}, …) are then filled inline from call variables or static text you map in the editor.
| Setting | Description |
|---|---|
| Name | Required. Action identifier |
| Trigger Condition | When to send |
| Recipients | Required. Phone numbers that always receive the message |
| Conditional Recipients | Condition-to-number mapping |
| Template | Required. Dropdown picker of pre-approved WhatsApp templates synced from the central Twilio Content account. Each entry shows the template name, language, and a preview of the body so you know which one to pick. |
| Template Variables | For the template you picked, the editor lists each placeholder ({{1}}, {{2}}, …) and lets you map it to a call variable (see below) or a static string. |
Available call variables you can map into template placeholders:
| Variable | Description |
|---|---|
{{call_from}} | Caller's phone number |
{{call_to}} | Number that was called |
{{call_summary}} | AI-generated call summary |
{{call_sentiment}} | Sentiment (positive/neutral/negative) |
{{call_task_achieved}} | Whether the call task was achieved |
{{call_transcription}} | Full call transcription |
WhatsApp requires every business-initiated message outside the 24-hour customer-service window to use a pre-approved template. HANC syncs the list of approved templates from the shared Twilio Content account, so the dropdown always shows exactly what's eligible to send right now — you can't accidentally pick a draft, a rejected template, or an SID that doesn't exist. To add a new template, contact support; once it's approved by WhatsApp it appears in the dropdown automatically.
API Call
The post-call API Call action is your generic webhook into the rest of your stack. Pick the method, set the URL, and we'll send the entire call payload — your endpoint receives a structured JSON object describing what happened.
| Setting | Description |
|---|---|
| Name | Required. Action identifier |
| Trigger Condition | When to fire (empty = every call). Evaluated by an LLM against the transcription. |
| API URL | Required. API endpoint URL |
| HTTP Method | Required. GET, POST, PUT, DELETE, PATCH |
| Headers | Optional request headers |
| Query Parameters | Optional query string parameters |
What your endpoint receives
For POST / PUT / PATCH, your endpoint gets a JSON object in the request body. Your configured body params are merged with the full call payload:
{
"call_from": "+431234567890",
"call_to": "+439876543210",
"direction": "inbound",
"call_type": "phone",
"call_status": "ended",
"start_timestamp": 1730000000000,
"end_timestamp": 1730000187000,
"duration": 187000,
"transcription": [
{ "speaker": "agent", "content": "Hello…", "timestamp": 1730000001000 },
{ "speaker": "user", "content": "Hi…", "timestamp": 1730000003000 }
],
"call_summary": "Customer asked about pricing…",
"task_achieved": true,
"sentiment": { "sentiment": "positive", "explanation": "…" },
"custom_analysis_data": {
"name": "John",
"email": "john@example.com"
},
"collected_data": { /* in-call form submissions */ },
"transfer_history": [ /* if any agent transfer happened */ ],
"recording_url": "https://…",
"disconnection_reason": "user_hangup",
"is_anonymous": false,
"is_simulation": false,
"created_at": 1730000000000,
"updated_at": 1730000187000
}
For GET / DELETE, the same fields are flattened into the query string — but nested values like transcription, sentiment, and custom_analysis_data are dropped (URLs can't carry structured data sanely). Use POST/PUT/PATCH if you need the transcript.
Every request also gets an X-Correlation-Id header for tracing, and times out after 30 seconds.
- Pre fetch templates
{phone}etc. into URL/headers/query/body. Returns into the prompt, before the call. - Post call API Call sends the whole call dump in body or query. No URL templating — your endpoint gets static URL + dynamic body.
Retrieval Variables
Retrieval Variables are custom data fields that the AI automatically extracts from conversations. For example, the agent can capture the caller's name, email, phone number, or any other information you define.
Default Variables
Every new agent is created with two default retrieval variables:
| Variable | Type | Description |
|---|---|---|
| Caller's email address | ||
| Phone | Phone | Caller's phone number |
These are enabled by default and shown in the call widget form. You can edit or remove them, and add your own custom variables.
Variable Types
| Type | Use Case | Example |
|---|---|---|
| Text | Names, addresses, notes, free-form input | Customer name, delivery address |
| Number | Quantities, budgets, IDs | Order quantity, budget amount |
| Email addresses with validation | Customer email | |
| Phone | Phone numbers with validation | Customer phone number |
| Selector | Choice from predefined options | Preferred plan (Basic/Pro/Enterprise) |
| Checkbox | Yes/no consent or confirmation | "I agree to receive marketing emails" |
Configuring a Variable
| Field | Description | Example |
|---|---|---|
| Variable Name | Required. Variable identifier | customer_email |
| Instructions for AI | Required. Instructions for the AI on when and how to extract this value | "The customer's email address. Ask if not provided." |
| Example Format | (Optional) Example of expected format | "john@example.com" |
| Options (for Selector) | (Selector only) List of allowed options | ["Basic", "Pro", "Enterprise"] |
| Show in Form | Whether to display this field in the call widget form | Enabled by default |
Show in Form
When Show in Form is enabled, the variable appears as a visible input field in the web widget before and during the call. This lets callers fill in their information directly, in addition to the AI extracting it from conversation.
The AI will naturally ask for missing information during the conversation. Set a clear description like "Customer's email address, ask politely if not provided" and the agent will handle it.
Adding Tools & Actions
- Navigate to your agent's Actions tab.
- Click Add Tool.
- Pick the right phase from the dropdown — Pre fetch, Live call, or Post call.
- Configure the settings.
- Save — changes apply on the next call.
All entries are listed together in the Actions table. Click any row to edit, or use the trash icon to delete.
Related
- Voice Agents Overview
- Prompt Engineering — Reference tools in your prompt
- Knowledge Base — Information sources
- Integrations — External systems your tools can talk to