> For the complete documentation index, see [llms.txt](https://docs.ojin.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ojin.ai/apps/overview/tools.md).

# Tools & Events

> Let your Human Agent call functions during a conversation, and react to the agent from your own page.

## Tool Calling

Tools (function calling) let the agent's LLM trigger actions while it talks — look something up, submit a form, update your UI, or notify a backend system. You define each tool's name, description, and JSON-schema parameters; the LLM decides when to call it and with what arguments.

Tools are configured **per agent**. You can manage them two ways:

* In the [Ojin dashboard](https://ojin.ai/dashboard) when [configuring an agent](/apps/overview/configure.md) — the agent's tools editor.
* Programmatically via the [REST API](#managing-tools-via-the-rest-api).

Each tool has an **enabled** flag. A disabled tool keeps its configuration but is hidden from the LLM, so you can turn a tool off without losing its setup.

## Delivery Modes

Every tool declares how its calls are delivered. There is no agent-wide default — each tool picks its own mode.

| Mode        | Where the call goes                                     | Your responsibility                        |
| ----------- | ------------------------------------------------------- | ------------------------------------------ |
| **Webhook** | Ojin POSTs the call to a URL you configure, server-side | Stand up the endpoint; no browser code     |
| **Client**  | The widget emits a DOM event in the user's browser      | Listen for the event on the embedding page |

Use **webhook** delivery for backend actions (write to a database, call an API, send a notification). Use **client** delivery for things that happen in the user's browser (navigate the page, open a modal, update on-page state).

## Webhook Delivery

When a webhook tool fires, Ojin sends an HTTP request to the tool's configured URL:

* Method `POST`, header `Content-Type: application/json`.
* A 30-second timeout.

The request body carries the call under a `tool_call_message` object. The fields you'll use:

```json
{
  "tool_call_message": {
    "name": "get_order_status",
    "parameters": "{\"order_id\":\"A123\"}",
    "tool_call_id": "call_abc",
    "response_required": true,
    "tool_type": "function"
  }
}
```

| Field               | Meaning                                                        |
| ------------------- | -------------------------------------------------------------- |
| `name`              | The tool that was called                                       |
| `parameters`        | The call arguments, as a **JSON string** (parse it before use) |
| `tool_call_id`      | Unique id for this call                                        |
| `response_required` | `true` when the agent is waiting for your response (see below) |

The body also includes additional session metadata fields alongside `tool_call_message`; you can ignore them for most integrations.

{% hint style="warning" %}
The webhook request is **not signed** — there is no secret header. Treat the webhook URL itself as a secret, serve it over HTTPS, and validate the payload before acting on it.
{% endhint %}

### Waiting for a response

Each webhook tool has a **wait for response** setting:

* **On** (`response_required: true`) — the agent waits for your endpoint, then feeds the result back into the conversation so the LLM can use it. Return HTTP `2xx` with a JSON body:

  ```json
  { "result": "Your order shipped on Tuesday." }
  ```

  If you omit the `result` key, the entire JSON body is used as the result. An empty body (or `204`) is treated as an empty result. A non-`2xx` status, a timeout, or an unparseable body surfaces an error result to the LLM instead.
* **Off** (`response_required: false`) — fire-and-forget. Ojin sends the request and does not wait; the LLM immediately continues with a neutral acknowledgement. Anything your endpoint returns is ignored.

## Client Delivery

When a client tool fires, the widget dispatches a DOM `CustomEvent` named `ojinToolCall`. The event fires on **`window`** (not on the `<ojin-agent>` element), so add the listener to `window`:

```js
window.addEventListener("ojinToolCall", (event) => {
  const { function_name, tool_call_id, arguments: args } = event.detail;
  // Run the browser-side action for `function_name` using `args`.
  // `args` is already an object — no parsing needed.
});
```

The `arguments` field is renamed to `args` above because `arguments` is a reserved word inside regular functions.

`event.detail` carries:

| Field           | Type     | Meaning                            |
| --------------- | -------- | ---------------------------------- |
| `function_name` | `string` | The tool that was called           |
| `tool_call_id`  | `string` | Unique id for this call            |
| `arguments`     | `object` | The call arguments, already parsed |

{% hint style="info" %}
Client delivery is **one-way**. There is no path to send a value back to the agent from the browser — the agent continues as soon as it dispatches the call. If the LLM needs to use a result, use **webhook** delivery with *wait for response* turned on.
{% endhint %}

## Managing Tools via the REST API

Agent endpoints accept your API key, so you can manage tools programmatically. Authenticate with the `X-API-Key` header (see [Get your API key](/getting-started/authentication.md)).

Tools live on the agent itself. Set them with the agent update endpoint, `PATCH /v1/agents/{agent_id}`, using the `tools` array:

```bash
curl -X PATCH https://api.ojin.ai/v1/agents/$AGENT_ID \
  -H "X-API-Key: $OJIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tools": [
      {
        "enabled": true,
        "function": {
          "name": "open_pricing",
          "description": "Open the pricing page for the user.",
          "parameters": {
            "type": "object",
            "properties": { "plan": { "type": "string" } }
          }
        },
        "delivery": { "mode": "client" }
      },
      {
        "enabled": true,
        "function": {
          "name": "create_ticket",
          "description": "File a support ticket.",
          "parameters": {
            "type": "object",
            "properties": { "summary": { "type": "string" } }
          }
        },
        "delivery": {
          "mode": "webhook",
          "url": "https://example.com/hooks/ticket",
          "wait_for_response": true
        }
      }
    ]
  }'
```

{% hint style="warning" %}
The `tools` array is **full-replace**. Tools are matched by `function.name`: a name already on the agent is updated in place, a new name is created, and **any existing tool whose name is not in the array is deleted**. To change one tool, send the complete set. To leave tools untouched, omit `tools` from the request entirely.
{% endhint %}

To read an agent's current tools (each returned with its server-assigned `tool_id`), use `GET /v1/agents/{agent_id}`. For the full schema, see the [REST API](/getting-started/api.md) reference.

## Widget Events

Beyond `ojinToolCall`, the `<ojin-agent>` widget emits DOM events you can listen for to build custom UI, transcripts, or telemetry around the conversation. All events fire on **`window`**.

### Conversation events

| Event                    | `detail`                                      | Fires when                                                                 |
| ------------------------ | --------------------------------------------- | -------------------------------------------------------------------------- |
| `ojinToolCall`           | `{ function_name, tool_call_id, arguments }`  | A client-delivered tool is called (see above)                              |
| `botIsSpeaking`          | `{ isSpeaking: boolean }`                     | The agent starts or stops speaking                                         |
| `ReceivedTranscriptUser` | `{ text, timestamp, user, final }`            | A user speech transcript arrives (`final: false` for interim)              |
| `ReceivedTranscriptBot`  | `{ text }`                                    | A chunk of the agent's response text arrives                               |
| `BotTtsStopped`          | *no payload (`detail` is `null`)*             | The agent's current spoken turn ends or is interrupted                     |
| `UserStartedSpeaking`    | *no payload (`detail` is `null`)*             | The user starts speaking                                                   |
| `UserStoppedSpeaking`    | *no payload (`detail` is `null`)*             | The user stops speaking                                                    |
| `ConnectionTimeout`      | `{ duration_s, bot_state, connection_state }` | Connection setup exceeds its timeout; the widget resets and shows an error |

```js
window.addEventListener("ReceivedTranscriptUser", (event) => {
  const { text, final } = event.detail;
  if (final) console.log("User said:", text);
});
```

### Advanced / telemetry events

These carry fine-grained audio and timing signals, useful for meters, latency dashboards, or debugging. They are more granular than the conversation events above and may change — don't depend on them for core logic.

| Event                    | `detail`                        | Fires when                               |
| ------------------------ | ------------------------------- | ---------------------------------------- |
| `userSpeakingChanged`    | `{ isSpeaking: boolean }`       | Browser voice-activity detection toggles |
| `userVolume`             | `{ volume: number }`            | The user's microphone level is sampled   |
| `botVolume`              | `{ volume: number }`            | The agent's playback level is sampled    |
| `LatencyReport`          | per-utterance latency breakdown | The agent reports timing for a turn      |
| `ConnectionTimingReport` | connection timing breakdown     | The session finishes connecting          |

## Next Steps

* [**Create & Configure**](/apps/overview/configure.md) — set up the agent these tools belong to
* [**Widget Integration**](/apps/overview/widget-integration.md) — embed the agent on your page
* [**REST API**](/getting-started/api.md) — full schema for programmatic management


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ojin.ai/apps/overview/tools.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
