tools
List Tools
List the tool instances this organization can attach to an agent: its own and the global built-ins. Each carries its input schema, fixed parameters, deadline, whether it asks before running, and the connection or destination it uses; credentials are never included.
The whole list can be long, so narrow it: search keeps the tools whose name
contains it, include_platform=false leaves out the operations imported from
this platform's own server, and limit with offset reads it a page at a
time, in a stable order.
query Parameters
searchinclude_platformlimitoffsetList Tools › Responses
Successful Response
conversation_onlydeadline_secondsdescriptionidnamerequires_confirmationsourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
example_requestsholds_credentialnamespacetagsvisibilityWhether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.
Create Tool
Create a tool instance: one named action with a schema, reusable across agents
and attachable to a node, globally, or as a pre/post hook. Use when the agent needs
one new action to call — one endpoint, or one flow offered as a tool. An instance with
config.endpoint is the HTTP tool, the way the client's own system is reached,
authorized through connection_id and never through a header. Where each person
has their own key, set connection_group instead -- never both -- and every run
uses the connection in that group whose subject_number is the conversation's
number, else whose subject_ref is its subject_ref; nobody matching fails the
run naming the group, with no fallback.
A flow is offered as a tool by an instance with source "builtin" and
config.flow_id set to the flow's id, and nothing else in config: no
endpoint, no connection_id, no header. The platform runs the flow itself, so
there is no address to call it at; never invent one. The agent calls it by the
instance's name with the parameters input_schema declares, and the flow's steps
read them as $ref:trigger.<key>. Testing the instance runs the flow. A minimal
body:
Code
Keep each instance to one action; a job with several steps or a branch is a flow
of instances, not one instance that does everything. A fixed_parameters value written
$ref:conversation.<key> names what the conversation was opened with (number on
either leg of a call, called_number on an inbound one, and number on a WhatsApp
conversation from its first message; contact is who the conversation is with -- the
number on a call, the address on a thread -- in every slot, post included, and
absent for a withheld number; id is the conversation's own id) and is resolved when
the tool runs. A key beginning atteq_ is the platform's
own and cannot be fixed.
For a built-in, read list_builtins first: it gives the current parameters and
which are usually fixed; the instance always carries the current ones, so
input_schema need not be sent.
Create Tool › Request Body
namesourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
conversation_onlydeadline_secondsdescriptionexample_requestsrequires_confirmationtagsCreate Tool › Responses
Successful Response
conversation_onlydeadline_secondsdescriptionidnamerequires_confirmationsourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
example_requestsholds_credentialnamespacetagsvisibilityWhether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.
List Builtins
The first-party built-ins this deployment runs, sorted by name — read it
before creating a built-in instance, so you learn what one accepts instead
of guessing or copying. search keeps the built-ins whose name contains it
(calendar, sheet, mail), which is how to read the one you need rather
than the whole catalogue.
name is what the instance carries in config.builtin; it is the only way
to name a built-in. input_schema is what that built-in accepts today,
fixed keys included, and an instance always carries this schema whenever it
was made — so create_tool need not send one. fixed_parameters names the
keys a person normally fixes on the instance, filling them in
fixed_parameters on the instance so the model never sees them (a calendar
id, a spreadsheet id). provider is the connection provider an instance
needs, so connection_id must point at a connection of that provider, or
null where it needs no authorization of yours. required_flag is the
feature an organization must have for the built-in to be addable at all.
input_schema is null for a built-in nobody creates by hand — one the
platform makes where it applies — and there is nothing else to offer for it.
The same answer for every organization: it describes this deployment and nobody's rows, less any built-in that needs a feature you do not have.
query Parameters
searchList Builtins › Responses
Successful Response
Import Mcp Server
Import every tool an MCP server offers, one configured tool per remote tool. Use when the client's actions already exist behind an MCP server, instead of creating one HTTP tool per endpoint by hand.
Each one arrives with the server's name, description and parameter schema, and with everything else left at its default for you to configure: fix a parameter, set a deadline, rename it. Importing the same server again refreshes what the server owns and keeps what you changed.
The server must be reachable on the public internet, with one exception: this platform's own MCP server, which is how an agent is given the API as tools. A tool calling it acts as whoever is talking to the agent.
Point the import at a connection to authorize it; the imported tools then
carry no credential of their own. The older auth field still works for a
stored bearer, but give one or the other, not both.
A server that will not list its tools is answered 502, naming it as you wrote it and saying what it answered. One that asks you to sign in first is connected from Connections, where your organization holds the MCP door, and imported again choosing that connection.
Reach for it when the client's actions already exist behind an MCP server. For
a plain HTTP API of theirs, create one HTTP tool per endpoint instead
(create_tool with config.endpoint and a connection_id). Each imported
tool is one action like any other instance: attach it to a node, or make it a
step of a flow.
Import Mcp Server › Request Body
Import Mcp Server › Responses
Successful Response
conversation_onlydeadline_secondsdescriptionidnamerequires_confirmationsourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
example_requestsholds_credentialnamespacetagsvisibilityWhether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.
Import Openapi
Turn the operations a client chose from a published API description into
ordinary HTTP tools, one per operation, with one connection (or one group of
connections, or none) for the whole set. Use it when the client's system
publishes an OpenAPI 3.x or Google Discovery description, instead of creating
one HTTP tool per endpoint by hand. Call import-openapi/preview first to get
the key of each operation and the server to choose.
Send the same url or document as the preview, the server for the set, the
operations (keys, at most 50) and connection_id or connection_group.
Nothing is created unless all of it can be: a failure leaves no tool behind.
Importing again with the same server refreshes the tools already made from it
(address, method, parameters, description) and keeps what the client changed
(name, fixed values, deadline, connection, attachments); chosen operations not
made yet are created. A tool whose operation the description no longer holds is
left as it is and listed in missing; nothing is deleted. A different server is
a new set. Each tool is then one action like any other instance: fix a parameter,
set a deadline, attach it to a node, or make it a step of a flow.
Import Openapi › Request Body
Import Openapi › Responses
Successful Response
Preview Openapi Import
Read a published API description and list what could become tools. Writes
nothing, costs nothing, and involves no model. Use it before import-openapi
to let the client choose.
Give the description's url (fetched by the platform, from the public internet
only) or its document (the text of the file). It must be JSON: an OpenAPI 3.x
description or a Google Discovery document; anything else is refused naming what
was found, and so is one larger than 16 MB or holding more than 3,000 operations.
The answer lists, in the description's order, every operations entry that can
become an HTTP tool -- its key (what import-openapi takes), method, path, the
tool name and description it would get, its input_schema and where each
value goes (locations) -- the unimportable ones with the reason (a form or
file body, a webhook, an unresolved reference...), the servers to choose one
from (url is null for a server given relative to a file: the client types it),
each with the connection the description's authorization suggests, and the bound
of operations per import. name_prefix shows the names with a prefix, joined by
_ unless it already ends in _ or - (crm gives crm_get_contacts).
Preview Openapi Import › Request Body
Preview Openapi Import › Responses
Successful Response
kindmax_operations_per_importtitleSuggest Tool Examples
Ask the platform's drafting model for example requests for a tool, written as a person would ask for what it does. Use while writing a tool's card, from its name, description and parameters, when none is written yet or a few more are wanted.
It is paid work, and nothing is stored. The language model's input and output
tokens are recorded against the organization, with no conversation and no tool
execution; the answer is a list to keep, change or drop, saved only by writing it
onto the tool (example_requests on create or update). Never called by an import.
Answers at most what is left of the card's bound after the requests sent in, and
nothing at all, asking no model, when the card is full. Refused with 402 while the
organization may not spend, and with 502 when the model could not answer.
Suggest Tool Examples › Request Body
namedescriptionexample_requestsSuggest Tool Examples › Responses
Successful Response
example_requestsGet Tool
Read one tool instance, own or global built-in: its source, input schema, fixed parameters, deadline, whether it asks before running, and the connection or destination it uses. Credentials are never included.
path Parameters
tool_idGet Tool › Responses
Successful Response
conversation_onlydeadline_secondsdescriptionidnamerequires_confirmationsourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
example_requestsholds_credentialnamespacetagsvisibilityWhether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.
Update Tool
Change a tool's name, description, fixed parameters, or the destination it may
reach. Use when adjusting an existing tool instance's configuration, not when the
agent needs a new action. A fixed_parameters value written
$ref:conversation.<key> names what the conversation was opened with (number
on either leg of a call, called_number on an inbound one, contact on a
channel; id is the conversation's own id) and is resolved when the tool runs. A
key beginning atteq_ is the platform's own and cannot be fixed.
fixed_parameters replaces the whole set: send every value you want fixed, not
only the new one.
path Parameters
tool_idUpdate Tool › Request Body
Update Tool › Responses
Successful Response
conversation_onlydeadline_secondsdescriptionidnamerequires_confirmationsourceWhere a tool comes from. Provenance matters to us (config/auth/billing), never to the LLM — which sees every source identically.
CUSTOM is retired and unreachable, and it is still here. It used to mean
"code written for one organization", and runtime.execution._route refused it
by name because nothing could run one. Code written for one organization now
runs the way every other first-party tool does — BUILTIN, with config
naming which built-in and the composition root binding it (ADR 0014, widened
by ADR 0017) — so there is one dispatch path and this member names none of
them. It is kept rather than removed because tool_source is a native
Postgres enum: dropping a value means recreating the type, rewriting every
dependent column and taking a lock on tools to delete a name no row has any
reason to carry. backend/features/custom_tools/DECISIONS.md D10 records
the trade. A row that somehow carries it is dispatched like any other non-MCP
row and answers execute_http's "this tool has no destination configured",
which is one answer rather than two.
example_requestsholds_credentialnamespacetagsvisibilityWhether the model is shown a tool whenever it is in reach, or only once the conversation has found it (tools.md rules 87-89). A fact about the tool, the same wherever it is attached; never what an agent may do, which attachment decides.
Test Tool
Testing a tool runs it. Use right after create_tool, before the
instance is attached to a node or made a step of a flow, with the parameters a
real call would carry; not for a custom tool's draft, which is
try_custom_tool. Nothing is simulated: the instance executes
against the destination it is configured for, with its own credentials — or,
where it points at a connection, with that connection's authorization. An instance
with config.flow_id runs its flow, every step, as a conversation would, with
these parameters as what the flow is started with.
Testing a tool that sends an email sends the email; testing one that writes
into your own system writes into it. The only thing this supplies is
parameters — pointing the test at a safe environment is your decision to
make, on the instance.
Because the call is real, a provider that refuses the connection's authorization marks that connection broken, exactly as it would during a conversation.
A tool that only exists inside a conversation has nothing to test here and is refused rather than executed, and a test is refused with 402 while the organization may not spend.
A refusal here is the refusal a conversation would get, so it is where a wrong endpoint or a broken connection shows up first.
path Parameters
tool_idTest Tool › Request Body
Test Tool › Responses
Successful Response
attemptscreated_atdetailduration_msidoutcomeHow one execution of a tool ended.
response_bodytool_idList Tool Tests
What this instance's tests did, newest first. Use when reviewing a tool's test history instead of running a new test.
path Parameters
tool_idList Tool Tests › Responses
Successful Response
attemptscreated_atdetailduration_msidoutcomeHow one execution of a tool ended.
response_bodytool_id