Call2Me
Developers

Functions: give your agent tools to call

Let a voice agent invoke your APIs mid-call — book a meeting, look up an order, transfer to a human. The function-calling protocol, parameter schemas, and the patterns that keep latency low.

Updated May 6, 2026

Functions are how a voice agent does anything beyond talking — book a meeting, look up an order, fire a webhook, transfer to a human. You define the tool; the LLM decides when to call it; the platform handles the I/O.

The mental model

A function is just an HTTP endpoint plus a JSON Schema describing its parameters. The LLM reads the schema, the user's intent, and decides when calling makes sense:

Caller: "What's the status of order 4521?"
LLM:    decides to call get_order_status({"order_id": "4521"})
Platform: POSTs to https://your-api.com/order-status with the args
Your API: returns {"status": "shipped", "eta": "2026-05-08"}
Platform: hands the result back to the LLM
LLM:    "Your order is shipped, expected to arrive May 8."

The agent narrates a filler line ("let me check that for you") while the HTTP call is in flight, so the silence doesn't feel like a hang.

Defining a function

Functions live inside the agent's response_engine.functions array, so you define them by updating the agent:

curl -X PATCH https://api.call2me.app/v1/agents/agent_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "response_engine": {
      "type": "call2me-llm",
      "functions": [
        {
          "name": "get_order_status",
          "description": "Look up the shipping status of an e-commerce order by ID.",
          "url": "https://your-api.com/order-status",
          "method": "POST",
          "parameters": {
            "order_id": {
              "type": "string",
              "description": "The order number, digits only",
              "required": true
            }
          },
          "headers": {
            "Authorization": "Bearer your-own-token"
          }
        }
      ]
    }
  }'

Or in the dashboard: Agents → Edit → Functions → New Function.

What each field does

FieldPurpose
nameInternal identifier; what the LLM uses to call it. Must match ^[a-zA-Z0-9_-]{1,64}$.
descriptionThe LLM reads this to decide when to call. Be explicit. Capped at 1024 characters.
urlYour endpoint (HTTPS only, public address — private/internal IPs are rejected)
methodGET, POST, PUT, or PATCH. Defaults to POST.
parametersA flat map of parameter name → {type, description, required, enum}. Not JSON Schema — the platform builds the schema for you.
headersOptional headers sent with your request, for your own auth. Host, Content-Length, and User-Agent are ignored.

Note that parameters is a plain object keyed by parameter name, and each parameter marks itself required. Wrapping it in a JSON Schema ({"type": "object", "properties": {...}}) will not work — the platform would read type and properties as parameter names.

Limits worth knowing

These are enforced by the platform, not configurable per function:

  • 8 second timeout. Slower than that, the agent tells the caller the lookup failed and moves on.
  • 10 function calls per conversation. A guard against LLM loops.
  • 1500 characters of response. Anything longer is truncated before reaching the LLM. Return a summary, not a full record dump.

There is no per-function filler-phrase field. To control what the agent says while waiting, instruct it in the system prompt — for example, "when you call get_order_status, first tell the caller you're checking."

What your endpoint receives

POST https://your-api.com/order-status
Content-Type: application/json
User-Agent: Call2Me-VoiceAgent/1.0
Accept: application/json, text/plain, */*

{
  "order_id": "4521"
}

Only the arguments the LLM filled in are sent — plus any headers you configured. If you need the call or agent ID on your side, declare it as a parameter and tell the agent to pass it, or subscribe to webhooks where the full call context is included.

For a GET function the arguments go in the query string instead of the body.

Your endpoint returns a JSON object. Whatever you return becomes available to the LLM:

{
  "status": "shipped",
  "carrier": "UPS",
  "tracking_number": "1Z999AA10123456784",
  "eta": "2026-05-08"
}

The LLM reads the result, summarizes it for the caller, and continues the conversation.

Built-in functions

Some functions are provided by the platform and don't require an endpoint:

FunctionWhat it does
end_callSpeak a closing line and hang up
transfer_callHand the active call off to a department or number
hold_callPut the caller on hold with music while something is checked
dtmf_inputCollect keypad digits — a PIN, an extension, a menu choice

These are enabled by name, not defined with a schema. Turn them on in the dashboard, or via response_engine.builtin_functions:

{
  "response_engine": {
    "type": "call2me-llm",
    "builtin_functions": ["end_call", "transfer_call", "dtmf_input"]
  }
}

A built-in that isn't in this list is inert — if the LLM tries to call it, it gets Function not enabled back. For end_call, the goodbye line comes from the agent's end_call_message if you set one; otherwise the platform uses a default in the agent's language.

Sending an SMS mid-call is not a built-in. Use the SMS API from a custom function, or configure a post-call notification.

Latency budget

Phone conversations are unforgiving. Aim for:

  • < 500ms — feels instant, no filler needed
  • 500ms–1.5s — a filler line from the system prompt bridges it cleanly
  • 1.5s–4s — feels slow; consider a longer filler with confidence
  • > 4s — fix the underlying API or pre-fetch ahead of the call

For inbound calls where you know the caller's identity from the number, pre-fetch their data into dynamic_variables instead of calling mid-conversation.

Error handling

The platform never raises an exception into the conversation. Whatever goes wrong, the LLM receives a plain-text result it can act on:

Error: webhook timed out after 8.0s — please try a different option
Error: webhook unreachable
Webhook returned status 503: Service Unavailable
Error: called too quickly — please wait a moment

Non-2xx responses pass your response body through (first 200 characters), so a useful error message from your API reaches the model — a 409 reading "That slot was just taken" lets the agent offer another time instead of apologizing vaguely.

Your agent prompt should know what to do:

"If a function call fails, apologize briefly and offer to transfer the caller to a human."

This is the pattern that keeps a single backend hiccup from torpedoing the conversation.

What's next

  • Agents — where functions are configured
  • Webhooks — pull-style events vs. push-style functions
  • Calls — how function results show up in the call object

Frequently asked

Q.What's a function in voice AI?

A callable tool the agent can invoke during a conversation. You define name, description, and a parameter map; the LLM decides when to call it; the platform executes the HTTP call to your endpoint and returns the result back into the conversation.

Q.How fast does a function call have to return?

Under 500ms is ideal — instruct the agent in its system prompt to say a brief filler ('let me check that') while the call is in flight. Above 1.5s you'll start to feel awkward pauses, and the platform gives up at 8 seconds.

Q.Can a function transfer the call to a human?

Yes. transfer_call is a built-in function — enable it in the agent's builtin_functions list and configure the destination on the agent. You don't have to implement the carrier side.

Q.What if my function endpoint fails?

The platform hands the LLM a plain-text error instead of raising, and passes through the first 200 characters of your response body. Your agent prompt should tell the model what to do on failure — usually 'apologize and offer to transfer to a human' or 'try again once.'

ShareX / TwitterLinkedIn

Ready to ship?

Spin up your first agent in 5 minutes — $5 free credit.

Build your voice agent in 10 minutes

No code. No credit card. Just a phone number.

Get Started FreeTalk to Sales
Try the voice agent liveLive Demo