> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Tools

> Let your AI agent execute actions by calling your webhooks

# Webhook Tools

Tools allow your AI agent to execute actions by calling your webhooks. The LLM decides when to use a tool based on the conversation context, executes the call, and uses the result in its response.

## What are Agent Tools?

A **Tool** is a webhook endpoint that your agent can call to perform actions or retrieve data. When you define a tool, you specify:

* **Name**: How the agent refers to the tool
* **Description**: When the agent should use it
* **Parameters**: What data to send (JSON Schema)
* **Webhook URL**: Your endpoint that handles the request

The AI model uses the description to decide when to invoke the tool automatically.

## Use Cases

| Tool                    | Description                 | Example Use                             |
| ----------------------- | --------------------------- | --------------------------------------- |
| `check_order_status`    | Look up order tracking info | "Where is my order #12345?"             |
| `create_support_ticket` | Open a support ticket       | "I need help with my account"           |
| `book_appointment`      | Schedule a calendar event   | "Book me for next Tuesday at 2pm"       |
| `get_inventory`         | Check product availability  | "Do you have the blue shirt in medium?" |
| `lookup_customer`       | Retrieve customer data      | "What's my account balance?"            |
| `process_refund`        | Initiate a refund           | "I want a refund for order #123"        |

## Via Dashboard

<Steps>
  <Step title="Navigate to Tools">
    Go to **Senders** > select your sender > **Agent** tab > **Tools** section.
  </Step>

  <Step title="Create New Tool">
    Click **Create Tool** and fill in the form:

    * **Name**: A short identifier (e.g., `check_order_status`)
    * **Description**: When should the agent use this tool? Be specific.
    * **Webhook URL**: Your HTTPS endpoint
  </Step>

  <Step title="Define Parameters">
    Specify the parameters your webhook expects:

    ```json theme={null}
    {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "The order ID to look up"
        }
      },
      "required": ["order_id"]
    }
    ```
  </Step>

  <Step title="Configure Security (Optional)">
    Add a **Webhook Secret** to verify requests are from Zavu. We'll sign each request with this secret.
  </Step>

  <Step title="Test the Tool">
    Use the **Test** button to send a sample request to your webhook and verify it responds correctly.
  </Step>

  <Step title="Enable the Tool">
    Toggle the tool to **Enabled** so the agent can use it.
  </Step>
</Steps>

## Via API

### Create Tool

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Zavudev from "@zavudev/sdk";

  const zavu = new Zavudev({
    apiKey: process.env["ZAVUDEV_API_KEY"],
  });

  const tool = await zavu.senders.agent.tools.create("sender_abc123", {
    name: "check_order_status",
    description: "Look up the status and tracking information for a customer order. Use this when a customer asks about their order status, shipping, or delivery.",
    parameters: {
      type: "object",
      properties: {
        order_id: {
          type: "string",
          description: "The order ID (e.g., ORD-12345)",
        },
      },
      required: ["order_id"],
    },
    webhookUrl: "https://api.yourcompany.com/webhooks/order-status",
    webhookSecret: "whsec_your_secret_here", // Optional
  });

  console.log("Tool created:", tool.id);
  ```

  ```python Python theme={null}
  import os
  from zavudev import Zavudev

  zavu = Zavudev(
      api_key=os.environ.get("ZAVUDEV_API_KEY"),
  )

  tool = zavu.senders.agent.tools.create(
      "sender_abc123",
      name="check_order_status",
      description="Look up the status and tracking information for a customer order. Use this when a customer asks about their order status, shipping, or delivery.",
      parameters={
          "type": "object",
          "properties": {
              "order_id": {
                  "type": "string",
                  "description": "The order ID (e.g., ORD-12345)",
              },
          },
          "required": ["order_id"],
      },
      webhook_url="https://api.yourcompany.com/webhooks/order-status",
      webhook_secret="whsec_your_secret_here",  # Optional
  )

  print(f"Tool created: {tool.id}")
  ```

  ```ruby Ruby theme={null}
  require "zavudev"

  client = Zavudev::Client.new(api_key: ENV["ZAVUDEV_API_KEY"])

  tool = client.senders.agent.tools.create("sender_abc123",
    name: "check_order_status",
    description: "Look up the status and tracking information for a customer order. Use this when a customer asks about their order status, shipping, or delivery.",
    parameters: {
      type: "object",
      properties: {
        order_id: {
          type: "string",
          description: "The order ID (e.g., ORD-12345)"
        }
      },
      required: ["order_id"]
    },
    webhook_url: "https://api.yourcompany.com/webhooks/order-status",
    webhook_secret: "whsec_your_secret_here" # Optional
  )

  puts "Tool created: #{tool.id}"
  ```

  ```go Go theme={null}
  tool, _ := client.Senders.Agent.Tools.Create(context.TODO(), "sender_abc123", zavudev.ToolCreateParams{
  	Name:        zavudev.String("check_order_status"),
  	Description: zavudev.String("Look up the status and tracking information for a customer order. Use this when a customer asks about their order status, shipping, or delivery."),
  	Parameters: &zavudev.ToolParameters{
  		Type: zavudev.String("object"),
  		Properties: map[string]interface{}{
  			"order_id": map[string]interface{}{
  				"type":        "string",
  				"description": "The order ID (e.g., ORD-12345)",
  			},
  		},
  		Required: []string{"order_id"},
  	},
  	WebhookURL:    zavudev.String("https://api.yourcompany.com/webhooks/order-status"),
  	WebhookSecret: zavudev.String("whsec_your_secret_here"), // Optional
  })

  fmt.Printf("Tool created: %s\n", tool.ID)
  ```

  ```php PHP theme={null}
  $tool = $client->senders->agent->tools->create('sender_abc123', [
      'name' => 'check_order_status',
      'description' => 'Look up the status and tracking information for a customer order. Use this when a customer asks about their order status, shipping, or delivery.',
      'parameters' => [
          'type' => 'object',
          'properties' => [
              'order_id' => [
                  'type' => 'string',
                  'description' => 'The order ID (e.g., ORD-12345)',
              ],
          ],
          'required' => ['order_id'],
      ],
      'webhookUrl' => 'https://api.yourcompany.com/webhooks/order-status',
      'webhookSecret' => 'whsec_your_secret_here', // Optional
  ]);

  echo "Tool created: {$tool->id}\n";
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/senders/sender_abc123/agent/tools \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "check_order_status",
      "description": "Look up the status and tracking information for a customer order.",
      "parameters": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string",
            "description": "The order ID (e.g., ORD-12345)"
          }
        },
        "required": ["order_id"]
      },
      "webhookUrl": "https://api.yourcompany.com/webhooks/order-status",
      "webhookSecret": "whsec_your_secret_here"
    }'
  ```
</CodeGroup>

### List Tools

<CodeGroup>
  ```typescript TypeScript theme={null}
  const tools = await zavu.senders.agent.tools.list("sender_abc123");

  for (const tool of tools.items) {
    console.log(`${tool.name} - ${tool.enabled ? "Enabled" : "Disabled"}`);
  }
  ```

  ```python Python theme={null}
  tools = zavu.senders.agent.tools.list("sender_abc123")

  for tool in tools.items:
      status = "Enabled" if tool.enabled else "Disabled"
      print(f"{tool.name} - {status}")
  ```

  ```ruby Ruby theme={null}
  tools = client.senders.agent.tools.list("sender_abc123")

  tools.items.each do |tool|
    status = tool.enabled ? "Enabled" : "Disabled"
    puts "#{tool.name} - #{status}"
  end
  ```

  ```go Go theme={null}
  tools, _ := client.Senders.Agent.Tools.List(context.TODO(), "sender_abc123", zavudev.ToolListParams{})

  for _, tool := range tools.Items {
  	status := "Disabled"
  	if tool.Enabled {
  		status = "Enabled"
  	}
  	fmt.Printf("%s - %s\n", tool.Name, status)
  }
  ```

  ```php PHP theme={null}
  $tools = $client->senders->agent->tools->list('sender_abc123');

  foreach ($tools->items as $tool) {
      $status = $tool->enabled ? 'Enabled' : 'Disabled';
      echo "{$tool->name} - {$status}\n";
  }
  ```

  ```bash cURL theme={null}
  curl https://api.zavu.dev/v1/senders/sender_abc123/agent/tools \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

### Update Tool

<CodeGroup>
  ```typescript TypeScript theme={null}
  const tool = await zavu.senders.agent.tools.update(
    "sender_abc123",
    "tool_xyz789",
    {
      description: "Updated description for better AI understanding",
      enabled: true,
    }
  );
  ```

  ```python Python theme={null}
  tool = zavu.senders.agent.tools.update(
      "sender_abc123",
      "tool_xyz789",
      description="Updated description for better AI understanding",
      enabled=True,
  )
  ```

  ```ruby Ruby theme={null}
  tool = client.senders.agent.tools.update("sender_abc123", "tool_xyz789",
    description: "Updated description for better AI understanding",
    enabled: true
  )
  ```

  ```go Go theme={null}
  tool, _ := client.Senders.Agent.Tools.Update(context.TODO(), "sender_abc123", "tool_xyz789", zavudev.ToolUpdateParams{
  	Description: zavudev.String("Updated description for better AI understanding"),
  	Enabled:     zavudev.Bool(true),
  })
  ```

  ```php PHP theme={null}
  $tool = $client->senders->agent->tools->update('sender_abc123', 'tool_xyz789', [
      'description' => 'Updated description for better AI understanding',
      'enabled' => true,
  ]);
  ```

  ```bash cURL theme={null}
  curl -X PATCH https://api.zavu.dev/v1/senders/sender_abc123/agent/tools/tool_xyz789 \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"description": "Updated description", "enabled": true}'
  ```
</CodeGroup>

### Delete Tool

<CodeGroup>
  ```typescript TypeScript theme={null}
  await zavu.senders.agent.tools.delete("sender_abc123", "tool_xyz789");
  ```

  ```python Python theme={null}
  zavu.senders.agent.tools.delete("sender_abc123", "tool_xyz789")
  ```

  ```ruby Ruby theme={null}
  client.senders.agent.tools.delete("sender_abc123", "tool_xyz789")
  ```

  ```go Go theme={null}
  client.Senders.Agent.Tools.Delete(context.TODO(), "sender_abc123", "tool_xyz789")
  ```

  ```php PHP theme={null}
  $client->senders->agent->tools->delete('sender_abc123', 'tool_xyz789');
  ```

  ```bash cURL theme={null}
  curl -X DELETE https://api.zavu.dev/v1/senders/sender_abc123/agent/tools/tool_xyz789 \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

## Webhook Payload

When the agent invokes a tool, your webhook receives a POST request:

```json theme={null}
{
  "tool": "check_order_status",
  "arguments": {
    "order_id": "ORD-12345"
  },
  "context": {
    "messageId": "msg_abc123",
    "contactPhone": "+14155551234",
    "sessionId": "session_xyz789"
  },
  "timestamp": 1703001234567
}
```

### Headers

| Header             | Description                                             |
| ------------------ | ------------------------------------------------------- |
| `Content-Type`     | `application/json`                                      |
| `X-Zavu-Tool`      | Tool name being invoked                                 |
| `X-Zavu-Timestamp` | Unix timestamp of the request                           |
| `X-Zavu-Signature` | HMAC-SHA256 signature (if webhook secret is configured) |

## Webhook Response

Your webhook should return JSON with the result:

```json theme={null}
{
  "status": "shipped",
  "carrier": "FedEx",
  "trackingNumber": "7891234567890",
  "estimatedDelivery": "2024-01-15"
}
```

The agent will incorporate this data into its response to the user.

### Error Responses

Return appropriate HTTP status codes for errors:

```json theme={null}
{
  "error": "Order not found",
  "code": "ORDER_NOT_FOUND"
}
```

The agent will handle errors gracefully and inform the user appropriately.

## Signature Verification

If you configured a webhook secret, verify the signature to ensure requests are from Zavu:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import crypto from "crypto";

  function verifySignature(
    payload: string,
    signature: string,
    secret: string
  ): boolean {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(payload)
      .digest("hex");
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected)
    );
  }

  // In your webhook handler
  app.post("/webhooks/order-status", (req, res) => {
    const signature = req.headers["x-zavu-signature"];
    const payload = JSON.stringify(req.body);

    if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    // Process the request...
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(payload: str, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(),
          payload.encode(),
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected)

  # In your webhook handler (Flask example)
  @app.route("/webhooks/order-status", methods=["POST"])
  def handle_order_status():
      signature = request.headers.get("X-Zavu-Signature")
      payload = request.get_data(as_text=True)

      if not verify_signature(payload, signature, WEBHOOK_SECRET):
          return {"error": "Invalid signature"}, 401

      # Process the request...
  ```

  ```ruby Ruby theme={null}
  require "openssl"

  def verify_signature(payload, signature, secret)
    expected = OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
    Rack::Utils.secure_compare(signature, expected)
  end

  # In your webhook handler (Sinatra example)
  post "/webhooks/order-status" do
    signature = request.env["HTTP_X_ZAVU_SIGNATURE"]
    payload = request.body.read

    unless verify_signature(payload, signature, ENV["WEBHOOK_SECRET"])
      halt 401, { error: "Invalid signature" }.to_json
    end

    # Process the request...
  end
  ```

  ```go Go theme={null}
  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/hex"
  	"io"
  	"net/http"
  )

  func verifySignature(payload, signature, secret string) bool {
  	mac := hmac.New(sha256.New, []byte(secret))
  	mac.Write([]byte(payload))
  	expected := hex.EncodeToString(mac.Sum(nil))
  	return hmac.Equal([]byte(signature), []byte(expected))
  }

  // In your webhook handler
  http.HandleFunc("/webhooks/order-status", func(w http.ResponseWriter, r *http.Request) {
  	signature := r.Header.Get("X-Zavu-Signature")
  	body, _ := io.ReadAll(r.Body)

  	if !verifySignature(string(body), signature, os.Getenv("WEBHOOK_SECRET")) {
  		http.Error(w, `{"error":"Invalid signature"}`, http.StatusUnauthorized)
  		return
  	}

  	// Process the request...
  })
  ```

  ```php PHP theme={null}
  function verifySignature(string $payload, string $signature, string $secret): bool {
      $expected = hash_hmac('sha256', $payload, $secret);
      return hash_equals($expected, $signature);
  }

  // In your webhook handler
  $signature = $_SERVER['HTTP_X_ZAVU_SIGNATURE'] ?? '';
  $payload = file_get_contents('php://input');

  if (!verifySignature($payload, $signature, getenv('WEBHOOK_SECRET'))) {
      http_response_code(401);
      echo json_encode(['error' => 'Invalid signature']);
      exit;
  }

  // Process the request...
  ```
</CodeGroup>

## Tool Execution Flow

```
Customer: "Where is my order #12345?"
                    |
                    v
            +---------------+
            |   AI Agent    |
            | Analyzes msg  |
            +---------------+
                    |
                    v
            +---------------+
            | Decides to    |
            | use tool      |
            +---------------+
                    |
                    v
            +---------------+
            | Calls webhook |
            | with order_id |
            +---------------+
                    |
                    v
            +---------------+
            | Your Server   |
            | Returns data  |
            +---------------+
                    |
                    v
            +---------------+
            | AI formulates |
            | response      |
            +---------------+
                    |
                    v
Customer: "Your order #12345 shipped via FedEx
           and will arrive January 15th."
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Write Clear Descriptions" icon="pen">
    The AI uses the description to decide when to use the tool. Be specific about when it should and shouldn't be used.
  </Card>

  <Card title="Use HTTPS" icon="lock">
    Webhook URLs must use HTTPS for security. We reject HTTP endpoints.
  </Card>

  <Card title="Handle Errors Gracefully" icon="shield-check">
    Return meaningful error messages so the agent can inform the user appropriately.
  </Card>

  <Card title="Respond Quickly" icon="bolt">
    Keep webhook response times under 10 seconds. The user is waiting for a response.
  </Card>
</CardGroup>

### Example Tool Descriptions

**Good description:**

```text theme={null}
Look up the status and tracking information for a customer order.
Use when the customer asks about their order status, shipping progress,
delivery estimate, or tracking number. Requires the order ID which
typically starts with "ORD-" followed by numbers.
```

**Poor description:**

```text theme={null}
Get order info.
```

<Warning>
  Poor descriptions lead to tools being used incorrectly or not at all. Invest time in writing clear, detailed descriptions.
</Warning>

## Security Considerations

1. **Always use HTTPS** for webhook endpoints
2. **Verify signatures** using the webhook secret
3. **Validate parameters** before processing
4. **Rate limit** your webhook endpoints
5. **Log requests** for debugging and auditing
6. **Never expose** sensitive data in tool responses that shouldn't be shared with customers

## Next Steps

<CardGroup cols={2}>
  <Card title="Add Knowledge Base" icon="book" href="/guides/ai-agents/knowledge-base">
    Let your agent answer questions from documents
  </Card>

  <Card title="Create Flows" icon="diagram-project" href="/guides/ai-agents/flows">
    Build structured conversation paths that use tools
  </Card>
</CardGroup>
