AI Agent Custom Integrations (Mid-Call Tool Webhooks)
A custom integration is a tool an AI agent can invoke during a live conversation. The agent collects a defined set of fields from the caller, the platform POSTs them as JSON to an HTTPS endpoint you host, and your endpoint’s JSON reply is handed straight back to the AI as the tool result — the agent reads it and speaks from it. One mechanism covers both directions:
- Submit (push): the agent sends data to your system — a lead, a booking request, a work order. Your endpoint stores it and replies with a confirmation the agent speaks.
- Lookup (pull): the agent fetches data from your system — an order status, an account balance, an appointment slot. Your endpoint replies with the data, and the agent answers the caller with it.
Custom integrations are configured on an agent by Cloudspire staff (PBX > AI > AI Agents > edit agent > Tools card). This page is the contract your developers build the receiving endpoint against.
How It Works
- Each integration is defined by a tool name, a description (the AI decides when to call the tool from this text), a field list (what the AI collects from the caller before calling), and your endpoint URL.
- The tool is offered to the AI as a callable function for the whole conversation. When the conversation reaches the point the description covers — “use this to submit a service request once you have the caller’s name, address, and issue” — the AI gathers any missing fields conversationally, then invokes the tool.
- The platform POSTs the collected fields to your endpoint (see the request contract below). While the request runs, the agent can speak a configured filler line (“One moment while I get that submitted…”).
- Your endpoint replies with JSON. That reply becomes the tool result the AI reads next — so whatever you return is what the agent knows and can say. On failure (timeout, non-2xx, unreachable) the AI instead receives a speakable failure and recovers the conversation (“I’m having trouble reaching that system right now”).
Request Contract
One HTTPS POST per tool invocation, Content-Type: application/json:
{
"name": "submit_booking",
"args": {
"caller_name": "Jane Smith",
"party_size": 4,
"requested_time": "2026-08-01 19:00"
},
"call": {
"call_id": "sip1-atl-1721857200.482",
"agent_id": 42,
"from_number": "+14045551234",
"to_number": "+14045550100"
}
}
| Field | Description |
|---|---|
name | The tool name as configured on the agent. One endpoint can serve several tools by switching on this value. |
args | The fields the AI collected, keyed exactly as the integration’s field list defines them. Types follow the field definitions (string / integer / number / boolean). Optional fields the caller did not provide may be absent. |
call.call_id | The stable per-call identifier. For voice calls the same value identifies the call in Result Webhook and campaign result deliveries, so you can join a mid-call submission to its post-call summary. Text conversations (web chat, SMS) may send an empty call_id here; their Result Webhook deliveries carry their own chat_id, so correlate text submissions by your own fields (phone number, your record id) instead. |
call.agent_id | Numeric ID of the AI agent that made the tool call. |
call.from_number / call.to_number | The call’s origin and destination (E.164), following the call’s direction: on an inbound call, from_number is the caller and to_number is the business number they dialed; on an outbound (campaign) call, from_number is the business number the agent is calling from and to_number is the person being called. Result Webhook bodies use the same semantics, so the parties always agree for one call_id. For web chat and SMS conversations these may be empty or carry the texting number. |
This body is byte-compatible with the Retell AI custom-function contract, so an endpoint originally written for a Retell agent works unchanged.
Response Contract
Reply 200 (any 2xx) with a JSON body. The body is given to the AI verbatim as the tool result — design it as the information you want the agent to have next:
Submit example — confirm what happened so the agent can close the loop:
{ "result": "Booked. Confirmation number A-1042. A text confirmation is on its way." }
Lookup example — return the data; the agent phrases it naturally:
{ "order_status": "shipped", "carrier": "UPS", "eta": "Friday, August 1" }
- Keys and structure are yours to choose — the AI reads whatever JSON you return. Short, flat, human-meaningful values work best; the agent will paraphrase them.
- A non-JSON body is wrapped as
{"response": "<raw text>"}and still reaches the agent. - To steer the agent, return an instruction as data:
{ "error": "no appointment slots that day", "suggest": "offer the next morning instead" }. - Lookup freshness: the same duplicate suppression that protects submissions also applies to lookups — an identical call (same conversation, tool, and arguments) within the ~15-minute window returns the first recorded result rather than re-querying your system. Mid-conversation that is almost always what you want; if a lookup must always be live, include a field whose value naturally varies (the AI re-asks with different arguments, e.g. an as-of time your endpoint ignores).
Delivery Rules
| Rule | Detail |
|---|---|
| HTTPS only, public host | The endpoint must be https:// on a publicly-routable host. Private/internal addresses are rejected before any request is sent. |
| Success is 2xx only | Redirects are not followed — a 301/302 counts as failure (the POST body would not reach the destination). Configure the final URL. |
| Timeout | ~20 seconds total (5 s to connect). A caller is waiting on the line — answer fast, and do slow work asynchronously after replying. |
| Response size | Up to 256 KB. Larger replies abort and the tool fails. |
| Duplicate suppression (best-effort) | The platform never re-sends a request on its own, and an identical retry by the AI (same conversation, tool, and arguments) within a ~15-minute window is suppressed on a best-effort basis: it receives the recorded result, or a refusal when the first attempt’s outcome is unproven (a timeout after your side may have committed). This suppression is not a distributed lock — a narrow race can let two identical requests through. Endpoints that perform irreversible actions must enforce their own idempotency (call_id plus a key derived from the submitted fields); treat platform-side suppression as an optimization, not a guarantee. |
| Failure is graceful | On timeout / non-2xx / unreachable, the agent receives a speakable failure result — the call continues; nothing crashes. |
Authenticating Requests
Mid-call tool POSTs are not signed (the contract is wire-compatible with Retell custom functions, which carried no signature either). Authenticate by putting a secret in the endpoint URL you give Cloudspire staff — a token query parameter or an unguessable path:
https://example.com/hooks/ai-intake?token=8f4c...e2a1
Reject requests whose token does not match, and treat the URL as a credential: serve it over HTTPS only (always true here), keep it out of client-side code, and rotate it with Cloudspire staff if it appears in access or proxy logs you do not control. Because possession of the URL is the whole credential, endpoints that perform irreversible actions should add their own idempotency key (call_id plus your own dedup) and sanity limits. If you need cryptographically signed deliveries, the post-call Result Webhooks surface signs every payload with a per-URL secret; if signed mid-call deliveries would unblock your integration, tell your Cloudspire contact.
Two-Way Data Access: the Full Picture
Custom integrations are the mid-call lane. The complete set of ways data moves between your systems and an AI agent:
| Direction | When | Mechanism |
|---|---|---|
| Into the agent | Call start | Dynamic variables — caller number and CNAM are injected automatically; outbound campaign calls carry the per-call variables you queued (see the AI Outbound Calling API). |
| Into the agent | Mid-call | Custom integration (lookup) — this page. The agent asks your system a question and speaks the answer. |
| Into the agent | Any time | Knowledge base articles — curated content the agent searches during the conversation. |
| Out of the agent | Mid-call | Custom integration (submit) — this page. The agent pushes collected data to your system while the caller is still on the line. |
| Out of the agent | After the conversation | Result Webhooks — outcome, transcript, sentiment, and summary POSTed to your URLs, signed. Reference. |
| Out of the agent | After an outbound campaign call | Campaign result callback — the per-call result POSTed to the webhook_response_url you supplied when queueing. Reference. |
Worked Example: a Booking Tool
Configuration on the agent (staff-entered):
| Tool name | submit_booking — letters, numbers, underscores, dashes. This is the function name the AI calls and the name your endpoint receives. |
| What it does | “Submit a table reservation once you have the caller’s name, party size, and requested time. Use only after confirming all three back to the caller.” — the AI decides when to invoke the tool from this text, so write it as an instruction about when and with what. |
| Fields | caller_name | string | required | Caller's full nameparty_size | integer | required | Number of guestsrequested_time | string | required | Requested date and time — these become the JSON Schema the AI fills; each description tells the AI what to ask for. |
| Endpoint URL | https://example.com/hooks/bookings?token=… — your system. |
| While it runs | “Let me get that booked for you.” — spoken during the request so the caller never hears dead air. |
The caller says “table for four Friday at seven, name’s Jane” — the AI confirms the details, invokes submit_booking, your endpoint books it and replies {"result":"Booked, confirmation A-1042"}, and the agent says “You’re all set — your confirmation number is A-1042.”