Custom tools — call your own APIs
The built-in tools (capture lead, schedule callback, transfer to human, etc.) cover the common cases. Custom tools cover everything else — any HTTP API you can describe with a URL and a few fields, the agent can call mid-conversation.
A few real examples customers ship:
- “How many credits do I have left?” → agent calls
GET https://api.yourapp.com/v1/users/{{user_id}}/balanceand reads the number back. - “Where’s my order?” → agent calls
GET https://api.yourapp.com/v1/orders/{{order_id}}and reads off the status, ETA, and tracking link. - “Book me an appointment for tomorrow” → agent calls
POST https://api.yourapp.com/v1/appointmentswith date + service. - “Update my mobile number to …” → agent calls
PATCH https://api.yourapp.com/v1/profile.
You configure each tool once on the agent’s Tools tab → Custom tools section. The agent decides when to fire each one based on the description you write — same as the built-in tools.
How it works (end-to-end)
- You define the tool — name, description, HTTP method, URL, headers, request fields, response mapping.
- The agent connects at call time and the platform passes your tool definitions into the call session as part of the room metadata.
- The agent registers your tools as functions the LLM can call. The LLM sees the tool’s name + description + parameters — that’s how it decides when to fire it.
- During the call, when the LLM decides to call your tool, the
platform substitutes any
{{placeholders}}in the URL/headers/body with the values the LLM filled in, then makes the HTTP request on your behalf. 10-second timeout. - The response comes back to the LLM — either the mapped attributes (when you’ve configured response mapping) or the raw JSON body, trimmed.
- The LLM speaks the result to the caller in natural language.
You can hear the tool fire on your dashboard’s Activity tab, and the call recording will reflect the agent reading back whatever your API returned.
Setup walkthrough
1. Open the Tools tab
Go to AI Agents → [your voice agent] → Tools.
Scroll past the built-in tool list to Custom tools. Click + Add tool.
2. Name the tool
Give it a snake_case name like check_balance, get_order_status,
or book_appointment. This is what the LLM uses internally to call
it; not what the caller hears.
Names must be unique across the agent’s custom tools.
3. Describe what it does
The description is the most important field — the LLM reads it to decide whether to fire your tool. Write it like a one-line spec:
“Looks up the caller’s current credit balance. Use when the caller asks how many credits / how much balance they have left.”
Be specific about when to use it. Vague descriptions lead to the LLM either never firing it or firing it too eagerly.
4. Set the API endpoint
Pick the HTTP method — GET, POST, PUT, PATCH, DELETE — and paste
the URL. Use {{field_name}} anywhere you want the LLM to fill in a
value at call time:
https://api.yourapp.com/v1/orders/{{order_id}}
https://api.yourapp.com/v1/users/{{user_id}}/balanceYou can also use these always-available context placeholders without declaring them as fields:
| Placeholder | Value |
|---|---|
{{call_id}} | The current voice call’s ID |
{{user_id}} | The Splashify Pro user ID |
{{agent_id}} | The voice agent’s ID |
{{contact_phone}} | The caller’s phone number |
{{contact_name}} | The caller’s saved contact name (if any) |
5. Add headers
Click + Add header for each. Common cases:
| Key | Value |
|---|---|
Authorization | Bearer sk_live_... |
X-API-Key | your-api-key-here |
Content-Type | application/json (added by default) |
Secrets paste in here and never leave the platform.
6. Define request fields
For each piece of data the agent should fill in, click + Add field:
| Column | What it controls |
|---|---|
| Name | Field name. Match the placeholder in the URL/body (e.g. order_id). |
| Location | Where the value goes: body, query, path, or header. |
| Type | string, number, or boolean. |
| Required | Forces the LLM to fill it before firing. |
| Description | What this field is for. The LLM uses this to fill it correctly — be specific. |
Or click Auto-detect — the platform parses your URL and pulls
out {{field}} placeholders + ?key=value query params + :param
path segments and seeds the field list automatically. You then refine
each row.
7. Map the response
When your API returns JSON, you usually only care about a few fields. Response attribute mapping lets you pick which keys the agent reads back:
| Attribute (what the agent sees) | Response key (JSON path into the API response) |
|---|---|
balance_inr | data.balance |
currency | data.currency |
expires_at | meta.subscription.expires_at |
The mapping uses dot-separated paths, including data.items.0.name
for arrays. Unmapped keys are dropped — the LLM won’t see them.
If you leave mappings empty, the LLM gets the raw JSON body trimmed to 500 characters. That works for simple responses but the LLM has to figure out which key matters.
8. Test it
Click ▶ Test on the tool card. The platform makes the HTTP request with no parameters filled in — just the literal URL + headers as configured. Use this to confirm authentication is working and the endpoint is reachable.
For a full end-to-end test (with the LLM filling in parameters), use Test Call at the top of the page and ask the agent the question that should fire your tool.
9. Toggle it on + Save
The right-most switch on the tool card enables / disables it. Saved tools that aren’t enabled stay in your config but the LLM doesn’t see them — useful for keeping work-in-progress tools around without exposing them.
Click Save tools at the bottom of the page.
Worked example: “How many credits do I have?”
You ship a service at https://api.yourapp.com that exposes
GET /v1/users/{user_id}/credits returning:
{
"data": {
"balance": 247,
"currency": "INR",
"expires_at": "2026-12-31"
}
}Configure the tool like this:
| Field | Value |
|---|---|
| Name | check_user_credits |
| Description | ”Looks up the caller’s credit balance. Use when the caller asks how many credits / what’s their balance / when their credits expire.” |
| Method | GET |
| URL | https://api.yourapp.com/v1/users/{{user_phone}}/credits |
| Headers | Authorization: Bearer sk_live_yourkey |
| Fields | user_phone (path, string, required, “The caller’s phone number”) |
| Mappings | balance ← data.balance, currency ← data.currency, expires_at ← data.expires_at |
During a call:
Caller: “Hey, do I have any credits left?”
Agent (decides to fire
check_user_creditswithuser_phone= {{contact_phone}}):Backend (you): replies with the JSON above.
Agent (reads mapped attributes back): “You have 247 INR in credits — they’re valid until December 31, 2026.”
Built-in security
- HTTPS-only URLs (HTTP allowed during dev, but logged as a warning).
- Request timeout: 10 seconds. Slow APIs return a polite “I couldn’t reach that service right now” so the call doesn’t stall.
- Tool firing is rate-limited per call — the LLM can’t loop on a single tool indefinitely.
- All tool fires are logged to your Activity tab with the request body and response body for replay / debugging.
- The LLM never sees your raw API keys — they live in the headers config, are sent server-side from the platform, and never leak into the call transcript.
Limits
| Limit | Value |
|---|---|
| Custom tools per agent | 20 |
| Headers per tool | No hard cap (keep it sane — 10 is plenty) |
| Request fields per tool | No hard cap |
| Response mappings per tool | No hard cap |
| Request timeout | 10 seconds |
| Response body considered | First 500 characters of trimmed JSON, OR all mapped values |
Common errors
| Symptom | Likely cause | Fix |
|---|---|---|
| Test returns “Network error” | URL unreachable from the platform | Confirm DNS resolves, firewall allows our egress IPs, endpoint is public-facing |
| Test returns HTTP 401 | Wrong auth header | Check the Authorization header value, refresh your API key if needed |
| Test returns HTTP 200 but the agent says “I couldn’t reach that service” | Firewall is blocking the platform’s egress | Whitelist 203.174.23.244 in your API gateway |
| Agent never fires the tool during a call | Description is too vague, OR the tool isn’t enabled | Rewrite the description to spell out when to fire it; confirm the toggle is on |
| Agent fires the tool with wrong values | LLM misunderstood the parameter description | Make field descriptions explicit (“Order ID — typically a 10-digit number starting with ‘ORD-’“) |
| Save fails with “duplicate tool name” | Two tools share a name | Rename one — names must be unique per agent |
What’s next
- Built-in tools — see Tools the agent can use for the platform-managed actions (capture lead, schedule callback, transfer to human, etc.).
- Knowledge base — for read-only lookups against your own documents, Knowledge base is faster than a custom tool because the data is pre-indexed.
- Test calls — run an end-to-end test before deploying. See Test call.