> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.ada.cx/reference/tools/create/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ada.cx/_mcp/server. # Create a tool POST https://example.ada.support/api/v2/tools/ Content-Type: application/json Creates an `api` or `code` tool. The body is the `Tool` envelope without `id` or read-only fields (`inputs[].required`). Request `body` / `headers` and code `environment` are optional. `availability_rules` is the structured Knowledge rule object, never the internal DSL string. `Authorization` headers on `api` tools are stored but stripped on every read. A `code.environment` row with `source=sensitive_value` is write-only: create stores the submitted value (empty means an empty secret). A GET-then-POST clone does not copy the stored secret. Send it again. Reference: https://docs.ada.cx/reference/tools/create ## Authentication - `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer `, where token is your auth token. ## Request ### Body (application/json) This endpoint expects a ToolCreate. - `type` (enum, required) - Allowed values: `api`, `code` - `name` (string, required) - `description` (string, required) - `inputs` (list of ToolInputCreate, optional) - `outputs` (list of ToolOutputCreate, optional) - `enabled` (boolean, optional, default: true) - `direct_use` (boolean, optional, nullable) - `availability_rules` (AvailabilityRule, optional, nullable) — A two-level tree of conditions that determines availability based on variable values during a conversation. The root group has a `match` combinator and a list of conditions or condition groups. A rule may contain at most 1000 conditions in total, counting every condition across the root and all nested condition groups. - `api` (ToolApiBodyCreate, optional) — How an `api` tool calls its HTTP endpoint, on create - `code` (ToolCodeBodyCreate, optional) — The sandboxed Python a `code` tool runs, on create ## Response ### 201 The created tool - `id` (string, required) — The tool's id - `type` (enum, required) — Which kind of tool this is, and which implementation key is present - Allowed values: `api`, `code` - `name` (string, required) — The tool's name, as the model sees it - `description` (string, required) — What the tool does, as the model sees it - `inputs` (list of ToolInput, required) — The inputs the tool declares - `outputs` (list of ToolOutput, required) — The values the tool returns. - `enabled` (boolean, required) — Whether the tool is available to the Agent at all - `direct_use` (boolean, required, nullable) — Whether the Agent may call this tool on its own, rather than only as a step inside a process. - `availability_rules` (AvailabilityRule, required, nullable) — The rule that gates when the tool is offered. `null` when the tool is always available. - `api` (ToolApiBody, optional) — How an `api` tool calls its HTTP endpoint - `code` (ToolCodeBody, optional) — The sandboxed Python a `code` tool runs ## Errors ### 400 Bad Request Error Bad Request - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 401 Unauthorized Error Unauthorized - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 403 Forbidden Error Authorization Error - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 409 Conflict Error Conflict — the name collides with a tool of the other type (api↔code). Same-type api name reuse is not rejected. - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 422 Unprocessable Entity Error Unprocessable — illegal write for this type, or the body failed validation - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 429 Too Many Requests Error Too Many Requests - `errors` (list of ErrorsErrorsItems, required) — A list of errors ### 500 Internal Server Error Internal Server Error - `errors` (list of ErrorsErrorsItems, required) — A list of errors ## Types ### ToolInputCreate An input on create or PATCH. `required` is ignored on write; GET always emits it (`null` for api and code tools). - `name` (string, required) - `id` (string, optional, nullable) — Existing row id from GET. Required to rename a `code` input. `null` / omitted on `api` inputs and on create. - `type` (string, optional, nullable) - `description` (string, optional) - `required` (boolean, optional, nullable) — Ignored on write. GET always emits this field; api and code tools send `null`. Accepted so a GET body can be PATCHed back. ### ToolOutputCreate An output on create. Only `name` is required. - `name` (string, required) - `id` (string, optional, nullable) — Existing row id from GET. Required to rename this output. `null` / omitted on create and on legacy rows. - `key` (string, optional, nullable) - `source` (enum, optional, nullable) - Allowed values: `body`, `status_code` - `is_visible_to_llm` (boolean, optional) - `save_as_variable` (boolean, optional) - `variable` (AvailabilityRuleVariableRef, optional, nullable) — Reference to a variable by its id ### AvailabilityRule A two-level tree of conditions that determines availability based on variable values during a conversation. The root group has a `match` combinator and a list of conditions or condition groups. A rule may contain at most 1000 conditions in total, counting every condition across the root and all nested condition groups. - `match` (enum, required) — Whether all conditions must pass (`all`) or any one condition must pass (`any`). - Allowed values: `all`, `any` - `conditions` (list of AvailabilityRuleConditionsItems, required) — List of conditions or condition groups. Must contain at least one entry. Each entry is either a `Condition` object (with `variable`, `operator`, and optional `value`) or a `ConditionGroup` object (with its own `match` and nested `conditions` list). ### ToolApiBodyCreate How an `api` tool calls its HTTP endpoint, on create - `url` (string, required) - `request` (ToolApiRequestCreate, optional) — HTTP request on create. `method` and `content_type` default in the handler; `body` and `headers` default to empty. ### ToolCodeBodyCreate The sandboxed Python a `code` tool runs, on create - `source_code` (string, required) - `environment` (list of ToolEnvVar, optional) ### ToolInput One input the tool declares. - `id` (string, required, nullable) — Server-generated identity. Stable across renames. Always `null` for `api` inputs. Present for `code` inputs. - `name` (string, required) — The input's name, unique within the tool - `type` (string, required, nullable) — The input's data type. `null` when the tool declares no type for it. - `description` (string, required) — What the input is for, as shown to the model - `required` (boolean, required, nullable) — Whether the tool requires this input. `null` for `api` and `code` tools, which do not model optionality. ### ToolOutput One value the tool returns - `id` (string, required, nullable) — Server-generated identity. Stable across renames. `null` for rows that predate this field. - `name` (string, required) — The output's name, unique within the tool - `key` (string, required, nullable) — Path to the value in the tool's result. `null` when the tool does not address its outputs by path. - `source` (enum, required, nullable) — Which part of the HTTP response the value is read from. `api` tools only; `null` for every other type. - Allowed values: `body`, `status_code` - `is_visible_to_llm` (boolean, required) — Whether the model can read this output - `save_as_variable` (boolean, required) — Whether the value is persisted to a variable - `variable` (ToolOutputVariable, required, nullable) — The variable the value is saved to, referenced by `id`. `null` when the output is not persisted, or when its variable no longer exists. ### ToolApiBody How an `api` tool calls its HTTP endpoint - `url` (string, required) — The request URL. Variable references appear in the `{var:|}` form. - `request` (ToolApiRequest, required) — The HTTP request shape for an `api` tool, nested under `api.request`. ### ToolCodeBody The sandboxed Python a `code` tool runs - `source_code` (string, required) — The tool's Python source - `environment` (list of ToolEnvVar, required) — Environment variables exposed to the sandbox ### ErrorsErrorsItems - `type` (string, required) — The error type - `message` (string, required) — The error message - `details` (string, optional, nullable) — Extra information about the error ### AvailabilityRuleVariableRef Reference to a variable by its id - `id` (string, required) — The variable's id ### AvailabilityRuleConditionsItems ### ToolApiRequestCreate HTTP request on create. `method` and `content_type` default in the handler; `body` and `headers` default to empty. - `method` (enum, optional) - Allowed values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` - `content_type` (enum, optional) - Allowed values: `json`, `xml`, `url_encoded` - `body` (string, optional) - `headers` (list of ToolHeader, optional) ### ToolEnvVar - `key` (string, required) — The environment variable's name - `source` (enum, required) — Where the value comes from. One of `literal`, `variable`, `secret`, or `sensitive_value`. `variable` takes a non-sensitive variable id. `secret` takes the id of any secret-bearing variable: a sensitive variable, or a client-secret-scoped token. Which store a `secret` row resolves from follows that variable's own scope. - Allowed values: `literal`, `variable`, `secret`, `sensitive_value` - `value` (string, required) — The literal value, or the id of the variable or secret, depending on `source`. When `source` is `sensitive_value`, this is write-only: it is stored encrypted and always read back empty. On PATCH, submitting it empty leaves the stored secret unchanged. Remove the row to clear it. On POST, empty stores an empty secret. A GET-then-POST copy does not copy the stored secret. ### ToolOutputVariable The variable the value is saved to, referenced by `id`. `null` when the output is not persisted, or when its variable no longer exists. - `id` (string, required) — The variable's id ### ToolApiRequest The HTTP request shape for an `api` tool, nested under `api.request`. - `method` (enum, required) — The HTTP method - Allowed values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE` - `content_type` (enum, required) — How the request body is encoded - Allowed values: `json`, `xml`, `url_encoded` - `body` (string, required) — The request body template. Variable references appear in the `{var:|}` form. - `headers` (list of ToolHeader, required) — Request headers. `Authorization` is never returned, whether or not the tool sends one. ### AvailabilityRuleCondition A single condition comparing a variable to a value. - `variable` (AvailabilityRuleConditionVariable, required) — The variable to test, referenced by `id`. Look up the ids for your Agent through the variables endpoint. Not every variable can be used in a rule; referencing one that can't returns a `400`. - `operator` (enum, required) — The comparison operator. Unary operators (`is_set`, `is_not_set`) must not include a `value` field. - Allowed values: `equals`, `does_not_equal`, `greater_than`, `less_than`, `starts_with`, `ends_with`, `contains`, `does_not_contain`, `is_set`, `is_not_set` - `value` (AvailabilityRuleConditionValue, optional) — The value to compare against. Omit for unary operators (`is_set`, `is_not_set`). - `case_sensitive` (boolean, optional) — Whether the comparison is case-sensitive. Defaults to `false`. Only meaningful for the equality (`equals`, `does_not_equal`) and string (`starts_with`, `ends_with`, `contains`, `does_not_contain`) operators. Omitted from responses when `false`. ### AvailabilityRuleConditionGroup A nested group of conditions inside a rule's top-level `conditions` list. Condition groups may contain only `Condition` objects — further nesting is not supported. - `match` (enum, required) — Whether all conditions must pass (`all`) or any one condition must pass (`any`). - Allowed values: `all`, `any` - `conditions` (list of AvailabilityRuleCondition, required) — List of conditions inside this condition group. Must contain at least one entry. ### ToolHeader - `name` (string, required) — The header's name - `value` (string, required) — The header's value. Variable references appear in the `{var:|}` form. ### AvailabilityRuleConditionVariable The variable to test, referenced by `id`. Look up the ids for your Agent through the variables endpoint. Not every variable can be used in a rule; referencing one that can't returns a `400`. - `id` (string, required) — The id of the variable. ### AvailabilityRuleConditionValue The value to compare against. Omit for unary operators (`is_set`, `is_not_set`). ## Examples **Request** ```json { "type": "api", "name": "get_order_status", "description": "Look up the status of a customer's order.", "api": { "url": "https://api.example.com/orders/{var:order_id}", "request": { "method": "GET", "content_type": "json" } } } ``` **Response** ```json { "id": "507f1f77bcf86cd799439011", "type": "api", "name": "get_order_status", "description": "Look up the status of a customer's order.", "inputs": [], "outputs": [], "enabled": true, "direct_use": true, "availability_rules": null, "api": { "url": "https://api.example.com/orders/{var:order_id}", "request": { "method": "GET", "content_type": "json", "body": "", "headers": [] } } } ``` **SDK Code** ```python import requests url = "https://example.ada.support/api/v2/tools/" payload = { "type": "api", "name": "get_order_status", "description": "Look up the status of a customer's order.", "api": { "url": "https://api.example.com/orders/{var:order_id}", "request": { "method": "GET", "content_type": "json" } } } headers = { "Authorization": "Bearer ", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://example.ada.support/api/v2/tools/'; const options = { method: 'POST', headers: {Authorization: 'Bearer ', 'Content-Type': 'application/json'}, body: '{"type":"api","name":"get_order_status","description":"Look up the status of a customer\'s order.","api":{"url":"https://api.example.com/orders/{var:order_id}","request":{"method":"GET","content_type":"json"}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://example.ada.support/api/v2/tools/" payload := strings.NewReader("{\n \"type\": \"api\",\n \"name\": \"get_order_status\",\n \"description\": \"Look up the status of a customer's order.\",\n \"api\": {\n \"url\": \"https://api.example.com/orders/{var:order_id}\",\n \"request\": {\n \"method\": \"GET\",\n \"content_type\": \"json\"\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "Bearer ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://example.ada.support/api/v2/tools/") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = 'Bearer ' request["Content-Type"] = 'application/json' request.body = "{\n \"type\": \"api\",\n \"name\": \"get_order_status\",\n \"description\": \"Look up the status of a customer's order.\",\n \"api\": {\n \"url\": \"https://api.example.com/orders/{var:order_id}\",\n \"request\": {\n \"method\": \"GET\",\n \"content_type\": \"json\"\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://example.ada.support/api/v2/tools/") .header("Authorization", "Bearer ") .header("Content-Type", "application/json") .body("{\n \"type\": \"api\",\n \"name\": \"get_order_status\",\n \"description\": \"Look up the status of a customer's order.\",\n \"api\": {\n \"url\": \"https://api.example.com/orders/{var:order_id}\",\n \"request\": {\n \"method\": \"GET\",\n \"content_type\": \"json\"\n }\n }\n}") .asString(); ``` ```php request('POST', 'https://example.ada.support/api/v2/tools/', [ 'body' => '{ "type": "api", "name": "get_order_status", "description": "Look up the status of a customer\'s order.", "api": { "url": "https://api.example.com/orders/{var:order_id}", "request": { "method": "GET", "content_type": "json" } } }', 'headers' => [ 'Authorization' => 'Bearer ', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://example.ada.support/api/v2/tools/"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"type\": \"api\",\n \"name\": \"get_order_status\",\n \"description\": \"Look up the status of a customer's order.\",\n \"api\": {\n \"url\": \"https://api.example.com/orders/{var:order_id}\",\n \"request\": {\n \"method\": \"GET\",\n \"content_type\": \"json\"\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "Bearer ", "Content-Type": "application/json" ] let parameters = [ "type": "api", "name": "get_order_status", "description": "Look up the status of a customer's order.", "api": [ "url": "https://api.example.com/orders/{var:order_id}", "request": [ "method": "GET", "content_type": "json" ] ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://example.ada.support/api/v2/tools/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```