VoiceDock Docs
Webhooks

Tool Calls Webhook

Called when the AI assistant needs to execute a custom tool or function during a conversation.

Called when the AI assistant needs to execute a custom tool/function during the conversation.

When It's Called

  1. Assistant has tools defined in llm_config.tools[]
  2. During conversation, LLM decides to call a tool
  3. HMS Sovereign sends POST request with tool call details
  4. Your endpoint returns the result
  5. LLM continues conversation with the result

Timeout

  • Sync tools: Must respond within 10 seconds
  • Async tools: Respond immediately with 200 OK (fire-and-forget)

Request Payload

{
  "message": {
    "type": "tool-calls",
    "timestamp": "2025-12-13T12:00:00.000Z",
    "call": {
      "id": "5c4d030f-43e3-4e65-899e-8148521e660f",
      "type": "inbound_phone_call",
      "status": "in-progress"
    },
    "phone_number": {
      "number": "+31850835037",
      "name": "HMS Sovereign Demo"
    },
    "customer": {
      "number": "+31612345678"
    },
    "tool_call_list": [
      {
        "id": "tool_abc123def456",
        "type": "function",
        "function": {
          "name": "lookup_contact",
          "arguments": {
            "query": "John Smith"
          }
        }
      }
    ]
  }
}

Response Formats

Multiple response formats are supported:

{
  "results": [
    {
      "tool_call_id": "tool_abc123def456",
      "result": {
        "name": "John Smith",
        "email": "john@example.com",
        "phone": "+31612345678"
      }
    }
  ]
}

Simple Format

{
  "result": {
    "name": "John Smith",
    "email": "john@example.com"
  }
}

Direct Format

Your data directly (no wrapper):

{
  "name": "John Smith",
  "email": "john@example.com"
}

Error Format

{
  "error": "Contact not found"
}

Defining Tools

Define tools in your assistant's llm_config.tools[]:

{
  "llm_config": {
    "tools": [
      {
        "name": "lookup_contact",
        "description": "Look up contact information by name or phone number",
        "url": "https://api.example.com/tools/lookup",
        "async": false,
        "async_response": "Event logged successfully.",
        "parameters": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Name or phone number to search"
            }
          },
          "required": ["query"]
        }
      }
    ]
  }
}

Tool Definition Fields

FieldTypeRequiredDescription
namestringYesUnique tool name
descriptionstringYesWhat the tool does (for LLM)
urlstringNoPer-tool URL (overrides assistant webhook_url)
asyncbooleanNoFire-and-forget mode (default: false)
async_responsestringNoMessage for async tools
parametersobjectNoJSON Schema for inputs

Async Tools

For logging or notification tools where you don't need to return a result:

{
  "name": "log_event",
  "description": "Log an event for analytics",
  "async": true,
  "async_response": "Event logged successfully.",
  "parameters": {
    "type": "object",
    "properties": {
      "event_type": { "type": "string" },
      "details": { "type": "string" }
    }
  }
}

The LLM receives the async_response immediately while your webhook processes in the background.

Built-in Tools

End Call Tool

Allow the assistant to end the call:

{
  "type": "end_call"
}

Transfer Call Tool

Allow the assistant to transfer the call:

{
  "type": "transfer_call",
  "destinations": [
    {
      "type": "number",
      "number": "+31612345678",
      "description": "Sales team",
      "message": "I'm transferring you to sales."
    },
    {
      "type": "number",
      "number": "+31687654321",
      "description": "Support team",
      "message": "I'm transferring you to support."
    }
  ]
}

Example Implementation (Node.js)

app.post('/webhooks/tool-calls', async (req, res) => {
  const { message } = req.body;
  const toolCalls = message.tool_call_list;
  
  const results = [];
  
  for (const toolCall of toolCalls) {
    const { id, function: fn } = toolCall;
    
    if (fn.name === 'lookup_contact') {
      const contact = await db.contacts.findOne({ 
        $or: [
          { name: { $regex: fn.arguments.query, $options: 'i' } },
          { phone: fn.arguments.query }
        ]
      });
      
      results.push({
        tool_call_id: id,
        result: contact || { error: 'Contact not found' }
      });
    }
    
    if (fn.name === 'book_appointment') {
      const appointment = await calendar.createAppointment({
        date: fn.arguments.date,
        time: fn.arguments.time,
        customerPhone: message.customer.number
      });
      
      results.push({
        tool_call_id: id,
        result: { 
          success: true, 
          appointmentId: appointment.id,
          confirmation: `Appointment scheduled for ${fn.arguments.date} at ${fn.arguments.time}`
        }
      });
    }
  }
  
  return res.json({ results });
});

See Tool Calls Webhook API for complete schema details.

On this page