Skip to content

Guides

Tools

Define function-calling tools that let your assistant call your backend during a call.

A tool gives your assistant a structured action it can take mid-conversation — look up a menu, submit a reservation, transfer the caller. Each tool is an OpenAI-style function definition plus step-by-step instructions, and a serverUrl on your backend that receives the final payload when the assistant fires the tool.

Tools are tenant-scoped: define each one for the specific use case of your assistant, then attach it via the assistant's toolIds field.

MethodPathPurpose
GET/toolsList tools
GET/tools/{id}Get one tool
POST/toolsCreate a tool
PUT/tools/{id}Replace a tool
DELETE/tools/{id}Delete a tool

Anatomy of a tool#

FieldRequiredDescription
nameNoFriendly display name (e.g. "GetMenu", "SubmitReservation").
serverUrlYes, except sendDtmfYour endpoint that receives the tool payload when it executes (e.g. https://api.myapp.com/v1/tools/get-menu). sendDtmf tools don't call a server, so it may be omitted.
bodyYesThe function definition — see below.
instructionYesStep-by-step guidance controlling how the assistant uses the tool — see below.

body — the function definition#

body.type is "function" (a regular tool), "dynamicTransfer" (a directory-driven transfer — see Dynamic transfer), or "sendDtmf" (a keypad press — see the API reference for its digits / codes fields).

For a function tool:

FieldRequiredDescription
body.function.nameYesFunction name, max 64 characters (e.g. getMenu).
body.function.descriptionYesWhat the function does. The model uses this to decide when to call the tool — be explicit.
body.function.parametersYesA JSON Schema object (type must be "object") describing the expected parameters, with properties and optional required.
body.asyncNoBoolean.
body.messagesNoTool messages, e.g. a request-start filler spoken while the tool runs. Each message has a type, a mode ("static" uses content, "generated" lets the AI phrase it), and optional content.

instruction — controlling the flow#

The instruction is required and tightly controls the assistant's behavior around the tool:

FieldRequiredDescription
titleYesShort title for the instruction.
descriptionYesWhat the instruction covers.
stepsYesAt least one step. Each step has a title, an action (what the assistant should do), a required wait_for_response boolean, optional additional_rules (strings), and an optional step_id (stable identifier, max 100 chars, used by per-assistant step overrides).

Create a tool#

POST /tools

bash
curl -X POST https://core-api.heysadie.ai/tools \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GetMenu",
    "serverUrl": "https://api.myapp.com/v1/tools/get-menu",
    "body": {
      "type": "function",
      "function": {
        "name": "getMenu",
        "description": "Fetches the current menu for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "locationId": {
              "type": "string",
              "description": "Unique ID of the restaurant location"
            }
          },
          "required": ["locationId"]
        }
      }
    },
    "instruction": {
      "title": "Fetch the menu",
      "description": "How to answer menu questions using getMenu.",
      "steps": [
        {
          "title": "Check requirements",
          "action": "Ensure the caller has specified which location they are asking about.",
          "wait_for_response": true
        },
        {
          "title": "Call the tool and respond",
          "action": "Invoke getMenu with the locationId, then summarize the results back to the caller.",
          "wait_for_response": false
        }
      ]
    }
  }'

Then attach it to an assistant by including its ID in the assistant's toolIds (see Assistants).

serverUrl-based execution#

When the model decides to call the tool, the collected arguments are sent to your serverUrl. Your server does the real work and responds; the assistant carries on with the result.

serverUrl values are validated for safety at write time — unresolvable or disallowed hosts are rejected.

TODO — verify

The exact request payload and headers delivered to a function tool's serverUrl at call time are produced by the call runtime, not this API, and are not specified in this repository's public docs. For the dynamicTransfer type the contract is fully documented in Dynamic transfer (payload of call_id, arguments, metadata plus the x-sadie-core-secret header); confirm whether plain function tools use the same envelope before relying on it.

List and search tools#

GET /tools supports:

Query paramTypeRequiredDescription
limitnumberNoPage size (max 500, default 50)
offsetnumberNoItems to skip
searchstringNoFilter by name
includeGlobal"true" | "false"NoInclude platform-provided global tools. Default false.
Terminal
curl "https://core-api.heysadie.ai/tools?search=menu&includeGlobal=true" \
  -H "Authorization: ApiKey YOUR_API_KEY"

Update and delete#

PUT /tools/{id} replaces the tool with the same body as create. DELETE /tools/{id} removes it.

Both are ownership-checked: attempting to modify or delete a tool owned by another tenant (including global tools visible via includeGlobal) returns 403 Forbidden.

Next steps#

  • Dynamic transfer — the dynamicTransfer tool type and your directory-server contract.
  • Task groups — reference tools by name from structured task flows.