What your agents can call while they reply — your server, signed, or the person’s app over the WebSocket — each turned on per agent. Reads on agent, writes on agent:write.
When and why to call these: the tools guide.
List tools
Section titled “List tools”GET /v1/tools
Your organisation’s tool library, newest first, in one page: at most the platform’s limits.org.tools (GET /v1/platform/limits).
A tool is something an agent’s model may call while it replies: webhook, your server, sent a signed POST with the call’s arguments; or app, the person’s app, called over the agent’s WebSocket. Each is turned on per agent (PUT /v1/agents/{agent_id}/tools), up to the platform’s limits.agent.tools an agent (GET /v1/platform/limits).
curl "https://api.lumavox.ai/v1/tools" \ -H "Authorization: Bearer $API_KEY"Responses
| Status | Description |
|---|---|
200 |
application/json — Every tool |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
data |
array of ToolInfo | |
data[].id |
string | The tool’s id, tl_…. |
data[].name |
string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
data[].description |
string | |
data[].kind |
"webhook", "app" |
webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
data[].parameters |
map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. |
data[].url |
string or null | A webhook’s address; null for an app’s. |
data[].timeout_ms |
integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. |
data[].ask_first |
boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. |
data[].action_only |
boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. |
data[].filler |
string or null | The line said before the call; null for none. |
data[].header_names |
array of string | The names of the headers sent with each call; their values are never shown. |
data[].secret_hint |
string or null | The signing secret’s last four characters; null for an app’s tool. |
data[].agent_ids |
array of string | The agents it is turned on for. |
data[].version |
integer | Moves by one on every save. Send it back as expected_version on an update to have the save refused (409) if somebody else changed the tool since you read it. |
data[].created_at |
string (date-time) | |
data[].updated_at |
string (date-time) | |
data[].recent_failures |
integer | Its failed calls in your agents’ conversations within the platform’s agents.tools.failure_window_minutes — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform’s agents.tools.report_every_minutes, so the count can lag by that much. At the platform’s agents.tools.failures_to_mail in the window, your organisation’s admins are mailed, at most once in its agents.tools.mail_every_hours per tool. Each is on GET /v1/platform/limits. |
data[].last_failed_at |
string (date-time) or null | When a call last failed; null if none has. |
data[].last_error |
string or null | What the model was told of the last failure; null if none has. |
next_cursor |
string or null | Pass as cursor for the next page; null when this is the last. |
Create a tool
Section titled “Create a tool”POST /v1/tools
Needs agent:write.
A webhook tool answers its signing secret (whsec_…) this once: every call carries X-Tool-Signature: t=<unix seconds>,v1=<hex>, the HMAC-SHA256 of <t>.<body> under it. Check it, and that t is within five minutes. headers are sent with every call — your own API token — and stored encrypted.
The tool is checked whole: its name (what the model calls it by, unique in your organisation), its parameters (the JSON Schema subset the model takes), a webhook’s url (https://, public, never this platform’s) and its headers are each a 422 naming the problem.
curl -X POST "https://api.lumavox.ai/v1/tools" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"lookup_order","description":"...","kind":"webhook"}'Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Required | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
description | string | Required | What the tool does and when to call it, in the model’s terms: it reads this to decide. 1–1000 characters. |
kind | "webhook", "app" | Required | webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
parameters | map of any | Its inputs (as above); omitted: it takes none. | |
url | string or null | A webhook’s https:// address, public, and not this platform’s. An app’s tool has none. At most 2048 characters. | |
timeout_ms | integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. | |
ask_first | boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. | |
action_only | boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. | |
filler | string or null | A line the agent says before the call, while it waits: Let me check that for you. null: none. At most 200 characters. | |
headers | map of string or null | A webhook’s own headers, sent with every call (your API token): at most 10, stored encrypted and never shown again — a tool answers their names. Content-Type, User-Agent, X-Tool-* and HTTP’s own are the platform’s. |
Responses
| Status | Description |
|---|---|
201 |
application/json — The tool, with its signing secret shown this once |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
409 |
A tool by that name exists already, or the library holds the platform’s limits.org.tools. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
503 |
This deployment cannot seal a tool’s secret (TOOL_SECRET_KEY unset): webhook tools cannot be saved or called. |
201 response body
| Field | Type | Description |
|---|---|---|
id |
string | The tool’s id, tl_…. |
name |
string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
description |
string | |
kind |
"webhook", "app" |
webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
parameters |
map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. |
url |
string or null | A webhook’s address; null for an app’s. |
timeout_ms |
integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. |
ask_first |
boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. |
action_only |
boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. |
filler |
string or null | The line said before the call; null for none. |
header_names |
array of string | The names of the headers sent with each call; their values are never shown. |
secret_hint |
string or null | The signing secret’s last four characters; null for an app’s tool. |
agent_ids |
array of string | The agents it is turned on for. |
version |
integer | Moves by one on every save. Send it back as expected_version on an update to have the save refused (409) if somebody else changed the tool since you read it. |
created_at |
string (date-time) | |
updated_at |
string (date-time) | |
recent_failures |
integer | Its failed calls in your agents’ conversations within the platform’s agents.tools.failure_window_minutes — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform’s agents.tools.report_every_minutes, so the count can lag by that much. At the platform’s agents.tools.failures_to_mail in the window, your organisation’s admins are mailed, at most once in its agents.tools.mail_every_hours per tool. Each is on GET /v1/platform/limits. |
last_failed_at |
string (date-time) or null | When a call last failed; null if none has. |
last_error |
string or null | What the model was told of the last failure; null if none has. |
secret |
string or null | The signing secret, whsec_…, shown this once: every call is signed with it (X-Tool-Signature). Keep it on your server. null for an app’s tool. |
Read a tool
Section titled “Read a tool”GET /v1/tools/{tool_id}
curl "https://api.lumavox.ai/v1/tools/$TOOL_ID" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Responses
| Status | Description |
|---|---|
200 |
application/json — The tool |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
200 response body
| Field | Type | Description |
|---|---|---|
id |
string | The tool’s id, tl_…. |
name |
string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
description |
string | |
kind |
"webhook", "app" |
webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
parameters |
map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. |
url |
string or null | A webhook’s address; null for an app’s. |
timeout_ms |
integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. |
ask_first |
boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. |
action_only |
boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. |
filler |
string or null | The line said before the call; null for none. |
header_names |
array of string | The names of the headers sent with each call; their values are never shown. |
secret_hint |
string or null | The signing secret’s last four characters; null for an app’s tool. |
agent_ids |
array of string | The agents it is turned on for. |
version |
integer | Moves by one on every save. Send it back as expected_version on an update to have the save refused (409) if somebody else changed the tool since you read it. |
created_at |
string (date-time) | |
updated_at |
string (date-time) | |
recent_failures |
integer | Its failed calls in your agents’ conversations within the platform’s agents.tools.failure_window_minutes — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform’s agents.tools.report_every_minutes, so the count can lag by that much. At the platform’s agents.tools.failures_to_mail in the window, your organisation’s admins are mailed, at most once in its agents.tools.mail_every_hours per tool. Each is on GET /v1/platform/limits. |
last_failed_at |
string (date-time) or null | When a call last failed; null if none has. |
last_error |
string or null | What the model was told of the last failure; null if none has. |
Update a tool
Section titled “Update a tool”PATCH /v1/tools/{tool_id}
Needs agent:write.
A partial update: omitted fields are left alone, an explicit null clears one, and headers replaces them all. kind is fixed.
Conversations already open pick this up within about a minute, at their next turn after the change reaches them. Send expected_version to be refused with 409 if somebody else changed the tool since you read it.
curl -X PATCH "https://api.lumavox.ai/v1/tools/$TOOL_ID" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{}'Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. | |
description | string | What the tool does and when to call it, in the model’s terms: it reads this to decide. 1–1000 characters. | |
parameters | map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. | |
url | string | A webhook’s https:// address, public, and not this platform’s. An app’s tool has none. At most 2048 characters. | |
timeout_ms | integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. | |
ask_first | boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. | |
action_only | boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. | |
filler | string or null | A line the agent says before the call, while it waits: Let me check that for you. null: none. At most 200 characters. | |
headers | map of string or null | Replaces every header; null or {} sends none. | |
expected_version | integer or null | The version you last read. When set and the tool has been changed since, the update is refused with 409 and nothing is written. Omit it to write unconditionally. |
Responses
| Status | Description |
|---|---|
200 |
application/json — The tool as it now stands |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
409 |
expected_version was sent and the tool has been changed since, or the new name is taken. Nothing was written. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
503 |
This deployment cannot seal a tool’s secret (TOOL_SECRET_KEY unset): webhook tools cannot be saved or called. |
200 response body
| Field | Type | Description |
|---|---|---|
id |
string | The tool’s id, tl_…. |
name |
string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
description |
string | |
kind |
"webhook", "app" |
webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
parameters |
map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. |
url |
string or null | A webhook’s address; null for an app’s. |
timeout_ms |
integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. |
ask_first |
boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. |
action_only |
boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. |
filler |
string or null | The line said before the call; null for none. |
header_names |
array of string | The names of the headers sent with each call; their values are never shown. |
secret_hint |
string or null | The signing secret’s last four characters; null for an app’s tool. |
agent_ids |
array of string | The agents it is turned on for. |
version |
integer | Moves by one on every save. Send it back as expected_version on an update to have the save refused (409) if somebody else changed the tool since you read it. |
created_at |
string (date-time) | |
updated_at |
string (date-time) | |
recent_failures |
integer | Its failed calls in your agents’ conversations within the platform’s agents.tools.failure_window_minutes — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform’s agents.tools.report_every_minutes, so the count can lag by that much. At the platform’s agents.tools.failures_to_mail in the window, your organisation’s admins are mailed, at most once in its agents.tools.mail_every_hours per tool. Each is on GET /v1/platform/limits. |
last_failed_at |
string (date-time) or null | When a call last failed; null if none has. |
last_error |
string or null | What the model was told of the last failure; null if none has. |
Delete a tool
Section titled “Delete a tool”DELETE /v1/tools/{tool_id}
Needs agent:write.
Turns it off for every agent that had it; conversations already open stop offering it within about a minute. This cannot be undone.
curl -X DELETE "https://api.lumavox.ai/v1/tools/$TOOL_ID" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Responses
| Status | Description |
|---|---|
204 |
The tool is gone |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
A tool’s recent calls
Section titled “A tool’s recent calls”GET /v1/tools/{tool_id}/calls
Its most recent calls in your agents’ conversations, newest first, from the past seven days: when, in which conversation and for whom, how long each took, and what failed. No inputs or answers: those are each turn’s, in the conversation’s history (tool_calls). A call shows here within about a minute. Sampled at volume: a tool called thousands of times an hour keeps a share of its calls here, never all — never a count to bill on. next_cursor is always null: ask for up to 100 with limit. Answers 503 while this deployment cannot read call logs.
curl "https://api.lumavox.ai/v1/tools/$TOOL_ID/calls" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | How many of the most recent calls, 1 to 100. 1–100. Default 50. |
Responses
| Status | Description |
|---|---|
200 |
application/json — Its most recent calls, newest first |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
502 |
The call log did not answer |
503 |
This deployment cannot read call logs yet (CF_ACCOUNT_ID, and the admin panel’s analytics token) |
200 response body
| Field | Type | Description |
|---|---|---|
data |
array of ToolCall | |
data[].at |
string (date-time) | When the call ended. |
data[].call_id |
string | The call’s id: the call_id your server was sent, and the turn’s tool_calls carry. |
data[].agent_id |
string | |
data[].session_id |
string | The conversation it was called in. |
data[].end_user_id |
string | The person, eu_…, whose conversation it was. |
data[].ok |
boolean | It answered in time, and not with an error. |
data[].duration_ms |
integer | |
data[].error |
string or null | What the model was told failed; null when nothing did. |
next_cursor |
string or null | Pass as cursor for the next page; null when this is the last. |
Rotate a tool’s signing secret
Section titled “Rotate a tool’s signing secret”POST /v1/tools/{tool_id}/secret
Needs agent:write.
A new signing secret, shown this once; the old one stops signing within about a minute, as conversations pick the change up. A webhook tool only.
curl -X POST "https://api.lumavox.ai/v1/tools/$TOOL_ID/secret" \ -H "Authorization: Bearer $API_KEY"Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Responses
| Status | Description |
|---|---|
200 |
application/json — The tool, with its new signing secret |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
422 |
The request failed validation |
429 |
Rate limit exceeded |
503 |
This deployment cannot seal a tool’s secret (TOOL_SECRET_KEY unset): webhook tools cannot be saved or called. |
200 response body
| Field | Type | Description |
|---|---|---|
id |
string | The tool’s id, tl_…. |
name |
string | What the model calls the tool by: 1 to 64 lower-case letters, digits and underscores, starting with a letter. Unique in your organisation; search_knowledge and remember_relation are the platform’s own. |
description |
string | |
kind |
"webhook", "app" |
webhook: your server, sent a signed POST with the call. app: the person’s app, called over the agent’s WebSocket. Fixed at creation. |
parameters |
map of any | Its inputs, as a JSON Schema object: {"type": "object", "properties": {…}, "required": […]}, each property string (with an optional enum), number, integer, boolean, array (with items) or object, each with an optional description. Nothing else is taken: a key outside this set is refused rather than passed on unenforced. |
url |
string or null | A webhook’s address; null for an app’s. |
timeout_ms |
integer | How long a call may take, in milliseconds: 1000 to 30000, 10000 by default. Past it the call failed, and the model is told so. 1000–30000. |
ask_first |
boolean | Never run on the model’s word alone: the agent asks the person first, and the tool runs only after they say yes. |
action_only |
boolean | An app’s tool only: the app is told to act and the reply carries on at once, with no answer awaited. |
filler |
string or null | The line said before the call; null for none. |
header_names |
array of string | The names of the headers sent with each call; their values are never shown. |
secret_hint |
string or null | The signing secret’s last four characters; null for an app’s tool. |
agent_ids |
array of string | The agents it is turned on for. |
version |
integer | Moves by one on every save. Send it back as expected_version on an update to have the save refused (409) if somebody else changed the tool since you read it. |
created_at |
string (date-time) | |
updated_at |
string (date-time) | |
recent_failures |
integer | Its failed calls in your agents’ conversations within the platform’s agents.tools.failure_window_minutes — a deadline missed, an error answered, a server out of reach — counted from the first; 0 once the window passes with none. Each conversation reports its failures at most once in the platform’s agents.tools.report_every_minutes, so the count can lag by that much. At the platform’s agents.tools.failures_to_mail in the window, your organisation’s admins are mailed, at most once in its agents.tools.mail_every_hours per tool. Each is on GET /v1/platform/limits. |
last_failed_at |
string (date-time) or null | When a call last failed; null if none has. |
last_error |
string or null | What the model was told of the last failure; null if none has. |
secret |
string or null | The signing secret, whsec_…, shown this once: every call is signed with it (X-Tool-Signature). Keep it on your server. null for an app’s tool. |
Call a tool once, as a test
Section titled “Call a tool once, as a test”POST /v1/tools/{tool_id}/test
Needs agent:write.
Sends your server one call, exactly as a conversation would — signed, with your headers, under the tool’s timeout — carrying the arguments you give and test: true, with no agent, conversation or person. Answers what came back as the model would read it: the JSON, or the text, cut at the platform’s agents.tools.result_max_bytes (GET /v1/platform/limits), or what failed. A webhook tool only; nothing is charged.
curl -X POST "https://api.lumavox.ai/v1/tools/$TOOL_ID/test" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{}'Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
tool_id | string | Required |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
arguments | map of any | What the call carries as arguments; {} when omitted. |
Responses
| Status | Description |
|---|---|
200 |
application/json — What your server answered, or why it did not |
400 |
The request could not be read: malformed JSON |
401 |
Missing or invalid credentials |
403 |
The key is valid but its scopes do not cover this capability — a key limited to tts gets this from /v1/images rather than a 401 |
404 |
Unknown tool id |
409 |
The tool’s stored secret cannot be opened: TOOL_SECRET_KEY changed since it was saved. Rotate its secret, and save its headers again. |
422 |
The request failed validation |
429 |
Rate limit exceeded |
503 |
This deployment cannot seal a tool’s secret (TOOL_SECRET_KEY unset): webhook tools cannot be saved or called. |
200 response body
| Field | Type | Description |
|---|---|---|
ok |
boolean | Your server answered 2xx within the timeout. |
status |
integer or null | Its HTTP status; null when none came (unreachable, or too slow). |
duration_ms |
integer | From sending to its answer’s last byte. |
result |
any | Its answer: the JSON it sent, else its text, cut at the platform’s agents.tools.result_max_bytes (GET /v1/platform/limits); null for none. |
truncated |
boolean | The answer was longer than the platform’s agents.tools.result_max_bytes and was cut. |
error |
string or null | What failed, as the model would be told; null when it did not. |