> ## Documentation Index
> Fetch the complete documentation index at: https://tinytalk.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Tool

> Define your own HTTP endpoint as a tool the agent can call during conversations.

A Custom Tool is a tool you define yourself by pointing Tiny Talk at an HTTP endpoint — your own backend, a SaaS API, or any HTTPS service. The agent calls it whenever the conversation calls for it, passing in arguments the AI model extracts from the chat.

Use Custom Tools to:

* **Look up records** — order status, account balance, ticket state, inventory
* **Update systems** — append a note to a CRM, mark a ticket resolved, log an event
* **Trigger workflows** — kick off a webhook, send a transactional email, schedule a job

For a high-level overview of how tools work, plan limits, and the security model that applies to every tool type, see [Tools](/features/tools).

## Creating a Custom Tool

Go to **Agent → Tools** and click **New tool**. The editor has six sections, top to bottom.

<Steps>
  <Step title="Metadata">
    The metadata describes the tool to the AI model and to you.

    * **Name** — A human-readable name (e.g. *Check Order Status*). Up to 100 characters; letters, digits, and spaces only.
    * **Slug** — Auto-generated from the name. This is the identifier the model uses to call the tool. Read-only and shown only after the tool is saved.
    * **When to use** — The single most important field for tool-calling accuracy. Describe the situations where the agent *should* invoke this tool. Be specific about the kinds of questions or intents it should match. Up to 1,000 characters.
    * **Description** — A short summary of *what* the tool does. Concatenated with **When to use** into the description the model sees at tool-selection time. Up to 1,000 characters.

    <Tip>
      Use **When to use** for *when* to call (intents, triggers) and **Description** for *what* the tool does (the action). The model reads both together when deciding whether to invoke the tool.
    </Tip>
  </Step>

  <Step title="Request">
    Defines the HTTP request Tiny Talk sends when the agent invokes the tool.

    * **Method** — `GET`, `POST`, or `PATCH`.
    * **URL** — Full HTTPS URL of the endpoint. Supports `{{params.name}}` placeholders (see [Templating](#templating)). HTTPS only; IP addresses are not allowed.
    * **Body (JSON)** — Required for `POST` and `PATCH`. Must be valid JSON. Supports both `{{params.name}}` and `{{secrets.name}}`. Up to 32 KB.
  </Step>

  <Step title="Headers">
    Add HTTP headers as key-value pairs. Both keys and values support templating, so you can inject secrets without hard-coding them.

    Common patterns:

    | Header          | Value                        |
    | --------------- | ---------------------------- |
    | `Authorization` | `Bearer {{secrets.api_key}}` |
    | `X-API-Key`     | `{{secrets.api_key}}`        |
    | `Accept`        | `application/json`           |

    A `Content-Type: application/json` header is added automatically for `POST` and `PATCH` requests if you don't set one yourself.

    Up to **20 headers** per tool.
  </Step>

  <Step title="Parameters">
    Parameters are the inputs the AI model collects from the conversation and passes into your tool. They become part of the function signature the model sees.

    For each parameter, set:

    * **Name** — Lowercase letters, digits, and underscores only (e.g. `order_id`). Up to 64 characters. Reference it in your URL, headers, and body as `{{params.order_id}}`.
    * **Type** — `string`, `number`, `integer`, `boolean`, or `enum`.
    * **Description** — What value the agent should collect. The model reads this to decide what to ask the user.
    * **Required** — If on, the model must provide the parameter before it can call the tool.
    * **Enum values** — For the `enum` type only. Comma-separated list of allowed values (e.g. `pending, shipped, delivered`).

    Up to **20 parameters** per tool.

    <Tip>
      Write parameter descriptions from the model's perspective: "The unique order number the user wants to check, formatted like `ORD-12345`." Concrete examples help the model extract the right value from a free-form message.
    </Tip>
  </Step>

  <Step title="Secrets">
    Secrets are credentials — API keys, bearer tokens, basic-auth strings — that you don't want exposed in the tool definition. Reference them as `{{secrets.name}}` in headers and body.

    For each secret, set:

    * **Name** — Lowercase letters, digits, and underscores only (e.g. `api_key`).
    * **Value** — The secret value. Up to 4,096 characters.

    Up to **20 secrets** per tool.

    <Info>
      Secrets are encrypted at rest with AES-256 and decrypted only at the moment a tool is invoked. After saving, the dashboard shows only a masked preview (`••••1234`) — the raw value is never sent back to the browser. Editing other fields without re-entering a secret keeps the existing value.
    </Info>

    <Warning>
      Secrets are **not allowed in the URL** — they would leak into access logs and analytics. Put them in headers or the request body instead.
    </Warning>
  </Step>

  <Step title="Response">
    Controls what part of the response the AI model sees.

    * **Full response visible to the agent** — The model receives the entire response body (default).
    * **Only selected fields visible** — The model receives only the fields you list. Useful when the API returns a large object but only a few values matter for the conversation.

    When you choose **selected**, list the fields as comma-separated dot-paths:

    ```text theme={null}
    status, shipping.tracking_url, customer.email
    ```

    Dot-paths reach into nested objects. Array indexing isn't supported — pass arrays through whole or restructure your response.

    Responses are capped at **20 KB**. Larger responses are truncated, and the model is told the response was truncated so it can react accordingly.
  </Step>
</Steps>

After saving, the tool appears in the agent's tool list and is enabled by default.

## Templating

Tiny Talk replaces `{{params.NAME}}` and `{{secrets.NAME}}` placeholders in your URL, headers, and body before sending the request.

| Location        | Params | Secrets | Notes                                                                    |
| --------------- | :----: | :-----: | ------------------------------------------------------------------------ |
| **URL**         |    ✓   |    —    | Param values are URL-encoded. Secrets are blocked here.                  |
| **Headers**     |    ✓   |    ✓    | Newlines (`\r`, `\n`) are rejected to prevent header injection.          |
| **Body (JSON)** |    ✓   |    ✓    | Type-preserving — a placeholder alone in a string keeps its native type. |

In a JSON body, a value of `"{{params.count}}"` where `count` is an `integer` is sent as a JSON number, not a string. Mixed strings (`"id-{{params.order_id}}"`) are interpolated textually and sent as strings. Special characters in values are JSON-escaped automatically — there's no way for a value to break out of the surrounding JSON structure.

## Testing your tool

Once saved, the tool editor shows a **Test** card. Fill in sample values for each parameter and click **Run test**. The response panel shows:

* The HTTP status code and the resolved request line
* The full response body (compacted JSON or text)
* The filtered body, if you're using selected-fields response mode
* A truncation notice, if the response exceeded the 20 KB cap

Use the test runner before attaching a tool to a production agent — interpolation mistakes, auth header typos, and unexpected response shapes show up here, where the model isn't yet involved.

## Disabling and deleting

Toggle the **Enabled** switch on a tool to stop the agent from calling it without losing the configuration. Disabled tools will count against your plan limit.

To remove a tool entirely, open it and use **Delete tool** in the Danger Zone. Active conversations stop referencing the tool immediately and the action can't be undone.

## Example use cases

### Single tool: order lookup

A typical e-commerce setup. The agent looks up order status when a visitor asks about their shipment.

| Field       | Value                                                                                                                                        |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Name        | Check Order Status                                                                                                                           |
| When to use | When the visitor asks about the status, shipment, or delivery of a specific order. They'll usually mention an order number like `ORD-12345`. |
| Description | Looks up an order by its ID and returns the current status, tracking URL, and estimated delivery date.                                       |
| Method      | `GET`                                                                                                                                        |
| URL         | `https://api.example.com/orders/{{params.order_id}}`                                                                                         |
| Headers     | `Authorization: Bearer {{secrets.api_key}}`                                                                                                  |
| Parameters  | `order_id` (string, required) — *The order number the user wants to check, formatted like `ORD-12345`.*                                      |
| Secrets     | `api_key`                                                                                                                                    |
| Response    | Selected: `status, shipping.tracking_url, estimated_delivery`                                                                                |

A visitor message like *"hey, what's going on with my order ORD-58821?"* triggers the tool with `order_id = "ORD-58821"`. The agent reads the filtered response and replies in plain English.

### Multiple tools: customer support kit

Three coordinated tools let an agent handle most account questions end-to-end.

1. **Look up customer** — `GET /customers?email={{params.email}}` — returns the customer ID, plan, and current balance.
2. **List recent orders** — `GET /customers/{{params.customer_id}}/orders?limit=5` — returns the most recent orders for a customer.
3. **Open a support ticket** — `POST /tickets` with body `{ "customer_id": "{{params.customer_id}}", "subject": "{{params.subject}}", "body": "{{params.body}}" }` — files a ticket the support team can pick up.

The agent chains them. When a visitor describes a billing issue, it looks up the customer by email (tool 1), retrieves their recent orders (tool 2), and either resolves the question on the spot or files a ticket with the relevant context (tool 3).

<Tip>
  When chaining tools, name your parameters consistently across tools — `customer_id` everywhere, not a mix of `customerId`, `cust_id`, and `id`. The model passes outputs from one tool into another based on naming and semantic match; consistency reduces ambiguity.
</Tip>

### Internal HR assistant

For an internal-facing agent, Custom Tools can hit your private APIs as long as they're reachable on the public internet.

* **Look up time-off balance** — `GET /hr/employees/{{params.employee_id}}/pto` — returns the employee's available, used, and pending PTO.
* **Submit a time-off request** — `POST /hr/pto-requests` with the start date, end date, and reason — files a request that routes through your existing approval flow.

Pair this with a system prompt that asks for the employee ID up front, and the agent handles routine PTO questions without HR involvement.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The agent isn't calling my tool">
    The model decides whether to invoke a tool based on the **Name**, **Description**, and **When to use** fields. If it never picks the tool, the description is usually too vague or doesn't match the kinds of messages visitors actually send.

    * Rewrite **When to use** with concrete examples: *"When the visitor asks about the status of an order, mentions an order number, or asks where their package is."*
    * Make sure the tool is **enabled** on the agent.
    * Check that the model you've selected supports tool calling — most modern models do, but some smaller or older models do not. The agent runtime skips tool registration on unsupported models.
  </Accordion>

  <Accordion title="The test runner fails with 'Tool could not be reached'">
    * Confirm the URL is HTTPS and the hostname is publicly resolvable. The runtime blocks IP literals, private addresses, and cloud-metadata endpoints.
    * If the endpoint requires VPN or IP allowlisting, expose it via a public gateway with auth — Tiny Talk's tool runtime calls from public infrastructure.
    * Check your endpoint isn't returning a redirect (3xx). Configure the final URL directly; the runtime does not follow redirects.
  </Accordion>

  <Accordion title="The test runner returns 'Tool call timed out'">
    The default timeout is 10 seconds. If your endpoint is slow:

    * Make the work asynchronous on your side — return a job ID immediately, and have a second tool that checks the status.
    * Tighten queries or add caching at the API layer.
    * The maximum timeout is 15 seconds and is set by the platform; tools cannot exceed this limit.
  </Accordion>

  <Accordion title="The agent gets confused between two similar tools">
    * Make the **When to use** fields explicitly distinct: each one should describe situations the *other* tool wouldn't match.
    * Consider merging them into a single tool with an `enum` parameter that switches behavior. One well-described tool with a `mode` enum is often clearer to the model than two near-duplicate tools.
  </Accordion>

  <Accordion title="The response is being truncated">
    Responses over 20 KB are cut off, and the model sees a `[Response truncated]` marker. To work around this:

    * Switch the **Response** mode from full to **Selected fields** and list only the fields the model needs.
    * Adjust your endpoint to return a slimmer response — pagination, projection, or summary fields.
  </Accordion>

  <Accordion title="A parameter the model passes is wrong or empty">
    * Tighten the parameter **Description** with examples and format hints.
    * Use the right **Type** — `enum` constrains the model to a fixed set; `integer` rejects decimals; `boolean` is a true on/off.
    * Mark essential parameters as **Required** so the model collects them before calling.
  </Accordion>

  <Accordion title="Can I use OAuth?">
    Not yet. The current secret model fits API keys, bearer tokens, and basic-auth credentials carried in headers. OAuth flows that require dynamic token refresh are on the roadmap. As a workaround, you can rotate long-lived tokens via the API or schedule them externally and update the secret in the dashboard.
  </Accordion>

  <Accordion title="Can a tool perform a destructive action like DELETE?">
    Not yet. Custom Tools support `GET`, `POST`, and `PATCH` only. `PUT` and `DELETE` are intentionally not supported — they need an approval flow before the agent should be allowed to call them. That's planned for a future release.
  </Accordion>
</AccordionGroup>
