Custom tools
Custom tools are how you connect your assistant to anything Insighto doesn't ship a built-in for. Your in-house CRM. Your shipping carrier's tracking API. Your inventory service. A weather provider. The pricing engine your developers wrote last quarter. Define one or more HTTP-callable functions, describe each one with a JSON-schema spec, point Insighto at your endpoint, and the assistant can call your API exactly like it calls HubSpot or Google Calendar.
What you can do with a custom tool
- Expose your own API as one or more functions the assistant can call.
- Define the request shape — endpoint path, HTTP method, content type, parameters as JSON schema.
- Wire up authentication — static API key, dynamic token exchange, or no auth at all.
Business use cases
Wrap your in-house CRM
A logistics company built their own dispatcher app in-house years before they ever heard of HubSpot. The contact data is in their app's database, accessible via an internal REST API. They build a custom tool with functions lookup_shipper, lookup_consignee, and add_dispatch_note. The voice agent now does the same things a built-in CRM tool would — entirely against their own service. No data migration.
Wrap a shipping-carrier tracking API
An e-commerce company supports four carriers (UPS, FedEx, USPS, DHL). Each has its own tracking API. They build one custom tool that fronts a small adapter service on their side — the adapter normalizes responses across carriers. Function name: track_shipment with one argument, tracking_number. The assistant fields "where's my package?" questions and reads back the unified status and ETA — without knowing which carrier the package is on.
Order lookup with rich response
A small SaaS team writes a custom tool that calls /api/orders/{id} on their own platform and returns the order JSON. They iterate on the response shape ("include the customer's contact info in the response so the model has it for the next step") without having to wait for a built-in update from Insighto. They control the contract entirely.
Pricing engine query
A B2B procurement service has a pricing engine that's the heart of their business. It returns a quote given (SKU, quantity, customer tier, shipping region). They build a get_quote custom tool. The voice agent collects the four inputs from the caller, calls the tool, and quotes the price in seconds — same logic the human reps use, just faster.
How custom tools work
You provide:
- A base URL for your service.
- One or more function definitions — each one has a name, a description, a parameter schema, an endpoint path, an HTTP method, and a content type.
At call time, the assistant decides to use a function based on the prompt and the user's input, fills in the arguments from conversation context, and Insighto sends them as the request body (or query string for GET) to your endpoint. Your endpoint responds; the response body is handed back to the assistant as the tool result.
Setup walkthrough
- Tools & Integrations → Custom → Add custom tool.
- Name the tool. Be descriptive — "Acme Order Lookup", not "Tool 1". This name shows up in the dashboard and on assistant tool lists.
- Base URL. The root of your service (e.g.
https://api.acme.com). Each function's endpoint path is appended to this. - Auth method. Pick one of:
- None — Your endpoint is open or auth is baked into the URL (e.g. a signed-URL token). Insighto just sends the request.
- Simple token — Insighto exchanges your stored credentials for a token at a known endpoint, then attaches the token to every subsequent request under a configurable header name. Useful when your service issues short-lived bearer tokens.
- OAuth — Standard OAuth code/refresh flow. Reserved for built-in integrations; custom tools rarely use this.
- Function definitions. For each function:
- Function name and description.
- JSON schema for the arguments.
- Endpoint path (appended to base URL).
- HTTP method (
GET,POST,PUT,DELETE). - Content type (usually
application/json).
- Save. The tool is now attachable to assistants.
The function schema — the part that matters
This is what the model sees and reasons about. Treat the description and per-parameter descriptions as API docs written for the LLM. The difference between "the tool gets called reliably" and "it gets called sometimes" lives here.
A function definition looks like:
{
"name": "lookup_order",
"description": "Look up an order by order number or customer email. Returns order status, shipping info, and line items. Use this whenever a customer asks about an order they've placed.",
"parameters": {
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The order number, e.g. 'ORD-12345'. Provide this if the customer gave you their order number."
},
"customer_email": {
"type": "string",
"description": "Email address on the order. Provide this if the customer gave you their email but not an order number."
}
},
"required": []
}
}
Two things move the needle:
- The function description. Explain when the model should use this, not just what it technically does. The model invokes based on description match against the user's intent.
- Per-parameter descriptions. Without these, the model guesses what each field means. With them, accuracy goes up significantly.
How the request is shaped
- The full URL is
base_url + endpoint. - Auth headers are attached automatically based on your auth method.
- A standard
User-Agentheader is always included. - For
application/json, the model's arguments object is sent as the JSON body. - For form-encoded content types, the arguments are sent as form data.
- For
GETrequests, the arguments become query string parameters instead of a body.
The arguments object is forwarded directly — no wrapping envelope. If your endpoint expects { "function": "lookup_order", "arguments": {...} }, build that wrapping on your side; Insighto sends just the arguments.
Worked example: order lookup
Step 1: build the endpoint. Your service at https://api.acme.com/insighto/order-lookup accepts a POST with body:
{ "order_number": "ORD-12345" }
And returns:
{
"order_number": "ORD-12345",
"status": "shipped",
"shipped_at": "2026-05-10T14:30:00Z",
"tracking_url": "https://carrier.com/track/abc",
"items": [{ "name": "Blue Widget", "qty": 2 }]
}
Step 2: register the custom tool in Insighto.
- Tool name: Acme Orders
- Base URL:
https://api.acme.com - Auth method: simple token (or none, depending on how you protect the endpoint)
- Function:
- Name:
lookup_order - Endpoint:
/insighto/order-lookup - Method:
POST - Content type:
application/json - Parameters: see schema above
- Name:
Step 3: attach to an assistant. In the assistant's Tools tab, pick "Acme Orders". In the system prompt:
You have access to an order lookup tool. When a customer asks about their order — status, shipping, where it is — call
lookup_order. Ask for either their order number or the email on their account. Tell them the status in plain language; if it's shipped, give them the tracking link.
Step 4: test. Open the chat widget. Say "where's my order ORD-12345?" The assistant calls lookup_order with { "order_number": "ORD-12345" }. Your endpoint returns the JSON; the assistant reads it back as "Your order ORD-12345 was shipped on May 10. Here's the tracking link: https://carrier.com/track/abc."
Example invocation
The assistant's call to your endpoint:
{
"function": "lookup_order",
"arguments": {
"order_number": "ORD-12345"
}
}
Your endpoint receives:
POST /insighto/order-lookup HTTP/1.1
Host: api.acme.com
Content-Type: application/json
Authorization: Bearer <your-token>
{"order_number": "ORD-12345"}
You return:
{
"order_number": "ORD-12345",
"status": "shipped",
"tracking_url": "https://carrier.com/track/abc"
}
The assistant uses this to compose its response: "Your order is on its way — shipped already. Here's the tracking link: https://carrier.com/track/abc"
Response handling
- Successful responses (2xx) have their bodies returned to the assistant verbatim.
- Non-2xx responses get wrapped — the assistant sees
"HTTP code: <status> while making the API request"instead of your error body. If you want the model to know specifics about a failure (e.g. "order not found, suggest contacting support"), return errors as 2xx with anerrorfield in the JSON body. - HTML responses (common when a misconfigured proxy serves an error page) confuse the model. Make sure your error paths return JSON too.
- No automatic retries. A failed call fails once.
Function categories
When you create the tool, pick a category — CRM, Calendar, Support, Communication, Database, Payment, Others, etc. This is purely organizational in the UI; it doesn't affect how the tool runs.
Security tips
- TLS only. Don't accept non-HTTPS in production.
- Authenticate. Don't expose your endpoint un-authed unless you've thought hard about who can hit it.
- Validate inputs. The model is sending you JSON that originated from a customer's conversation. Don't trust it — validate types, sanitize before SQL/shell, enforce length limits.
- Rate limit on your side. A misbehaving model could in theory loop-call your endpoint. Keep a per-key rate limit on your end.
- Return narrow, model-friendly responses. Don't return your entire database schema — return what the assistant needs to answer the user.
Failure modes
- Endpoint returns HTML, not JSON. Common when an error page comes back from a misconfigured proxy or auth gateway. The assistant gets a wall of HTML as the tool result and gets confused. Make sure error paths return JSON.
- Auth token rotation drift. You rotated a token; Insighto's stored value is stale; every call fails with 401. Re-run setup or implement token refresh in your simple-token config.
- Non-2xx wrapping. The assistant never sees your endpoint's error body directly — only the wrapper string. Return errors as 2xx with an
errorJSON field if you want the model to react meaningfully. - Timeout on your side. A slow endpoint blocks the conversation; voice callers hear silence. Aim for sub-second response times on tool calls used in voice flows.
Where to next
- For Postgres-only lookups, the built-in Postgres tool is simpler than rolling a custom one.
- For services that already speak Model Context Protocol, use MCP servers — schemas auto-discovered from the server.
- See the Tools overview for prompt patterns that apply across every integration.