Custom HTTP tools
Connect your agent to your own APIs with HTTP tools: JSON-schema parameters, templates, secrets, timeouts, safety limits, and an order-lookup example.
Last updated:
Custom HTTP tools let your agent look things up and take actions in your own systems: check an order, find a booking slot, create a ticket, query your product catalogue. You describe an HTTP request and the data it needs; the AI decides when to call it and fills in the details from the conversation. SiteTrellis makes the request from its servers and gives the (size-limited) response back to the AI so it can answer.
How a tool call works#
- The AI is shown each tool's name, description and parameters (a JSON schema).
- When a visitor's request needs it, the AI calls the tool with arguments. For example, "Where is my order 4821?" calls
lookup_orderwith{"order_number": "4821"}. - SiteTrellis validates the arguments against your schema, builds the request from your templates, sends it, and passes the response back to the AI as untrusted data.
- The AI writes its reply using the result. A tool may be called several times in one reply, up to the agent's max tool iterations (default 4, maximum 8).
Tool settings#
| Setting | Meaning | Limits |
|---|---|---|
| Name | Identifier the AI uses. Lowercase letters, digits and underscores; must start with a letter. Cannot reuse a built-in tool name. | 2 to 40 characters, unique per agent |
| Description | Tells the AI when and why to use the tool. Required to publish. Be specific. | Up to 600 characters |
| Parameters | JSON schema of the inputs: names, types, descriptions, required list, optional enums. | Types: string, integer, number, boolean, array, object |
| Method | GET, POST, PUT, PATCH or DELETE. | Default POST |
| URL | The endpoint. Must start with https://. The scheme and host must be fixed text (no placeholders there). | Up to 500 characters |
| Headers | Request headers, values may use placeholders and secrets. | Up to 20 headers |
| Query template | Query-string parameters, values may use placeholders. | Up to 20 |
| Body template | Request body for POST, PUT and PATCH. | Up to 4,000 characters |
| Timeout | Seconds to wait for your server (also limited by the time left in the reply). | 1 to 25, default 10 |
| Running message | Optional text such as "Checking your order…". It is stored with the tool, but in this version the widget shows its normal typing indicator while a tool runs. | Up to 120 characters |
| Confirm before running | Show the visitor the exact action and arguments with Confirm and Cancel buttons. Only the Confirm button authorizes that action; a text reply cannot. Confirmation expires after ten minutes and is canceled by another message or changed context. Use for anything that changes data. | On by default for tools that are not GET, off for GET |
| Enabled | Disabled tools are not offered to the AI. | On by default |
Placeholders in templates#
Use double curly braces inside the URL path, query values, header values and body template:
| Placeholder | Value |
|---|---|
{{order_number}} or {{args.order_number}} | The argument the AI supplied for the parameter named order_number. |
{{secret.NAME}} | A secret stored for this agent (below). |
{{visitor.KEY}} | A visitor attribute, such as {{visitor.email}}. These come from the browser and are unverified. |
{{conversation.id}}, {{site.id}}, {{visitor.id}} | Public ids of the conversation, site and visitor. |
Substitution is context-aware, so values cannot break out of the place you put them: values in the URL path are percent-encoded, values in a JSON body are JSON-escaped, and line breaks are removed from header values. In a JSON body, a placeholder that stands alone as a whole value keeps its type: {"limit": "{{limit}}"} sends the number 5, not the text "5", when limit is an integer parameter.
If you leave the body template empty on a POST, PUT or PATCH, the arguments are sent as a JSON object. For a GET with no query template and no placeholders in the URL, the arguments are added as query parameters automatically.
Secrets#
Never write an API key into the prompt, the URL or a header value directly. Store it as an agent secret and reference it with {{secret.NAME}}:
- Open the agent and find Secrets.
- Add a name (for example
SHOP_API_KEY; names are up to 60 characters) and the value. - Reference it in a header:
Authorization: Bearer {{secret.SHOP_API_KEY}}.
- Secrets are encrypted at rest. After saving you only see a short hint, never the full value.
- They are never included in prompts, in the widget, or in published version snapshots, so rolling back an agent does not roll back its secrets.
- If a secret value appears in a tool's response or arguments, it is replaced with
[redacted]in the stored logs and in what the AI sees. - Use a key with the narrowest possible permissions (read-only where you can) dedicated to this integration, so it is easy to rotate.
Example: order lookup#
Goal: when a visitor asks about an order, check its status in your shop backend.
The definition#
{
"name": "lookup_order",
"description": "Look up the status of an order. Use when the visitor asks where their order is or about delivery. Needs the order number and the email address used to order.",
"method": "GET",
"url": "https://api.example-shop.com/v1/orders/{{order_number}}",
"headers": {
"Authorization": "Bearer {{secret.SHOP_API_KEY}}"
},
"query_template": {
"email": "{{email}}"
},
"timeout_s": 8,
"confirm": false,
"parameters": {
"type": "object",
"properties": {
"order_number": { "type": "string", "description": "The order number, digits only, e.g. 4821" },
"email": { "type": "string", "description": "Email address used when ordering" }
},
"required": ["order_number", "email"]
}
}
This is the shape stored for the tool, shown for reference. In the dashboard you fill in the same fields in a form.
What your server should return#
Return compact JSON containing only what the AI should say. Excess data costs tokens and can leak things you did not intend.
{
"order_number": "4821",
"status": "shipped",
"carrier": "DHL",
"tracking_url": "https://track.example.com/JD0146000012345",
"estimated_delivery": "2026-10-10"
}
Server-side checks#
Your endpoint must enforce authorisation. The visitor typed the order number and email, and anyone can type anyone's. Match them to each other on your server, return nothing when they do not match, and never return other people's data. For anything stronger than "status of my order", require the visitor to be authenticated by your own means.
<?php
// GET /v1/orders/{number}?email=... (Authorization: Bearer <secret>)
declare(strict_types=1);
header('Content-Type: application/json');
if (!hash_equals(getenv('SHOP_API_KEY') ?: '', substr($_SERVER['HTTP_AUTHORIZATION'] ?? '', 7))) {
http_response_code(401);
exit(json_encode(['error' => 'unauthorized']));
}
$number = preg_replace('/\D/', '', (string)($_GET['number'] ?? ''));
$email = strtolower(trim((string)($_GET['email'] ?? '')));
$order = find_order($number); // your lookup
if (!$order || strtolower($order['email']) !== $email) {
http_response_code(404); // same answer for "no such order" and "wrong email"
exit(json_encode(['error' => 'not_found']));
}
echo json_encode([
'order_number' => $order['number'],
'status' => $order['status'],
'estimated_delivery' => $order['eta'],
]);
Prompt guidance#
Add a short instruction to the agent's prompt, for example: "To check an order, ask for the order number and the email used to order, then use lookup_order. If it returns not found, say you could not find a match and offer a person." See the prompt writing guide.
Safety protections#
- Host is fixed. The scheme and host come from your template as written; placeholders cannot change where the request goes, so a visitor or the AI cannot redirect a tool to another server.
- No private networks. The destination is resolved and checked before connecting, and requests to private, loopback and link-local addresses (including internal and cloud metadata addresses) are blocked. A tool cannot be used to probe your internal network, or ours. Your endpoint must be reachable on the public internet; protect it with a secret.
- No redirects. Responses with redirect status codes are not followed. Use the final URL.
- Size and time caps. Responses are read up to a fixed size (about 200 KB), and only the first 4,000 characters of the response text are given to the AI, marked as truncated if longer. Requests time out after your configured timeout.
- Results are data. The AI is told that tool output is untrusted data, not instructions; still, never return text from untrusted sources that tries to give the AI commands, and keep write actions behind the confirm option.
- Argument validation. Arguments are checked against your schema (types, enums, required) before any request is made. Unknown arguments are dropped, and text arguments are capped at 2,000 characters.
- Each request carries an
X-SiteTrellis-Conversationheader with the conversation id, useful for your own logs.
Testing and debugging#
- Test your endpoint first with curl, using the same headers.
- In the builder's Tools tab use the tool's Run test button: it fills your templates with sample argument values, calls your endpoint, and shows the status, timing and a scrubbed excerpt of the response without involving the AI.
- Add the tool to the agent and chat in the playground. Playground tool calls run for real against your endpoint, so use a test environment for write actions.
- Check the conversation's trace in the dashboard: each tool call is recorded with its arguments, an excerpt of the result, the HTTP status and the time taken. Secret values are redacted.
- If a call fails, the AI is told so plainly and offers an alternative, and a
tool.failedevent is sent to your webhooks (live conversations only).
| Symptom | Likely cause |
|---|---|
| "the destination address is not allowed" | The host resolves to a private or internal address, or the URL points to localhost. |
| "the host could not be found" | DNS name wrong or not public. |
| "the request timed out" | Your endpoint is slower than the tool timeout. Speed it up or raise the timeout (maximum 25 seconds). |
| HTTP 401 or 403 | Wrong or missing secret, or the secret name in the header does not match. |
| HTTP 3xx | Redirect not followed. Use the final HTTPS URL. |
| The AI never calls the tool | Description too vague, the prompt does not mention it, the model does not support tools, or max tool iterations is set to 0. |
| The AI calls it with wrong values | Tighten the parameter descriptions, use enum where possible, and add examples in the descriptions. |
Related: Built-in tools, Security and privacy.