Logo
Search
Docs

Create tool

POST https://app.sulus.ai/api/tools

Create and publish a tool (writes to the live platform first)

Authentication

Send a bearer token created on your dashboard's Developer page:

Authorization: Bearer <YOUR_API_KEY>

Request body

name string Required

Tool display name.

Validation: required|string|max:100
Stored in tools.name
type string Required

Tool-type discriminator. Fixed at creation — cannot be changed on update.

Validation: required|string|in:apiRequest,function,transferCall,endCall,dtmf,voicemail,handoff,query,mcp
Stored in tools.type
user_id integer Optional Nullable

Owner assignment — honoured only for admin roles.

Validation: nullable|integer|exists:users,id
Stored in tools.user_id
reseller_id string Optional Nullable

Reseller assignment — honoured only for super admins.

Validation: nullable|uuid|exists:resellers,id
Stored in tools.reseller_id
isGlobal boolean Optional Nullable

Marks the tool a platform Global Tool (no owner). Super-admin only; clears user_id.

Validation: nullable|boolean
functionName string Optional Nullable

The callable name the model uses to invoke the tool. Falls back to a slug of name when omitted.

Validation: nullable|string|max:64|regex:/^[a-zA-Z0-9_-]+$/
Stored in tools.function_def
description string Optional Nullable

Top-level tool description (apiRequest). Function tools keep their description inside function_def.

Validation: nullable|string|max:2000
Stored in tools.description
parameters array Optional Nullable

Function/handoff parameter schema-builder rows: {name, type, description, required, enum}.

Validation: nullable|array
Stored in tools.function_def
parametersLockSchema boolean Optional Nullable

Locks the parameters schema (additionalProperties:false).

Validation: nullable|boolean
Stored in tools.function_def
strict boolean Optional Nullable

Enforce exact schema adherence on the tool call (function tools).

Validation: nullable|boolean
Stored in tools.function_def
server object Optional Nullable

Request configuration (apiRequest) / webhook target (function). See Fields page.

Validation: nullable|array
Stored in tools.server
server.url string Optional Nullable
Validation: nullable|url|max:2000
Stored in tools.server
server.method string Optional Nullable
Validation: nullable|string|in:GET,POST,PUT,PATCH,DELETE
Stored in tools.server
server.timeoutSeconds integer Optional Nullable
Validation: nullable|integer|min:1|max:300
Stored in tools.server
server.headers array Optional Nullable

Rows of {key, value}.

Validation: nullable|array
Stored in tools.server
server.bodyProperties array Optional Nullable

Recursive request-body schema tree — validated as a whole array (see Fields page).

Validation: nullable|array
Stored in tools.server
server.staticFields array Optional Nullable

Rows of {key, type, value} merged into the request body as typed literals.

Validation: nullable|array
Stored in tools.server
server.lockSchema boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.server
server.encryptedFields array Optional Nullable

Header/body field names whose values are stored encrypted.

Validation: nullable|array
Stored in tools.server
server.encryptedFields.* string Optional
Validation: string|max:200
Stored in tools.server
server.backoffPlan object Optional Nullable

Retry policy for failed requests.

Validation: nullable|array
Stored in tools.server
server.backoffPlan.type string Optional Nullable
Validation: nullable|string|in:fixed,exponential
Stored in tools.server
server.backoffPlan.maxRetries integer Optional Nullable
Validation: nullable|integer|min:0|max:10
Stored in tools.server
server.backoffPlan.baseDelaySeconds integer Optional Nullable
Validation: nullable|integer|min:0|max:300
Stored in tools.server
server.backoffPlan.excludedStatusCodes array Optional Nullable
Validation: nullable|array
Stored in tools.server
server.backoffPlan.excludedStatusCodes.* integer Optional
Validation: integer|min:100|max:599
Stored in tools.server
server.credentialId string Optional Nullable

A personal credential's id (see Credentials group) used to authenticate the outbound request.

Validation: nullable|string|max:100
Stored in tools.server
server.staticIpAddressesEnabled boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.server
server.serverEncryptedPaths array Optional Nullable
Validation: nullable|array
Stored in tools.server
server.serverEncryptedPaths.* string Optional
Validation: string|max:200
Stored in tools.server
server.sipInfoDtmfEnabled boolean Optional Nullable

dtmf tools only.

Validation: nullable|boolean
Stored in tools.server
server.beepDetectionEnabled boolean Optional Nullable

voicemail tools only — wait for the answering-machine beep.

Validation: nullable|boolean
Stored in tools.server
server.protocol string Optional Nullable

mcp tools only — transport: shttp (Streamable HTTP, default) or sse (legacy). Sent to the voice platform as metadata.protocol.

Validation: nullable|string|in:shttp,sse
Stored in tools.server
variableExtractionPlan object Optional Nullable

Extracts variables from the API response (apiRequest). See Fields page.

Validation: nullable|array
Stored in tools.variable_extraction_plan
variableExtractionPlan.properties array Optional Nullable

Schema rows: {name, type, description, required, enum}.

Validation: nullable|array
Stored in tools.variable_extraction_plan
variableExtractionPlan.aliases array Optional Nullable

Rows of {key, value}.

Validation: nullable|array
Stored in tools.variable_extraction_plan
destinations array Optional Nullable

Transfer/handoff targets (transferCall, handoff). See Fields page.

Validation: nullable|array
Stored in tools.destinations
destinations.*.type string Optional Nullable
Validation: nullable|string|in:number,sip,assistant,dynamic,squad
Stored in tools.destinations
destinations.*.server object Optional Nullable

Handoff dynamic-destination server config.

Validation: nullable|array
Stored in tools.destinations
destinations.*.server.url string Optional Nullable
Validation: nullable|url|max:2000
Stored in tools.destinations
destinations.*.server.timeoutSeconds integer Optional Nullable
Validation: nullable|integer|min:1|max:300
Stored in tools.destinations
destinations.*.server.headers array Optional Nullable
Validation: nullable|array
Stored in tools.destinations
destinations.*.squadId string Optional Nullable

Handoff squad-destination reference.

Validation: nullable|string|max:100
Stored in tools.destinations
destinations.*.entryAssistantName string Optional Nullable
Validation: nullable|string|max:255
Stored in tools.destinations
destinations.*.number string Optional Nullable
Validation: nullable|string|max:50
Stored in tools.destinations
destinations.*.extension string Optional Nullable
Validation: nullable|string|max:20
Stored in tools.destinations
destinations.*.callerId string Optional Nullable
Validation: nullable|string|max:50
Stored in tools.destinations
destinations.*.sipUri string Optional Nullable
Validation: nullable|string|max:255
Stored in tools.destinations
destinations.*.assistantId string Optional Nullable
Validation: nullable|string|max:100
Stored in tools.destinations
destinations.*.assistantName string Optional Nullable
Validation: nullable|string|max:255
Stored in tools.destinations
destinations.*.transferMode string Optional Nullable
Validation: nullable|string|in:rolling-history,swap-system-message-in-history,swap-system-message-in-history-and-remove-transfer-tool-messages,delete-history
Stored in tools.destinations
destinations.*.message string Optional Nullable
Validation: nullable|string|max:1000
Stored in tools.destinations
destinations.*.description string Optional Nullable
Validation: nullable|string|max:2000
Stored in tools.destinations
destinations.*.numberE164CheckEnabled boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.destinations
destinations.*.sipHeaders array Optional Nullable

Rows of {key, value}.

Validation: nullable|array
Stored in tools.destinations
destinations.*.contextEngineeringPlan object Optional Nullable

Handoff destination context-carry strategy.

Validation: nullable|array
Stored in tools.destinations
destinations.*.contextEngineeringPlan.type string Optional Nullable
Validation: nullable|string|in:none,all,lastNMessages,userAndAssistantMessages,previousAssistantMessages
Stored in tools.destinations
destinations.*.contextEngineeringPlan.maxMessages integer Optional Nullable
Validation: nullable|integer|min:1|max:1000
Stored in tools.destinations
destinations.*.transferPlan object Optional Nullable
Validation: nullable|array
Stored in tools.destinations
destinations.*.transferPlan.mode string Optional Nullable
Validation: nullable|string|max:80
Stored in tools.destinations
destinations.*.transferPlan.message string Optional Nullable
Validation: nullable|string|max:1000
Stored in tools.destinations
destinations.*.transferPlan.sipVerb string Optional Nullable
Validation: nullable|string|in:refer,bye,dial
Stored in tools.destinations
destinations.*.transferPlan.timeout integer Optional Nullable
Validation: nullable|integer|min:1|max:600
Stored in tools.destinations
destinations.*.transferPlan.dialTimeout integer Optional Nullable
Validation: nullable|integer|min:1|max:600
Stored in tools.destinations
destinations.*.transferPlan.holdAudioUrl string Optional Nullable
Validation: nullable|string|max:2000
Stored in tools.destinations
destinations.*.transferPlan.transferCompleteAudioUrl string Optional Nullable
Validation: nullable|string|max:2000
Stored in tools.destinations
destinations.*.transferPlan.twiml string Optional Nullable

Raw call-control markup for the transfer (sipVerb: dial).

Validation: nullable|string|max:4000
Stored in tools.destinations
destinations.*.transferPlan.sipHeadersInReferToEnabled boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.destinations
toolMessages array Optional Nullable

mcp tools only — spoken-message overrides for individual tools on the MCP server. An empty messages list silences that tool.

Validation: nullable|array
Stored in tools.function_def
toolMessages.*.name string Required

The name of the tool on the MCP server.

Validation: required|string|max:64
Stored in tools.function_def
toolMessages.*.messages array Optional Nullable

Same message shape as messages.

Validation: nullable|array
Stored in tools.function_def
knowledgeBases array Optional Nullable

query tools — knowledge bases the assistant can search.

Validation: nullable|array
Stored in tools.function_def
knowledgeBases.*.name string Optional Nullable
Validation: nullable|string|max:255
Stored in tools.function_def
knowledgeBases.*.description string Optional Nullable
Validation: nullable|string|max:2000
Stored in tools.function_def
knowledgeBases.*.provider string Optional Nullable
Validation: nullable|string|in:google
Stored in tools.function_def
knowledgeBases.*.fileIds array Optional Nullable

Array of file ids, or a comma-separated string.

Validation: nullable
Stored in tools.function_def
rejectionPlan object Optional Nullable

Conditions under which the model's tool call is rejected. Supported by every tool type.

Validation: nullable|array
Stored in tools.rejection_plan
rejectionPlan.conditions array Optional Nullable
Validation: nullable|array
Stored in tools.rejection_plan
rejectionPlan.conditions.*.type string Optional Nullable
Validation: nullable|string|in:regex,liquid
Stored in tools.rejection_plan
rejectionPlan.conditions.*.value string Optional Nullable
Validation: nullable|string|max:2000
Stored in tools.rejection_plan
messages array Optional Nullable

Lifecycle messages. See Fields page.

Validation: nullable|array
Stored in tools.messages
messages.*.type string Required
Validation: required_with:messages|string|in:request-start,request-complete,request-failed,request-response-delayed
Stored in tools.messages
messages.*.mode string Optional Nullable

request-start only.

Validation: nullable|string|in:default,none,custom
Stored in tools.messages
messages.*.content string Optional Nullable
Validation: nullable|string|max:1000
Stored in tools.messages
messages.*.blocking boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.messages
messages.*.role string Optional Nullable
Validation: nullable|string|in:assistant,system
Stored in tools.messages
messages.*.endCallAfterSpokenEnabled boolean Optional Nullable
Validation: nullable|boolean
Stored in tools.messages
messages.*.timingMilliseconds integer Optional Nullable

request-response-delayed only.

Validation: nullable|integer|min:100|max:120000
Stored in tools.messages
messages.*.contents array Optional Nullable

Spoken-message variants — one is picked at random.

Validation: nullable|array
Stored in tools.messages
messages.*.contents.*.type string Optional Nullable
Validation: nullable|string|max:20
Stored in tools.messages
messages.*.contents.*.text string Optional Nullable
Validation: nullable|string|max:1000
Stored in tools.messages
messages.*.contents.*.language string Optional Nullable
Validation: nullable|string|max:20
Stored in tools.messages
messages.*.conditions array Optional Nullable

Gating conditions for this message.

Validation: nullable|array
Stored in tools.messages
messages.*.conditions.*.param string Optional Nullable
Validation: nullable|string|max:200
Stored in tools.messages
messages.*.conditions.*.operator string Optional Nullable
Validation: nullable|string|in:eq,neq,gt,gte,lt,lte
Stored in tools.messages
messages.*.conditions.*.value string Optional Nullable
Validation: nullable|string|max:1000
Stored in tools.messages
async boolean Optional Nullable

Run the tool call without blocking the conversation.

Validation: nullable|boolean
Stored in tools.async
metadata object Optional Nullable

Arbitrary metadata.

Validation: nullable|array
Stored in tools.metadata

Errors

StatusMeaningBody
403 Assigning another owner without admin rights {"success":false,"message":"You may only create tools for yourself."}
422 Target owner unknown {"success":false,"message":"Target user not found."}
403 Target owner outside your account {"success":false,"message":"Target user is outside your reseller."}
422 Composition invalid for the live platform {"success":false,"message":"Tool payload invalid: <detail>"}
502 Live platform write failed
500 Live write succeeded but local mirror failed — response carries upstream_id for support
422 Validation failed {"message":"…","errors":{"field":["…"]}}
403 Workspace permission denied
401 Missing or invalid bearer token
403 Feature tools not enabled for the account

Notes

  • Validation messages include: "Tool name is required.", "Unsupported tool type.", "The tool name may only contain letters, numbers, underscores and dashes.", "Please provide a valid request URL."
  • function_def is always populated (even for apiRequest) with the resolved callable name; it additionally carries strict/parameters/parametersLockSchema for function and handoff tools.
  • metadata is stored locally and forwarded to the live platform.
  • coreData mirrors the live voice platform's own tool object; only its id is guaranteed to appear — the rest of its shape is internal and not part of the documented contract (see Overview).
curl --request POST \
  --url https://app.sulus.ai/api/tools \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Check Order Status",
  "type": "apiRequest",
  "description": "Looks up an order by ID and returns its current status.",
  "server": {
    "url": "https://api.example-business.com/orders/lookup",
    "method": "POST",
    "timeoutSeconds": 20,
    "headers": [{"key": "X-Api-Key", "value": "{{env.ORDERS_API_KEY}}"}],
    "bodyProperties": [
      {"name": "orderId", "type": "string", "description": "The order confirmation number", "required": true}
    ],
    "staticFields": [{"key": "source", "type": "string", "value": "voice-assistant"}],
    "lockSchema": false,
    "encryptedFields": ["X-Api-Key"],
    "backoffPlan": {"type": "exponential", "maxRetries": 3, "baseDelaySeconds": 2, "excludedStatusCodes": [400, 404]},
    "credentialId": "3f9c2a10-6b4e-4a2d-9f0a-1c2d3e4f5a6b"
  },
  "variableExtractionPlan": {
    "properties": [
      {"name": "orderStatus", "type": "string", "description": "Current status of the order", "required": false}
    ],
    "aliases": [{"key": "status", "value": "orderStatus"}]
  },
  "rejectionPlan": {
    "conditions": [{"type": "regex", "value": "^(cancelled|refunded)$"}]
  },
  "messages": [
    {"type": "request-start", "mode": "custom", "content": "Let me check on that order for you."},
    {"type": "request-failed", "mode": "custom", "content": "I'\''m having trouble reaching our order system right now."}
  ],
  "async": false,
  "metadata": {"category": "orders"}
}'
import requests

url = "https://app.sulus.ai/api/tools"
headers = {"Authorization": "Bearer <YOUR_API_KEY>"}
payload = {
  "name": "Check Order Status",
  "type": "apiRequest",
  "description": "Looks up an order by ID and returns its current status.",
  "server": {
    "url": "https://api.example-business.com/orders/lookup",
    "method": "POST",
    "timeoutSeconds": 20,
    "headers": [{"key": "X-Api-Key", "value": "{{env.ORDERS_API_KEY}}"}],
    "bodyProperties": [
      {"name": "orderId", "type": "string", "description": "The order confirmation number", "required": true}
    ],
    "staticFields": [{"key": "source", "type": "string", "value": "voice-assistant"}],
    "lockSchema": false,
    "encryptedFields": ["X-Api-Key"],
    "backoffPlan": {"type": "exponential", "maxRetries": 3, "baseDelaySeconds": 2, "excludedStatusCodes": [400, 404]},
    "credentialId": "3f9c2a10-6b4e-4a2d-9f0a-1c2d3e4f5a6b"
  },
  "variableExtractionPlan": {
    "properties": [
      {"name": "orderStatus", "type": "string", "description": "Current status of the order", "required": false}
    ],
    "aliases": [{"key": "status", "value": "orderStatus"}]
  },
  "rejectionPlan": {
    "conditions": [{"type": "regex", "value": "^(cancelled|refunded)$"}]
  },
  "messages": [
    {"type": "request-start", "mode": "custom", "content": "Let me check on that order for you."},
    {"type": "request-failed", "mode": "custom", "content": "I'm having trouble reaching our order system right now."}
  ],
  "async": false,
  "metadata": {"category": "orders"}
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	url := "https://app.sulus.ai/api/tools"
	payload := strings.NewReader(`{
  "name": "Check Order Status",
  "type": "apiRequest",
  "description": "Looks up an order by ID and returns its current status.",
  "server": {
    "url": "https://api.example-business.com/orders/lookup",
    "method": "POST",
    "timeoutSeconds": 20,
    "headers": [{"key": "X-Api-Key", "value": "{{env.ORDERS_API_KEY}}"}],
    "bodyProperties": [
      {"name": "orderId", "type": "string", "description": "The order confirmation number", "required": true}
    ],
    "staticFields": [{"key": "source", "type": "string", "value": "voice-assistant"}],
    "lockSchema": false,
    "encryptedFields": ["X-Api-Key"],
    "backoffPlan": {"type": "exponential", "maxRetries": 3, "baseDelaySeconds": 2, "excludedStatusCodes": [400, 404]},
    "credentialId": "3f9c2a10-6b4e-4a2d-9f0a-1c2d3e4f5a6b"
  },
  "variableExtractionPlan": {
    "properties": [
      {"name": "orderStatus", "type": "string", "description": "Current status of the order", "required": false}
    ],
    "aliases": [{"key": "status", "value": "orderStatus"}]
  },
  "rejectionPlan": {
    "conditions": [{"type": "regex", "value": "^(cancelled|refunded)$"}]
  },
  "messages": [
    {"type": "request-start", "mode": "custom", "content": "Let me check on that order for you."},
    {"type": "request-failed", "mode": "custom", "content": "I'm having trouble reaching our order system right now."}
  ],
  "async": false,
  "metadata": {"category": "orders"}
}`)
	req, _ := http.NewRequest("POST", url, payload)
	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")
	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))
}
Response 201
{
    "success": true,
    "data": {
        "name": "Check Order Status",
        "user_id": 42,
        "external_tool_id": "d4e5f6a7-8b9c-4a1d-9e2f-3a4b5c6d7e8f",
        "status": "published",
        "created_by": 42,
        "reseller_id": "f1e2d3c4-b5a6-4978-8901-234567890abc",
        "workspace_id": "9a8b7c6d-5e4f-4a3b-9c8d-7e6f5a4b3c2d",
        "type": "apiRequest",
        "description": "Looks up an order by ID and returns its current status.",
        "function_def": {
            "name": "check_order_status"
        },
        "server": {
            "url": "https://api.example-business.com/orders/lookup",
            "method": "POST",
            "timeoutSeconds": 20,
            "headers": [
                {
                    "key": "X-Api-Key",
                    "value": "{{env.ORDERS_API_KEY}}"
                }
            ],
            "bodyProperties": [
                {
                    "name": "orderId",
                    "type": "string",
                    "required": true,
                    "description": "The order confirmation number"
                }
            ],
            "staticFields": [
                {
                    "key": "source",
                    "type": "string",
                    "value": "voice-assistant"
                }
            ],
            "encryptedFields": [
                "X-Api-Key"
            ],
            "backoffPlan": {
                "type": "exponential",
                "maxRetries": 3,
                "baseDelaySeconds": 2,
                "excludedStatusCodes": [
                    400,
                    404
                ]
            },
            "credentialId": "3f9c2a10-6b4e-4a2d-9f0a-1c2d3e4f5a6b"
        },
        "variable_extraction_plan": {
            "properties": [
                {
                    "name": "orderStatus",
                    "type": "string",
                    "description": "Current status of the order",
                    "required": false
                }
            ],
            "aliases": [
                {
                    "key": "status",
                    "value": "orderStatus"
                }
            ]
        },
        "destinations": null,
        "rejection_plan": {
            "conditions": [
                {
                    "type": "regex",
                    "value": "^(cancelled|refunded)$"
                }
            ]
        },
        "messages": [
            {
                "type": "request-start",
                "content": "Let me check on that order for you."
            },
            {
                "type": "request-failed",
                "content": "I'm having trouble reaching our order system right now."
            }
        ],
        "metadata": {
            "category": "orders"
        },
        "async": false,
        "created_at": "2026-07-30T14:22:03+00:00",
        "updated_at": "2026-07-30T14:22:03+00:00",
        "user": {
            "id": 42,
            "name": "Jane Cooper",
            "email": "[email protected]"
        },
        "creator": {
            "id": 42,
            "name": "Jane Cooper",
            "email": "[email protected]"
        },
        "read_only": false,
        "coreData": {
            "id": "d4e5f6a7-8b9c-4a1d-9e2f-3a4b5c6d7e8f"
        }
    }
}