> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.lumenia.net/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.lumenia.net/_mcp/server.

# Tools

A **tool** is an HTTP action the assistant can call while talking to a user — for example "look up an order", "create a ticket", or "check availability". Tools are defined once — at the **organization** level or at the **partner** level — and **attached** to the workspaces that should be able to use them.

## Anatomy of a tool

`GET /v1/tools/{toolId}` returns a tool definition:

| Field            | What it is                                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | The tool's identifier the model sees. 1–64 characters, letters/digits/underscores/hyphens, no spaces.                                 |
| `description`    | Natural-language explanation of what the tool does and when to use it. **This is what the model reads to decide whether to call it.** |
| `method`, `url`  | The HTTP request the tool makes (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`).                                                            |
| `headers`        | Header names and values, JSON-Schema-shaped. **Every** header value is masked in responses (the names are shown).                     |
| `requestSchema`  | JSON Schema of the request payload — the parameters the model fills in from the conversation.                                         |
| `responseSchema` | JSON Schema of the fields to **extract** from the tool's HTTP response.                                                               |
| `aliases`        | Named bindings that map extracted response fields to variables you can reference.                                                     |

> **Info**
>
> Header values are never returned in full by default — **every** value is masked (`••••••`), whether or not it is actually a secret. (Headers appear only on the tool detail, `GET /v1/tools/{toolId}`, not in the tool list.) Reveal specific values on a single read with `GET /v1/tools/{toolId}?unmask=Header-Name`.

## Two ownership scopes

Every tool is owned by exactly one of two scopes, and the scope decides who can use it:

| Scope            | Created with                                                 | Usable by                                                                                                                         |
| ---------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| **Organization** | `POST /v1/organizations/{orgId}/tools` (with `X-Account-Id`) | Workspaces of that organization only.                                                                                             |
| **Partner**      | `POST /v1/tools` (API key only, **no** `X-Account-Id`)       | Any workspace under your key — directly, or via [prompt templates](/documentation/core-concepts/prompt-templates#template-tools). |

Use an org tool for something specific to one customer (their ticketing system, their credentials); use a partner tool for an action you want to reuse across customers.

On the item routes (`GET`/`PATCH`/`DELETE /v1/tools/{toolId}`), the `X-Account-Id` header selects the scope: **with** the header the id is resolved as an org tool under that account, **without** it as a partner tool — an org tool's id without the header will not resolve (`404`). Tool `name`s are unique within their scope (per organization, per partner).

## Attaching a tool to a workspace

A workspace opts in to a tool by attaching it:

```
PUT    /v1/workspaces/{workspaceId}/tools/{toolId}   # attach (idempotent)
DELETE /v1/workspaces/{workspaceId}/tools/{toolId}   # detach
GET    /v1/workspaces/{workspaceId}/tools            # list directly attached tools
```

An org tool must belong to the workspace's own organization; a partner tool must be owned by your company. Only attached tools are exposed to that workspace's assistant.

## The effective tool set

Attachments are not the only way a workspace gets tools: a [prompt template](/documentation/core-concepts/prompt-templates#template-tools) pinned to the workspace brings its own partner tools along. What the assistant actually sees is the **union** of both:

```
GET /v1/workspaces/{workspaceId}/tools/effective          # per channel: chat + voice
GET /v1/workspaces/{workspaceId}/tools/effective?mode=chat
```

The response lists each channel's runtime tools with their `source` (`direct` or `template`) and `scope` (`organization` or `partner`). When two tools collide on a name, only one is offered to the model: a directly attached org tool wins over a directly attached partner tool, which wins over a template-provided tool — the losers are reported as `shadowed` with the `overriddenBy` winner. The plain `GET .../tools` list stays direct attachments only.

> **Info**
>
> A tool that is referenced by a **published** template version (or by an archived version still pinned to a workspace) cannot be **renamed or deleted** — those requests return `409`. Its `url`, `headers`, schemas, and aliases stay editable, so rotating a credential never requires republishing a template.

## Calling a tool from the prompt

You do not invoke a tool with special syntax. Every tool attached to a workspace is offered to the assistant by its `name` and `description`, and the model decides **on its own** when to call one, filling the `requestSchema` parameters from what the caller said (this is standard function-calling).

Your prompt's job is to **guide** that decision. Refer to the tool by its `name` and say when to use it:

```text
You are a support assistant.
When the caller asks about the status of an order, call the lookup_order
tool with their order number. Do not guess an order status — always use
the tool.
```

Two things make tools reliable:

1. A precise **`description`** on the tool itself — the model leans on it heavily to decide whether the tool applies.
2. Clear **instructions in the prompt** naming the tool and the situations that should trigger it.

## Using a tool's response in the prompt

To feed a tool's result back into the conversation, extract fields from its HTTP response and expose them as variables:

1. **`responseSchema`** — declare which fields to pull out of the tool's JSON response.
2. **`aliases`** — give each extracted field a variable name. An alias maps a name to a Liquid expression pointing at the response, e.g. the alias `orderStatus` → `{{ data.status }}`.
3. **Reference the alias** anywhere in your prompt or the assistant's messages as a Liquid variable: `{{ orderStatus }}`.

For example, with an alias `orderStatus` bound to the response field `data.status`:

```text
The caller's order status is {{ orderStatus }}. Tell them clearly and
ask if there is anything else you can help with.
```

At runtime the assistant calls the tool, extracts the fields defined by `responseSchema`, binds them to your alias names, and substitutes `{{ orderStatus }}` with the live value.

> **Info**
>
> Alias values are Liquid expressions over the tool's response (e.g. `{{ data.user.id }}`). The alias **name** (left side) is what you reference in the prompt; the **expression** (right side) is where the value comes from in the response.

## Tools and voice

Tools are how a [voice assistant](/documentation/core-concepts/voice-integration) takes real actions during a call. Attach the tools a workspace's voice assistant should be able to use, then guide their use from the voice prompt exactly as above.