curl --request POST \
--url https://app.famulor.io/api/v1/tools \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": {
"Authorization": "Bearer sk_live_..."
},
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": true,
"source": "llm",
"location": "query"
}
],
"timeout_ms": 10000,
"async": false,
"filler_phrase": "One moment, I am checking that for you."
}
}
'import requests
url = "https://app.famulor.io/api/v1/tools"
payload = {
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": { "Authorization": "Bearer sk_live_..." },
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": True,
"source": "llm",
"location": "query"
}
],
"timeout_ms": 10000,
"async": False,
"filler_phrase": "One moment, I am checking that for you."
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'check_order_status',
description: 'Look up the status of a customer order by order number.',
type: 'api',
config: {
method: 'GET',
url: 'https://api.example.com/orders/status',
headers: {Authorization: 'Bearer sk_live_...'},
parameters: [
{
name: 'order_number',
type: 'string',
description: 'The order number',
required: true,
source: 'llm',
location: 'query'
}
],
timeout_ms: 10000,
async: false,
filler_phrase: 'One moment, I am checking that for you.'
}
})
};
fetch('https://app.famulor.io/api/v1/tools', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.famulor.io/api/v1/tools",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'check_order_status',
'description' => 'Look up the status of a customer order by order number.',
'type' => 'api',
'config' => [
'method' => 'GET',
'url' => 'https://api.example.com/orders/status',
'headers' => [
'Authorization' => 'Bearer sk_live_...'
],
'parameters' => [
[
'name' => 'order_number',
'type' => 'string',
'description' => 'The order number',
'required' => true,
'source' => 'llm',
'location' => 'query'
]
],
'timeout_ms' => 10000,
'async' => false,
'filler_phrase' => 'One moment, I am checking that for you.'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.famulor.io/api/v1/tools"
payload := strings.NewReader("{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.famulor.io/api/v1/tools")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.famulor.io/api/v1/tools")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "f1b2c3d4-0000-4000-8000-000000000010",
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": {
"Authorization": "•••"
},
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": true,
"source": "llm",
"location": "query"
}
],
"static_values": {},
"response_mapping": {},
"timeout_ms": 10000,
"async": false,
"filler_phrase": "One moment, I am checking that for you."
},
"is_active": true,
"created_at": "2026-07-01T09:00:00Z",
"updated_at": "2026-07-01T09:00:00Z"
}
}Create a tool
Create one centrally managed API, MCP, or built-in tool. Requires an admin-level credential because configs may contain secrets. Assign it via PUT /assistants/{id}/tools. Required scope: assistants:write.
curl --request POST \
--url https://app.famulor.io/api/v1/tools \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": {
"Authorization": "Bearer sk_live_..."
},
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": true,
"source": "llm",
"location": "query"
}
],
"timeout_ms": 10000,
"async": false,
"filler_phrase": "One moment, I am checking that for you."
}
}
'import requests
url = "https://app.famulor.io/api/v1/tools"
payload = {
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": { "Authorization": "Bearer sk_live_..." },
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": True,
"source": "llm",
"location": "query"
}
],
"timeout_ms": 10000,
"async": False,
"filler_phrase": "One moment, I am checking that for you."
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'check_order_status',
description: 'Look up the status of a customer order by order number.',
type: 'api',
config: {
method: 'GET',
url: 'https://api.example.com/orders/status',
headers: {Authorization: 'Bearer sk_live_...'},
parameters: [
{
name: 'order_number',
type: 'string',
description: 'The order number',
required: true,
source: 'llm',
location: 'query'
}
],
timeout_ms: 10000,
async: false,
filler_phrase: 'One moment, I am checking that for you.'
}
})
};
fetch('https://app.famulor.io/api/v1/tools', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.famulor.io/api/v1/tools",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'check_order_status',
'description' => 'Look up the status of a customer order by order number.',
'type' => 'api',
'config' => [
'method' => 'GET',
'url' => 'https://api.example.com/orders/status',
'headers' => [
'Authorization' => 'Bearer sk_live_...'
],
'parameters' => [
[
'name' => 'order_number',
'type' => 'string',
'description' => 'The order number',
'required' => true,
'source' => 'llm',
'location' => 'query'
]
],
'timeout_ms' => 10000,
'async' => false,
'filler_phrase' => 'One moment, I am checking that for you.'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.famulor.io/api/v1/tools"
payload := strings.NewReader("{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.famulor.io/api/v1/tools")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.famulor.io/api/v1/tools")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"check_order_status\",\n \"description\": \"Look up the status of a customer order by order number.\",\n \"type\": \"api\",\n \"config\": {\n \"method\": \"GET\",\n \"url\": \"https://api.example.com/orders/status\",\n \"headers\": {\n \"Authorization\": \"Bearer sk_live_...\"\n },\n \"parameters\": [\n {\n \"name\": \"order_number\",\n \"type\": \"string\",\n \"description\": \"The order number\",\n \"required\": true,\n \"source\": \"llm\",\n \"location\": \"query\"\n }\n ],\n \"timeout_ms\": 10000,\n \"async\": false,\n \"filler_phrase\": \"One moment, I am checking that for you.\"\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"id": "f1b2c3d4-0000-4000-8000-000000000010",
"name": "check_order_status",
"description": "Look up the status of a customer order by order number.",
"type": "api",
"config": {
"method": "GET",
"url": "https://api.example.com/orders/status",
"headers": {
"Authorization": "•••"
},
"parameters": [
{
"name": "order_number",
"type": "string",
"description": "The order number",
"required": true,
"source": "llm",
"location": "query"
}
],
"static_values": {},
"response_mapping": {},
"timeout_ms": 10000,
"async": false,
"filler_phrase": "One moment, I am checking that for you."
},
"is_active": true,
"created_at": "2026-07-01T09:00:00Z",
"updated_at": "2026-07-01T09:00:00Z"
}
}Authorizations
API key (fam_..., created under Settings → API Keys) or an OAuth 2.0 access token (fam_at_...). REST operations also require API Access for the credential's workspace. Keys can be restricted to scopes such as assistants:read, calls:write, campaigns:write, automations:read, dashboards:read, dashboards:write, leads:write, segments:write, loop:read, loop:write, phone_numbers:write, sip_trunks:write, knowledge:write, voices:read, billing:read, billing:write, settings:write, platform:read, platform:write; a *:write scope implies the matching *:read. Automation and dashboard endpoints also accept the legacy calls:* scope. Keys without scope restrictions have full access within the workspace's available capabilities.
Body
Create/update payload for a reusable tool. On update, type is immutable and sending ••• for a secret config value keeps the stored value unchanged.
Tool name — becomes the LLM function name.
^[a-zA-Z][a-zA-Z0-9_-]{0,63}$api = HTTP API tool, mcp = external MCP server, builtin = built-in capability (call transfer & co.). Immutable after creation.
api, mcp, builtin Type-specific configuration. api: { method, url, headers, parameters[], static_values, response_mapping, timeout_ms, async, filler_phrase, speak_after }. speak_after (default true): when false, the tool completes silently without an LLM spoken reply. mcp: { url, auth_type, auth_header_name, auth_value_encrypted, allowed_tools[], tool_options: { [toolName]: { cancellable, on_duplicate, report_progress } }, timeout_ms }; on_duplicate is allow, reject, replace, or confirm, and replace requires cancellation. builtin: exactly one built-in tool item { type: call_transfer|warm_call_transfer|end_call|…|collect_payment_card|set_variable, description?, stripe_connection_id? for collect_payment_card, allowed_keys? for set_variable, ... } — same shape as one element of an assistant's builtin_tools array; secrets are masked as ••• in responses. For warm_call_transfer, optional post_answer_dtmf accepts up to 32 characters (0-9*#A-D and w, case-insensitive); each w is a fixed half-second pause after answer and before the briefing. Omit or clear it for a direct destination. See BuiltinTool for field constraints. Saved caller-facing announcements, filler phrases and end-call farewells remain editable. With secondary languages configured on the assistant, they guide generated wording in the current supported conversation language without fixed saved audio. Otherwise their existing delivery behavior is preserved. An empty End call farewell adds no speech.
What the tool does — shown to the LLM as the function description.
1000Whether the tool is active (default true).
Required for PATCH to prevent lost concurrent updates.
x >= 1Response
The created tool (secret values masked).
A reusable tool of the account. Secret values inside config (auth values, header values) are always masked as •••.
Show child attributes
Show child attributes