Home / Blog / Guides
Guides

Connect your AI agent to your own systems: tools, APIs and webhooks

How to give a voice agent a tool that calls your API. Every field in the tool editor, the exact JSON your endpoint receives, and what to send back.

F Foan Team / Published May 27, 2026 / Updated Sep 19, 2026 / 9 min read
Rows of server racks in a machine room, receding into the distance

A voice agent is only as useful as the systems it can reach. In Foan you connect it with tools: named actions you define on the agent, each pointed at one HTTPS endpoint you own. The agent decides when an action is needed, collects the facts it requires, POSTs them to your URL, and reads your reply back to the caller.

This guide covers every field in the tool editor, the exact payload your endpoint will receive, the response contract, and the wider API surface around it.

What is a tool, in one sentence?

A tool is a promise to the model: when the conversation reaches this situation, gather these specific facts and send them to this URL. Everything else follows from that. The name and instructions decide when the tool fires. The questions decide what arrives. The webhook decides where. The behaviour settings decide what the caller hears while it happens.

Open the agent, expand the Integrations accordion, and add a tool. The editor has four sections: Basics, Questions, Webhook and Behavior. Work through them in order.

Two columns listing what you declare on a tool against what Foan sends to your endpoint anyway
Half the fields people declare are already in the payload. Check before you ask the caller.

Step 1: Name the action and say when to use it

Action name is what a human reads. The placeholder is Book Event. From it, Foan generates the snake_case tool name the model actually sees, matching ^[a-zA-Z][a-zA-Z0-9_]{0,63}$, so Book Event becomes book_event.

Instructions is the trigger condition, and it is the field people underwrite. It is not a description of your API. It is guidance to the model about the moment this tool belongs in. The placeholder shows the right shape:

Use this tool when the user wants to book an event.
Collect the event name, date, and time before calling.

Two rules make this work. Name the trigger, not the mechanism: write "when the caller asks where their order is", not "queries the orders service". And name the preconditions, so the model gathers what it needs before firing rather than calling with blanks and asking follow-up questions afterwards.

Remember that this text competes for the same budget as the rest of the agent's prompt. All customer-authored instruction fields on an agent share a 4,000 character ceiling, with a warning at 3,000. Tool instructions are part of that total, which is another reason to keep each one to two or three lines.

Step 2: Decide what the agent must collect

Each Question in the tool becomes one field in the payload. A question has a prompt the agent may ask, an Answer key that names the field, and a Type.

TypeStored asUse it for
TexttextNames, order references, free description
Date & timedatetimeAppointments and slots, with a Default timezone
Yes / NoyesnoConfirmations and consent
ChoicechoiceA fixed set, listed in Options (comma-separated)
Phone numberphoneA number that is not the caller's own
List of textlist_textSeveral items, such as a multi-item order

Two fields matter more than the rest.

Mapped value supplies a value instead of asking for it. The hint reads "Use a specific value instead of asking the user", and the placeholder is e.g., {{participantName}} or a fixed value. Anything you can fill from a custom variable or a constant should go here. Every question you map is one question the caller never has to answer.

Required decides whether the model will chase a missing answer before it calls. Mark only what your endpoint genuinely cannot work without. A required field the caller cannot easily supply turns a thirty second call into a two minute interrogation.

You will want a live agent to point at while you read this, so create a free account and build the tool as you go. Phone agents covers the call mechanics around the tool, and the developer reference has the payload in full. A tool that takes two seconds to answer also changes what the call costs, which pricing explains.

Step 3: Point the tool at your endpoint

The Webhook section is short, and its limits are worth knowing before you design the endpoint.

  • Webhook URL, which must start with http or https.
  • Bearer token, optional. When set, it is sent as an Authorization: Bearer header.
  • Timeout (ms), default 10000.
  • Retries, default 0.

The method is fixed to POST. There is no method picker and there is no custom headers field, so a bearer token is the only credential a tool can carry. If your API expects a key in a custom header or a query string, put a small adapter in front of it that accepts the bearer token and rewrites the request. Do not weaken your API to fit.

Design to those defaults rather than around them. Ten seconds is a long time on a live call and a short time for a cold database query. If a lookup can take longer, return a fast acknowledgement and do the slow part asynchronously.

Step 4: Set what the caller hears while it runs

The Behavior section is the difference between a tool that feels deliberate and one that feels like dead air.

Confirm before running is on by default, with a confirmation question that reads "Just to confirm, should I do this now?". Keep it on for anything that writes, charges or cancels. Turn it off for a read-only lookup, where a confirmation step just adds a turn.

Prevent duplicates is on by default. Foan sends an Idempotency-Key header derived from the call id, the tool name and a hash of the answers, so a repeated call with identical answers can be recognised on your side. Store that key and return the original result when you see it again. This is the cheapest protection you will get against a double booking.

Then three lines of speech, with these defaults:

  • Say while running: "Please wait while I process your request..."
  • Say on success: "Done! Your request has been processed."
  • Say on failure: "Sorry, something went wrong. Please try again."

Rewrite all three in your own voice. The failure line in particular should tell the caller what happens next, because it is what they hear when your endpoint times out.

One hard limit to plan around: an agent can hold a maximum of five function tools. Five well-scoped actions beat eight overlapping ones anyway, but if you are pushing the cap, collapse related actions into a single endpoint that branches on a choice answer.

A round trip diagram showing caller speech reaching the agent, a POST to your API, and the answer spoken back
One tool call, end to end. The filler line covers the part in the middle.

What does Foan actually POST?

This is the body your endpoint receives:

{
  "tool_name": "book_event",
  "args":    { "event_name": "Diwali dinner", "guests": "6" },
  "answers": { "event_name": "Diwali dinner", "guests": "6" },
  "call_context": {
    "call_id": "<room name>",
    "room_name": "<room name>",
    "org_id": "...",
    "agent_id": "...",
    "participant_number": "+919740891516",
    "participant_name": "...",
    "plivo_call_uuid": "..."
  }
}

args and answers carry the same object. Mapped values are merged in and override anything the model produced, so a mapped field is authoritative. Read whichever you prefer.

The headers that come with it:

Content-Type: application/json
User-Agent: foan-bridge/1.0
Authorization: Bearer <token>      only when a bearer token is set
Idempotency-Key: <sha256>          only when Prevent duplicates is on

What should your endpoint return?

Reply with JSON containing a result string. That string is what the model receives, and it is the basis of what the caller hears:

{ "result": "Booked for 7:30pm on 8 November. Reference RG-1234." }

If the body has no result key, the whole JSON body is handed to the model as a string, which usually means the agent reads out something shaped like a database row. Always send result, and write it as a sentence rather than a record.

A minimal handler:

app.post("/foan/book-event", async (req, res) => {
  const { answers, call_context } = req.body;

  const booking = await createBooking({
    title: answers.event_name,
    guests: Number(answers.guests),
    phone: call_context.participant_number,
    idempotencyKey: req.get("Idempotency-Key")
  });

  res.json({ result: `Booked for ${booking.time}. Reference ${booking.ref}.` });
});

On an HTTP error or a timeout, the agent speaks the Say on failure text and the conversation continues. It does not stall and it does not hang up. That is forgiving behaviour, and it is also why a silent 500 can go unnoticed for days: log your own failures, because the call will not surface them for you.

What does the agent already know without asking?

The caller's number. It is built once per call and sent on every tool webhook as call_context.participant_number.

This matters because the most common tool design mistake is declaring a required phone question. The agent then dutifully asks a caller to read out the number they are calling from, mis-hears a digit, and the lookup fails. Never declare a required phone question on a tool that runs during a call. Use call_context.participant_number and reserve the phone type for a different number, such as an alternate contact for a delivery.

The same principle applies to participant_name and any custom variables you have set on the agent. Anything already in context should be mapped, not asked. For an order status agent, that single change is often the whole difference between a workable call and an annoying one, as D2C order support on the phone goes into.

What should never go in a tool?

Two things.

Do not put a secret in a place a tool can expose. The bearer token is stored with the tool configuration, and on a chat agent the widget token is public by design, which means the setup of every tool the agent calls is readable by anyone viewing the page source. Give tools a scoped credential that can do exactly one thing, and nothing else.

Do not use a tool as a substitute for knowledge. Static facts such as opening hours, policies and menus belong in a knowledge base, where the agent reads them without a network round trip. Tools are for the things that change per caller: an order, a slot, a balance, a booking.

If you would rather see a finished example than a spec, wiring a where-is-my-order agent to a real order API walks through the same machinery end to end, and turn taking explains why the filler line matters so much while your endpoint is thinking.

Beyond tools: the REST API, the CLI and MCP

Tools are the agent reaching out. The rest of the platform is you reaching in.

The public API lives at https://api.foan.ai/api/v1, authenticated with an API key as a bearer token. Every response uses the same envelope:

curl https://api.foan.ai/api/v1/voice/agents \
  -H "Authorization: Bearer $FOAN_API_KEY"
{ "success": true, "data": {}, "message": "..." }

Create keys under Developers, then API Keys, and pick scopes deliberately. Scopes are granular, covering reads and writes separately across calls, recordings, agents, campaigns, numbers, knowledge, billing and the wallet. A key that only needs to place a call should hold calls:create, not agents:write.

For scripting there is the foan-ai npm package, which installs a foan binary and supports both OAuth login and API-key auth:

npm install -g foan-ai
foan login
foan agents list
foan calls list

And for driving Foan from an AI coding tool, there is a hosted MCP server at https://api.foan.ai/mcp, over Streamable HTTP, authenticated with the same API key as a bearer token or with OAuth 2.1. It exposes agents, campaigns, calls, knowledge bases, numbers and account operations as tools. Destructive operations return a dry-run preview first and only act when you confirm.

One honest limitation: there is no customer subscribable call completed webhook today. To get transcripts, outcomes and recordings into your CRM or warehouse, pull them from the call history endpoints on a schedule, using the API, the CLI, or the MCP server. Plan for a pull, not a push. The full endpoint reference is on the developers page.

Where to go next

Build the agent first and add tools once it holds a decent conversation without them, then give it the static facts it needs in a knowledge base it will not hallucinate from. For what a tool-equipped agent can do on a live line, see phone agents.

Try it on your own number

Build an agent, point a number at it and listen to the first call.

Frequently asked questions

Can a tool webhook use any HTTP method?
No. Tool webhooks are POST only. There is no method picker and no custom header field, so the only credential a tool can carry is an optional bearer token sent in the Authorization header.
How many tools can one agent have?
Five function tools per agent, enforced in the editor. If you need wider coverage, merge thin actions into one endpoint that branches on an argument rather than adding a sixth tool.
Does the agent have to ask the caller for their phone number?
No, and you should never make it. The caller's number arrives on every tool call as call_context.participant_number, so a required phone question only wastes conversation time and invites transcription errors.
What happens if my API is slow or returns an error?
The timeout defaults to 10000 ms and retries default to 0. On a timeout or an HTTP error the agent speaks the say on failure line you wrote and continues the conversation, so write that line as something the caller can act on.
How do I get call transcripts and outcomes into my own system?
Pull them from the call history endpoints on the REST API, the foan CLI, or the hosted MCP server. There is no customer subscribable call completed webhook today, so run it as a scheduled pull rather than waiting for a push.
Can I test a tool without making a phone call?
Point the webhook at a request inspector first and use the Web Call button in the agent's Call section to drive the conversation from your browser. You will see the exact body before you wire it to anything that writes data.
Get started

Voice agents that pick up.
On the first ring.

Create an agent, attach a number or forward your existing line, and hear it answer. Usage-based pricing, no setup fee.