ChatbotAssistant

Custom Tools - Complete Documentation

Custom Tools is an advanced feature that allows chatbots to call external APIs via webhooks. This allows your chatbot to perform actions in external systems - from adding leads to CRM, through sending notifications, to updating data in databases. This documentation contains everything you need to create and deploy your own tools.

Prerequisites: To use Custom Tools, you need an active chatbot and a system capable of receiving webhooks (HTTPS endpoint). The system must be accessible from the internet.
1

What are Custom Tools?

Custom Tools are functions that a chatbot can call during a conversation with a user. Each tool is a webhook defined by you that will be called when the chatbot determines it is needed.

Key Features

  • Integration with OpenAI Function Calling - Tools are automatically exposed to the AI model as functions
  • Webhook-based - Each tool call is an HTTP POST request to your endpoint
  • Secure - HMAC-SHA256 signing, rate limiting, retry logic, encrypted secrets
  • Flexible - Full control over parameters, request/response mapping, custom headers
  • Monitored - Complete execution history, analytics, performance metrics

Basic Flow

  1. User writes a message to the chatbot (e.g., "Add me to the newsletter")
  2. AI model analyzes the message and decides that a tool call is needed
  3. System sends a webhook to your endpoint with parameters extracted by AI
  4. Your system processes the request (e.g., adds email to database)
  5. You return a response with the operation result
  6. Chatbot delivers the result to the user in a natural way
Example: Imagine a user who writes: "I want to schedule an appointment on Wednesday at 3:00 PM". The chatbot will automatically call your tool create_appointment with parameters date: "2024-01-10" and time: "15:00", and then inform the user of the result.
2

Example Use Cases

Custom Tools open unlimited integration possibilities. Here are the most popular use cases:

🎯 Marketing & Sales

  • Lead Collection - automatic saving of contacts to CRM (Salesforce, HubSpot, Pipedrive)
  • Newsletter Management - subscribing/unsubscribing from mailing lists
  • Quote Generation - creating personalized quotes based on conversation
  • Conversion Tracking - sending events to Google Analytics, Facebook Pixel

📅 Reservations & Calendars

  • Appointment Scheduling - integration with Calendly, Google Calendar, booking systems
  • Availability Check - verification of available time slots in real-time
  • Reservation Management - canceling, rescheduling, confirming meetings

🛒 E-commerce

  • Inventory Check - verification of product availability
  • Order Creation - initiating the purchase process
  • Delivery Status - shipment tracking
  • Product Recommendations - personalized suggestions based on preferences

💬 Communication

  • Sending Notifications - integration with Slack, Microsoft Teams, Discord
  • SMS/Email alerts - sending notifications via Twilio, SendGrid
  • Escalation to Agent - transferring conversation to a human

📊 Analytics & Data

  • Data Retrieval - searching information in databases
  • Report Generation - creating summaries on demand
  • Record Update - modifying data in external systems

🎫 Customer Support

  • Ticket Creation - integration with Zendesk, Freshdesk, JIRA
  • Checking Ticket Status - verification of problem resolution progress
  • Knowledge Base - searching in documentation, FAQ
No Limits: You can create any tool that communicates via HTTP. The only limitation is your imagination and your backend capabilities!
3

How does it work? - System Architecture

Understanding the architecture will help you better utilize Custom Tools and debug potential issues.

Custom Tool Lifecycle

1. Tool Definition

You create a tool in the ChatbotAssistant panel, defining:

  • Function name (e.g., create_lead)
  • Description - clear explanation of when AI should use this tool
  • Parameters - OpenAI schema defining arguments (JSON Schema)
  • Webhook URL - endpoint in your system

2. Registration in OpenAI

The tool is automatically registered in the AI model as a function. When the chatbot talks with a user, the model sees available tools and can decide to call them based on the conversation context.

3. Intent Detection by AI

The model analyzes the user's message and conversation context. If it determines that a tool is needed (e.g., user asks about a product, and you have a tool search_products), AI:

  • Selects the appropriate tool
  • Extracts needed parameters from the user's message
  • Generates function call with arguments

4. Webhook Execution

Our system:

  1. Checks rate limit - whether the call limit has been exceeded
  2. Creates execution log - saves all details to the database
  3. Builds payload - using optional template or default structure
  4. Generates HMAC signature - signs the request (if webhook secret is configured)
  5. Sends HTTP POST to your endpoint with timeout and retry logic
  6. Processes response - maps according to response_mapping (if configured)

5. Processing by Your System

Your endpoint:

  1. Verifies HMAC signature (if using webhook secret)
  2. Processes request - executes business logic
  3. Returns response in JSON format

6. Conversation Continuation

The chatbot receives the tool result and continues the conversation, delivering information to the user in a natural way (e.g., "Great! I've added you to our newsletter. You will receive messages at [email protected]").

Tool Statuses

Status Description Available Actions
DRAFT Tool created but not verified Edit, verify, delete
VERIFYING Webhook URL verification in progress Waiting for result
VERIFIED URL verified, ready for activation Activate, edit, test
ACTIVE Tool active and available for chatbot Deactivate, test, analyze
INACTIVE Temporarily disabled by user Activate, edit
FAILED Verification failed Check error, edit URL, retry verification
Monitoring: Every tool call is logged with complete details: parameters, request/response payload, execution time, errors. You can browse history and analytics in the management panel.
4

Creating a Custom Tool - Step by Step

Creating your first Custom Tool is simple. Follow these steps:

Step 1: Go to Tool Management

  1. Log in to the ChatbotAssistant panel
  2. Select the chatbot for which you want to add a tool
  3. In the chatbot options click "Manage Custom Tools"
  4. Click the button "Create new tool"

Step 2: Fill in Basic Information

Field Required Description Example
Function Name Yes Name used by AI to call the tool. Only letters, numbers and underscore. 3-100 characters. create_newsletter_subscription
Description Yes Clear description of when AI should use this tool. This is crucial - the AI model decides based on the description! "Subscribes user to newsletter. Use when user wants to subscribe, receive updates or newsletter."
Webhook URL Yes HTTPS endpoint in your system that will handle the call. Must be accessible from the internet. https://api.yourcompany.com/webhooks/newsletter
Webhook Secret No Secret key for signing requests (HMAC-SHA256). Strongly recommended for security! whsec_... (generate a secure key)
Description is crucial! The AI model decides whether to use the tool based on the description. Provide clear, specific explanation WHEN to use the tool. Avoid generalities. Good description: "Creates table reservation in restaurant. Use when user wants to reserve a table or schedule a visit." Bad description: "Manages reservations."

Step 3: Define Parameters

Parameters specify what information AI should extract from the conversation and pass to your webhook. We use the OpenAI Function Calling format (JSON Schema).

Detailed description in the next section → Parameter Definition

Step 4: Advanced Configuration (Optional)

  • Timeout - maximum response wait time (configurable)
  • Retry policy - number of retry attempts in case of error (configurable)
  • Rate limiting - limit on number of calls per time unit
  • Custom headers - additional HTTP headers (JSON format)
  • Request template - Jinja2 template for building payload
  • Response mapping - webhook response mapping
  • Fallback response - response returned in case of error

Step 5: Save and Verify

  1. Click "Save tool"
  2. System will create tool with status DRAFT
  3. Click "Verify URL" - system will send a test request to your endpoint
  4. If verification succeeds, status will change to VERIFIED
  5. Click "Activate" to make the tool available for the chatbot
Testing: Before activation, use the "Test" function to check if the webhook works correctly. You can provide sample parameters and see the request/response.
5

Parameter Definition (OpenAI Schema)

Parameters define what data AI should extract from the conversation and pass to your webhook. We use the standard JSON Schema compatible with OpenAI Function Calling API.

Basic Structure

{
  "type": "object",
  "properties": {
    "parameter_name": {
      "type": "string",
      "description": "Parameter description for AI"
    }
  },
  "required": ["parameter_name"]
}

Parameter Types

Type Description Example value
string Text of any length "John Smith"
number Number (integer or float) 42, 3.14
integer Integer number 100
boolean Boolean value true, false
array List of values ["tag1", "tag2"]
object Nested object {"street": "...", "city": "..."}

Example 1: Newsletter subscription

{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "description": "User's email address to subscribe to newsletter"
    },
    "full_name": {
      "type": "string",
      "description": "User's full name (optional)"
    },
    "interests": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "List of user's interest categories (e.g., technology, marketing)"
    }
  },
  "required": ["email"]
}

Example 2: Table reservation

{
  "type": "object",
  "properties": {
    "date": {
      "type": "string",
      "description": "Reservation date in YYYY-MM-DD format"
    },
    "time": {
      "type": "string",
      "description": "Reservation time in HH:MM format"
    },
    "guests_count": {
      "type": "integer",
      "description": "Number of people (minimum 1)"
    },
    "phone": {
      "type": "string",
      "description": "Contact phone number"
    },
    "special_requests": {
      "type": "string",
      "description": "Special requests or notes (optional)"
    }
  },
  "required": ["date", "time", "guests_count", "phone"]
}

Example 3: Creating a lead in CRM

{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "description": "Potential client's email address"
    },
    "company_name": {
      "type": "string",
      "description": "Company name"
    },
    "industry": {
      "type": "string",
      "enum": ["IT", "Finance", "Healthcare", "Retail", "Other"],
      "description": "Company industry"
    },
    "budget_range": {
      "type": "string",
      "enum": ["< 10k", "10k-50k", "50k-100k", "> 100k"],
      "description": "Estimated project budget"
    },
    "contact_preference": {
      "type": "string",
      "enum": ["email", "phone", "meeting"],
      "description": "Preferred contact method"
    },
    "notes": {
      "type": "string",
      "description": "Additional notes from conversation"
    }
  },
  "required": ["email", "company_name"]
}

Advanced Properties

Property Description Example
enum Restriction to specific values "enum": ["small", "medium", "large"]
minimum/maximum Number range restriction "minimum": 1, "maximum": 100
pattern Regex pattern for string "pattern": "^[0-9]{9}$"
minLength/maxLength String length "minLength": 3, "maxLength": 50
items Type of elements in array "items": {"type": "string"}
Important tips:
  • description is crucial - AI uses it to understand what to extract
  • Be specific - provide date format, units, expected values
  • Use enum when possible - this reduces errors and improves results
  • Mark required - AI will try to obtain this data before calling
Check: You can validate your schema online using JSON Schema validator tools. Make sure the schema is correct before saving!
6

Webhook URL Verification

Before activating the tool, the system must verify that your endpoint is accessible and properly configured. Verification ensures that the webhook will work when the chatbot needs it.

Verification Process

  1. You click the button "Verify URL" in the management panel
  2. System generates a unique verification_token
  3. Sends a POST request to your endpoint:
POST https://your-endpoint.com/webhook
Content-Type: application/json

{
  "verification_token": "abc123def456...",
  "tool_name": "your_tool_name",
  "action": "verify"
}

4. Your endpoint should respond with code 200 OK and return the token:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": true,
  "verification_token": "abc123def456..."
}

5. System checks if the returned token matches the sent one

6. If yes - status changes to VERIFIED

Implementation Example (Python/Flask)

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.json

    # URL Verification
    if data.get('action') == 'verify':
        return jsonify({
            'success': True,
            'verification_token': data.get('verification_token')
        }), 200

    # Normal tool handling
    # ... your logic ...

    return jsonify({'success': True, 'result': 'OK'}), 200

Implementation Example (Node.js/Express)

app.post('/webhook', (req, res) => {
    const data = req.body;

    // URL Verification
    if (data.action === 'verify') {
        return res.json({
            success: true,
            verification_token: data.verification_token
        });
    }

    // Normal tool handling
    // ... your logic ...

    res.json({ success: true, result: 'OK' });
});

Common verification problems

Problem Cause Solution
Timeout Endpoint doesn't respond within 10 seconds Check if URL is correct and accessible. Use curl for testing.
SSL Error Invalid HTTPS certificate Make sure the SSL certificate is valid and trusted
404 Not Found Endpoint does not exist Check URL path - make sure route is correctly defined
Token mismatch Returned token does not match Make sure you return EXACTLY the same token you received
Security requirements:
  • Endpoint MUST use HTTPS (not HTTP)
  • Cannot be a local address (localhost, 192.168.x.x, 10.x.x.x)
  • SSL certificate must be valid and trusted
  • Port must be standard (443) or explicitly specified in URL
7

Webhook Request Format

When chatbot calls your tool, system sends HTTP POST request to your webhook endpoint. Learn the request format to properly handle calls.

Default payload structure

If you have not configured custom request template, we send standard structure:

POST https://your-endpoint.com/webhook
Content-Type: application/json
X-Signature-Timestamp: 1704034800
X-Signature-Nonce: abc123def456...
X-Signature-Hash: hmac_sha256_signature

{
  "tool_name": "create_newsletter_subscription",
  "arguments": {
    "email": "[email protected]",
    "full_name": "John Smith",
    "interests": ["technology", "AI"]
  },
  "timestamp": "2024-01-10T15:30:00+01:00"
}

Payload Fields

Field Type Description
tool_name string Name of called tool
arguments object Parameters extracted by AI according to your schema
timestamp string (ISO 8601) Call timestamp

Custom headers

You can configure custom HTTP headers in tool settings (JSON format):

{
  "X-API-Key": "your-api-key",
  "X-Custom-Header": "value",
  "Authorization": "Bearer token123"
}

Request Body Template (Advanced)

If you need custom payload format, you can use Jinja2 template. Template has access to variable arguments containing parameters:

Example template:

{
  "event": "subscription",
  "data": {
    "subscriber_email": "{{ arguments.email }}",
    "name": "{{ arguments.full_name }}",
    "tags": {{ arguments.interests | tojson }},
    "source": "chatbot",
    "subscribed_at": "{{ now().isoformat() }}"
  }
}

Resulting payload:

{
  "event": "subscription",
  "data": {
    "subscriber_email": "[email protected]",
    "name": "John Smith",
    "tags": ["technology", "AI"],
    "source": "chatbot",
    "subscribed_at": "2024-01-10T15:30:00+01:00"
  }
}
Template security:
  • Template length is limited for security
  • Rendering has timeout protecting against hanging
  • Result size is limited
  • Template is executed in secure environment (no access to file system and server resources)
8

Webhook Security (HMAC-SHA256)

HMAC (Hash-based Message Authentication Code) ensures that webhook comes from us and was not modified during transmission. If you configured webhook secret, every request is signed.

Signature headers

Header Description Example
X-Signature-Timestamp Unix timestamp of call (seconds) 1704034800
X-Signature-Nonce Random identifier preventing replay attacks (32 hex chars) a1b2c3d4e5f6...
X-Signature-Hash HMAC-SHA256 signature 7f8b9c...

Verification Algorithm

  1. Read timestamp, nonce and hash from headers
  2. Check if timestamp is not older than 5 minutes (replay attack protection)
  3. Read raw request body (JSON string)
  4. Compose message: timestamp.nonce.body
  5. Calculate HMAC-SHA256 using your webhook secret
  6. Compare calculated hash with received one

Verification example (Python)

import hmac
import hashlib
import time
from flask import request, abort

WEBHOOK_SECRET = "your-webhook-secret"
MAX_TIMESTAMP_AGE = 300  # Recommended: a few minutes

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 1. Get headers
    timestamp = request.headers.get('X-Signature-Timestamp')
    nonce = request.headers.get('X-Signature-Nonce')
    received_hash = request.headers.get('X-Signature-Hash')

    if not all([timestamp, nonce, received_hash]):
        abort(401, 'Missing signature headers')

    # 2. Check timestamp validity
    if abs(time.time() - int(timestamp)) > MAX_TIMESTAMP_AGE:
        abort(401, 'Request timestamp too old')

    # 3. Get raw body
    body = request.get_data(as_text=True)

    # 4. Compose message
    message = f"{timestamp}.{nonce}.{body}"

    # 5. Calculate HMAC
    expected_hash = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        message.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    # 6. Compare
    if not hmac.compare_digest(expected_hash, received_hash):
        abort(401, 'Invalid signature')

    # Signature valid - process request
    data = request.json
    # ... your logic ...

    return {'success': True, 'result': 'OK'}

Verification example (Node.js)

const crypto = require('crypto');

const WEBHOOK_SECRET = 'your-webhook-secret';
const MAX_TIMESTAMP_AGE = 300; // 5 minutes

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
    // 1. Get headers
    const timestamp = req.headers['x-signature-timestamp'];
    const nonce = req.headers['x-signature-nonce'];
    const receivedHash = req.headers['x-signature-hash'];

    if (!timestamp || !nonce || !receivedHash) {
        return res.status(401).json({error: 'Missing signature headers'});
    }

    // 2. Check validity
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp)) > MAX_TIMESTAMP_AGE) {
        return res.status(401).json({error: 'Request too old'});
    }

    // 3. Get raw body (as Buffer/string)
    const body = req.body.toString('utf8');

    // 4. Compose message
    const message = `${timestamp}.${nonce}.${body}`;

    // 5. Calculate HMAC
    const expectedHash = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(message)
        .digest('hex');

    // 6. Compare (timing-safe)
    if (!crypto.timingSafeEqual(
        Buffer.from(expectedHash),
        Buffer.from(receivedHash)
    )) {
        return res.status(401).json({error: 'Invalid signature'});
    }

    // Signature valid - process request
    const data = JSON.parse(body);
    // ... your logic ...

    res.json({success: true, result: 'OK'});
});

Verification example (PHP)

<?php
$webhookSecret = 'your-webhook-secret';
$maxTimestampAge = 300; // 5 minutes

// 1. Get headers
$timestamp = $_SERVER['HTTP_X_SIGNATURE_TIMESTAMP'] ?? '';
$nonce = $_SERVER['HTTP_X_SIGNATURE_NONCE'] ?? '';
$receivedHash = $_SERVER['HTTP_X_SIGNATURE_HASH'] ?? '';

if (!$timestamp || !$nonce || !$receivedHash) {
    http_response_code(401);
    die(json_encode(['error' => 'Missing signature headers']));
}

// 2. Check validity
if (abs(time() - intval($timestamp)) > $maxTimestampAge) {
    http_response_code(401);
    die(json_encode(['error' => 'Request too old']));
}

// 3. Get raw body
$body = file_get_contents('php://input');

// 4. Compose message
$message = "{$timestamp}.{$nonce}.{$body}";

// 5. Calculate HMAC
$expectedHash = hash_hmac('sha256', $message, $webhookSecret);

// 6. Compare (timing-safe)
if (!hash_equals($expectedHash, $receivedHash)) {
    http_response_code(401);
    die(json_encode(['error' => 'Invalid signature']));
}

// Signature valid - process request
$data = json_decode($body, true);
// ... your logic ...

echo json_encode(['success' => true, 'result' => 'OK']);
?>
Critical security rules:
  • Always verify signature if you use webhook secret!
  • Check timestamp - reject old requests (recommended: a few minutes)
  • Track nonce - store used nonces in cache to prevent reuse (replay attack)
  • Use timing-safe compare - avoid timing attacks when comparing hashes
  • Protect webhook secret - don't commit to repo, use environment variables or secret manager
Optional, but recommended: Webhook secret is NOT required, but without it anyone can send false request to your endpoint. In production environment ALWAYS use webhook secret!
9

Webhook Response Format

Your endpoint must return JSON response that chatbot will use to continue conversation. Response format is flexible - you can return any structure.

Minimal response (success)

HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": true,
  "result": "User has been added to newsletter!"
}

Error response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": false,
  "error": "This email address is already subscribed to newsletter"
}

Rich response with additional data

{
  "success": true,
  "result": "Reservation confirmed",
  "data": {
    "reservation_id": "RES-12345",
    "date": "2024-01-15",
    "time": "18:00",
    "guests": 4,
    "table_number": "A5",
    "confirmation_code": "CONF789"
  },
  "message": "Your reservation has been confirmed. Confirmation number: CONF789"
}

Technical Requirements

Requirement Value Notes
HTTP status code 200 OK Other codes (4xx, 5xx) treated as error
Content-Type application/json Required
Maximum size Limited Too large responses will be rejected
Timeout Configurable (1-30s) Default 10 seconds

Response Mapping (advanced)

If your API returns complex structure, you can configure response mapping to extract needed fields:

Example - Your API returns:

{
  "status": "ok",
  "data": {
    "user": {
      "id": 123,
      "email": "[email protected]"
    },
    "subscription": {
      "active": true,
      "created_at": "2024-01-10T15:30:00Z"
    }
  },
  "meta": {
    "request_id": "req-abc"
  }
}

Configure mapping (JSON):

{
  "user_id": "data.user.id",
  "email": "data.user.email",
  "is_active": "data.subscription.active",
  "request_id": "meta.request_id"
}

Chatbot will receive:

{
  "success": true,
  "user_id": 123,
  "email": "[email protected]",
  "is_active": true,
  "request_id": "req-abc"
}
Simplification: Response mapping uses simple dot-notation. For more complex transformations, adjust response on your API side.
What does chatbot do with the response? AI model processes returned data and formulates natural response for user. You can return both code (e.g., reservation ID) and descriptive text - AI knows how to use it in conversation.
10

Execution Mode

Custom tools execute synchronously (SYNC). System waits for webhook response before continuing conversation, so chatbot receives result and can use it in response to user.

Synchronous mode characteristics

SYNC
  • Chatbot waits for response from webhook before continuing conversation
  • Maximum timeout: 30 seconds (configurable in range 1-30s, default 10s)
  • Automatic retry on errors - up to 3 attempts with exponential backoff
  • Full logging request/response for each call
  • Webhook response is passed to AI as function result

Timeout configuration

In advanced tool settings you can adjust timeout to your needs:

  • Short timeout (1-5s): Ideal for fast database operations, cache checking
  • Medium timeout (5-15s): Standard API calls, saving to CRM
  • Long timeout (15-30s): Complex operations, integrations with slower systems

Retry policy

System automatically retries failed calls according to configuration:

Attempt Delay Description
1 0s First call - immediate
2 1s First retry
3 2s Second retry
4 4s Third retry (last)

Example Use Cases

  • Product Availability Check - chatbot waits for result and informs user about inventory status
  • Creating leads in CRM - confirmation of contact saved
  • Appointment booking - availability verification and reservation confirmation
  • Data Retrieval from database - searching information and presenting results
  • Integrations with external APIs - getting order status, tracking shipment
Response time optimization: Make sure your webhook responds as quickly as possible. User waits for result, so operations should be optimized. For long-running processes consider returning immediate confirmation and later notification through another channel (email, SMS).
Best practice: Try to keep response time below 3 seconds for best user experience. If operation requires more time, inform user in response (e.g., "Processing your request, this may take a moment...").
11

Advanced Features

Custom Tools offer a range of advanced features allowing precise customization of tool behavior to your needs.

Rate Limiting

Rate limiting protects your endpoint from overload and ensures fair resource utilization. Each tool has configured call limits that you can adjust to your needs.

How does it work?

  • System tracks number of tool calls in specified time period
  • When limit is exceeded, subsequent calls are rejected
  • Chatbot receives information about limit exceeded and can inform user
  • Counter resets automatically after period elapses
Optimization: Set limits adequate to expected traffic and capabilities of your endpoint. Too low limits may frustrate users, too high may overload system.

Retry Logic

System automatically retries failed webhook calls to increase reliability. Retry is applied in the following cases:

  • Timeout - endpoint did not respond in specified time
  • Network errors - connection issues, DNS, SSL
  • 5xx errors - server errors (500, 502, 503, 504)
  • Connection errors - connection refused, reset

Retry is NOT applied for:

  • 4xx errors (400, 401, 404 etc.) - client errors require fixing
  • 200 responses with {"success": false} - logical application error

Exponential Backoff Strategy

Delay between attempts grows exponentially, giving system time to recover and reduces load during failure.

Idempotency: Make sure your endpoint is idempotent - multiple call with same parameters should give same result. Use unique request ID (available in payload) for deduplication on your side.

Fallback Response

Fallback response is the response that chatbot will receive when webhook call fails (after exhausting retries). This allows chatbot to continue conversation with user in a graceful way, instead of showing error.

Example fallback response (JSON):

{
  "success": false,
  "message": "Sorry, we could not process your request at this time. Try again in a moment or contact us by email: [email protected]"
}

Chatbot will use this message to inform user about problem in natural way.

User experience: A well-constructed fallback response should:
  • Explain problem in simple way (without technical details)
  • Suggest alternative solution (email, phone, try later)
  • Apologize for inconvenience
  • Be specific and helpful

Custom Request Templates (Jinja2)

If your API requires specific payload format, you can use Jinja2 template for full control over request structure.

Available variables in template:

Variable Type Description
arguments dict Call parameters extracted by AI
tool_name string Name of called tool
now() function Returns current datetime

Advanced example - CRM integration:

{
  "action": "create_contact",
  "timestamp": "{{ now().isoformat() }}",
  "source": {
    "channel": "chatbot",
    "tool": "{{ tool_name }}"
  },
  "contact": {
    "email": "{{ arguments.email }}",
    "firstName": "{{ arguments.first_name | default('') }}",
    "lastName": "{{ arguments.last_name | default('') }}",
    "phone": "{{ arguments.phone | default('') }}",
    "tags": {{ arguments.interests | default([]) | tojson }},
    "customFields": {
      "leadSource": "chatbot_{{ tool_name }}",
      "capturedAt": "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
    }
  },
  "metadata": {
    "apiVersion": "v2"
  }
}

Available Jinja2 filters:

  • default(value) - default value if variable does not exist
  • upper, lower - change letter case
  • tojson - conversion to JSON
  • length - length of list/string
  • trim - remove white spaces

Response Mapping

Response mapping allows extracting specific fields from webhook response using dot notation.

Example - API returns nested structure:

{
  "status": "success",
  "code": 201,
  "payload": {
    "reservation": {
      "id": "RES-789",
      "confirmationCode": "ABC123",
      "guestDetails": {
        "name": "John Smith",
        "phone": "+48500600700"
      }
    },
    "timestamp": "2024-01-10T15:30:00Z"
  }
}

Mapping (JSON):

{
  "reservation_id": "payload.reservation.id",
  "confirmation": "payload.reservation.confirmationCode",
  "guest_name": "payload.reservation.guestDetails.name",
  "created_at": "payload.timestamp"
}

Chatbot will receive simplified structure:

{
  "success": true,
  "reservation_id": "RES-789",
  "confirmation": "ABC123",
  "guest_name": "John Smith",
  "created_at": "2024-01-10T15:30:00Z"
}
Combining functions: You can combine all advanced functions together. For example: custom request template + response mapping + fallback response + retry logic for full control over integration.
12

Testing and Debugging

Effective testing and debugging are crucial for ensuring Custom Tools reliability. System offers range of tools for diagnosing and solving problems.

Test Function in panel

In the management panel each tool has a button "Test", which allows for quick webhook testing without needing full conversation.

How to test tool:

  1. Go to tool details in Custom Tools panel
  2. Click the button "Test Tool"
  3. Enter example parameter values in JSON format
  4. Click "Execute test"
  5. System will send webhook to your endpoint and display:
    • Sent request (headers + body)
    • Received response (status, headers, body)
    • Execution time
    • Possible errors

Example test payload:

{
  "email": "[email protected]",
  "full_name": "John Test",
  "interests": ["AI", "marketing"]
}
Debugging: Test function is ideal tool for debugging - you can iteratively test different scenarios and immediately see results.

Execution History

Every Custom Tool call is logged with full details. Execution history is your main tool for analysis and debugging.

What is logged?

Information Description
Timestamp Exact date and time of call
Tool name Name of called tool
Arguments Parameters extracted by AI
Request payload Full payload sent to webhook
Request headers All HTTP headers (including HMAC)
Response status HTTP status code (200, 404, 500, etc.)
Response body Response returned by endpoint
Execution time Execution time in milliseconds
Status Success, Failed, Timeout, Rate Limited
Error details Error details if occurred
Retry attempts Number of retries

Filtering and Searching

In execution history you can filter by:

  • Date range - last 24h, 7 days, 30 days, custom
  • Status - only successful, only errors, timeouts
  • Tool - select specific tool
  • Text - search in payloads and responses

Analytics and Metrics

The analytics panel provides aggregated statistics to help with optimization and monitoring:

Available metrics:

  • Number of calls - total, last 24h, trend
  • Success rate - percentage of successful calls
  • Average response time - endpoint performance
  • Error rate - percentage of errors by type
  • Rate limit hits - how many times the limit was exceeded
  • Top errors - most common error messages
  • Time chart - visualization of calls and errors

Local Debugging

During development you may need to test webhook locally. Here are best methods:

Method 1: Tunnels (ngrok, LocalTunnel, Cloudflare Tunnel)

Tunnels expose your local server to internet through temporary HTTPS URL:

# Ngrok
ngrok http 3000

# Will generate URL: https://abc123.ngrok.io
# Use this URL in Custom Tool configuration
Security notice: Tunnels expose your local server to internet. Use them only in development and always implement webhook secret!

Method 2: Webhook testing services (webhook.site, requestbin)

These services give you temporary URL to which you can send webhooks and browse them in browser. Great for initial testing without writing code.

Method 3: Endpoint-side Logging

Implement detailed logging in your endpoint:

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # Log everything
    logger.info(f"Received webhook: {request.method} {request.url}")
    logger.info(f"Headers: {dict(request.headers)}")
    logger.info(f"Body: {request.get_data(as_text=True)}")

    try:
        result = process_webhook(request.json)
        logger.info(f"Success: {result}")
        return jsonify(result)
    except Exception as e:
        logger.error(f"Error: {str(e)}", exc_info=True)
        return jsonify({"error": str(e)}), 500

Pre-production Testing Checklist

  • Tested all parameter scenarios (required, optional, edge cases)
  • Webhook secret configured and verification works
  • Endpoint responds in < 10 seconds (ideally < 3 sec)
  • Error handling returns meaningful messages
  • HTTPS certificate is valid and trusted
  • Rate limiting adjusted to expected traffic
  • Monitoring and alerts configured (Sentry, CloudWatch, etc.)
  • Fallback response configured and tested
  • Endpoint is idempotent (retry-safe)
  • Tested with a real chatbot in conversation
Continuous testing: After deployment, regularly review execution history and analytics. Set alerts for anomalies (error increase, timeout spike, rate limit exceeded).
13

Best Practices

The following best practices will help you create reliable, secure and efficient Custom Tools.

🔒 Security

1. Always use Webhook Secret in production

# Generate a strong, random secret (min. 32 characters)
import secrets
webhook_secret = secrets.token_urlsafe(32)
# E.g.: "whsec_abc123XYZ..."

2. Verify HMAC signature on every request

  • Check timestamp - reject old requests
  • Track nonce in cache to prevent replay attacks
  • Use timing-safe compare when comparing hashes

3. Limit access to endpoint

  • Use firewall rules if possible (whitelist IP)
  • Implement rate limiting on your server
  • Log all failed verification attempts

4. Don't reveal sensitive details in responses

# ❌ Bad - reveals details
{"error": "SQL error at line 42: table 'users' doesn't exist"}

# ✅ Good - general message
{"success": false, "message": "Could not process request"}

5. Store webhook secret securely

  • Use environment variables or secret manager
  • Don't commit secrets to repository
  • Rotate secrets regularly (every 3-6 months)
  • Use different secrets for dev/staging/production

⚡ Performance and reliability

1. Optimize response time

  • Goal: < 3 seconds for 95% of requests
  • Use database indexes for frequently queried columns
  • Cache repeatable queries (Redis, Memcached)
  • Consider async processing for long-running operations

2. Endpoint must be idempotent

# Use unique identifiers for deduplication
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.json
    request_id = data.get('request_id')  # Unique ID from payload

    # Check if you already processed this request
    if cache.exists(f"processed:{request_id}"):
        return cached_response

    # Process...
    result = process_action(data)

    # Save in cache (TTL = 1 hour)
    cache.setex(f"processed:{request_id}", 3600, result)
    return result

3. Graceful error handling

  • Always return HTTP 200 for application errors
  • Use {"success": false, "error": "..."} for logical errors
  • Return 5xx only for real server errors
  • Implement fallback behaviors

4. Monitor and Alert

# Example Sentry integration
import sentry_sdk

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    try:
        return process_webhook(request.json)
    except Exception as e:
        sentry_sdk.capture_exception(e)
        return {"success": false, "message": "Internal error"}, 500

📝 Schema and description design

1. Write clear, specific tool descriptions

# ❌ Bad - too general
"Manages reservations"

# ✅ Good - specific, with examples
"Creates table reservation in restaurant. Use when user wants to reserve a table, schedule a visit or reserve a seat. Requires date, time and number of people."

2. Define precise parameters

  • Use enum when possible (reduces errors)
  • Provide examples in description
  • Specify formats (YYYY-MM-DD, HH:MM, email, etc.)
  • Mark required fields in required
{
  "type": "object",
  "properties": {
    "date": {
      "type": "string",
      "description": "Reservation date in YYYY-MM-DD format, e.g. 2024-01-15",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high", "urgent"],
      "description": "Ticket priority"
    }
  },
  "required": ["date", "priority"]
}

3. Avoid too many parameters

  • Ideal: 3-5 parameters
  • Max: 10 parameters
  • If you need more - consider splitting into multiple tools

🎯 User Experience

1. Messages for users

  • Write clearly and understandably (avoid technical jargon)
  • Be positive and helpful
  • Provide next steps or alternatives
  • Personalize when possible
# ❌ Technically correct, but not user-friendly
{"result": "Record inserted with ID 12345"}

# ✅ User-friendly
{"result": "Great! I've added you to our newsletter. You'll receive our updates at [email protected]"}

2. Fallback responses should be helpful

{
  "success": false,
  "message": "Sorry, our reservations are currently unavailable. You can call us at: +48 123 456 789 or try again in a few minutes."
}

🏗️ Architecture and Organization

1. One tool = one responsibility

  • Avoid universal tools like "manage_everything"
  • Better: create_lead, update_lead, get_lead_status
  • AI better understands specific, specialized tools

2. Name tools clearly

# ❌ Bad
"tool1", "webhook_handler", "api_call"

# ✅ Good
"create_newsletter_subscription"
"check_product_availability"
"book_appointment"

3. Version your API

  • Add version to URL: /api/v1/webhook
  • Or in custom headers: X-API-Version: 1
  • Maintain backwards compatibility when possible

4. Log everything (in dev and prod)

  • Request ID for correlation
  • Input parameters (without sensitive data!)
  • Execution time
  • Errors with stack traces
  • Use structured logging (JSON)

📊 Testing and Deployment

1. Test in staging before production

  • Use separate tools for dev/staging/prod
  • Test with real data (but anonymized)
  • Verify all edge cases
  • Load testing at expected traffic + 50%

2. Deployment checklist

  • ✓ HTTPS certificate valid and automatically renewed
  • ✓ Webhook secret configured
  • ✓ Rate limiting set up
  • ✓ Monitoring and alerts ready
  • ✓ Backup strategy for data
  • ✓ Rollback plan prepared
  • ✓ Documentation for team
Golden rule: Design Custom Tools thinking about the end user, not technical details. If chatbot says "Webhook returned code 500" instead of "I couldn't reserve a table" - something is wrong.
14

Troubleshooting

Encountered a problem with Custom Tools? Below you will find solutions to the most common issues.

❌ Problem: URL verification fails

Symptom:

Tool status is FAILED after verification attempt

Possible causes and solutions:

Cause Solution
Endpoint doesn't respond • Check if server is running (curl https://your-url.com)
• Check server logs
• Make sure the port is open in firewall
SSL/TLS error • Check certificate: openssl s_client -connect your-domain.com:443
• Make sure the certificate is valid and trusted
• Check if the full certificate chain is installed
Timeout • Endpoint must respond within a few seconds
• Make sure verification does not trigger heavy operations
• Check network latency
404 Not Found • Check URL path - is routing correctly configured?
• Test locally: curl -X POST https://your-url.com/webhook -d '{}'
Token mismatch • Make sure you return EXACTLY the same token you received
• Check that you are not modifying the token (trim, encode, etc.)

❌ Problem: Tool is not called by chatbot

Symptom:

Chatbot ignores tool, even though it should call it

Possible causes and solutions:

  • Description is too general or unclear
    → Rewrite description using specific examples and usage scenarios
    → Add keywords that user might use in conversation
  • Tool is inactive
    → Check status - must be ACTIVE
    → If status is DRAFT or INACTIVE - activate tool
  • Parameters are too complex or unclear
    → Simplify schema - AI may have problem extracting too many parameters
    → Add clear descriptions to each parameter
  • Conflict with other tools
    → If you have several similar tools, AI may choose wrong one
    → Differentiate descriptions to be unambiguous
Test: Try asking the chatbot a question that EXPLICITLY refers to the tool function. E.g., for "newsletter" tool write "I want to subscribe to newsletter". If still not working - description needs improvement.

❌ Problem: Webhook timeout

Symptom:

Execution history shows status "Timeout", endpoint didn't respond in time

Solutions:

  • Increase timeout in tool settings (max 30s)
  • Optimize endpoint:
    • Add database indexes
    • Use cache for repeatable queries
    • Profile code to find bottlenecks
  • Consider async processing:
    • Return immediate response ("Processing...")
    • Execute heavy operation in background (Celery, RQ, etc.)
    • Notify user through another channel (email, SMS)

❌ Problem: HMAC signature invalid

Symptom:

Endpoint rejects requests with error "Invalid signature"

Debugging checklist:

  1. Check if you're using correct secret
    # Print secret used in code (only in dev!)
    print(f"Secret: {WEBHOOK_SECRET}")
    
    # Compare with the secret in Custom Tools panel
  2. Check message assembly order
    # Must be: timestamp.nonce.body
    message = f"{timestamp}.{nonce}.{body}"
    
    # NOT: nonce.timestamp.body (wrong!)
    # NOT: timestamp-nonce-body (separator must be a dot!)
  3. Check if you're using raw body
    # ✅ Correct - raw string
    body = request.get_data(as_text=True)
    
    # ❌ Wrong - parsed JSON
    body = json.dumps(request.json)  # This will change key order!
  4. Check hash algorithm
    # Must be HMAC-SHA256
    hash = hmac.new(secret, message, hashlib.sha256).hexdigest()
    
    # Encoding: UTF-8 for secret and message
  5. Compare hashes securely
    # Python
    hmac.compare_digest(expected, received)
    
    # Node.js
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))
    
    # PHP
    hash_equals($expected, $received)

❌ Problem: Rate limit exceeded

Symptom:

Execution history shows "Rate Limited", subsequent calls are blocked

Solutions:

  • Increase limits in tool settings if justified
  • Split functionality - maybe one tool does too much?
  • Implement caching - if many calls return same data
  • Educate users - maybe they're trying to spam?

❌ Problem: AI extracts incorrect parameters

Symptom:

Webhook receives incorrect parameter values or missing data

Solutions:

  • Fix parameter descriptions
    # ❌ Bad - too general
    "date": {"type": "string", "description": "Date"}
    
    # ✅ Good - specific with example
    "date": {
      "type": "string",
      "description": "Appointment date in YYYY-MM-DD format, e.g., 2024-01-15",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
    }
  • Use enum for limited set of values
    "priority": {
      "type": "string",
      "enum": ["low", "normal", "high", "urgent"],
      "description": "Ticket priority level"
    }
  • Add validation constraints
    • minimum, maximum for numbers
    • minLength, maxLength for strings
    • pattern for regex validation
  • Test with different phrasings - see how AI interprets different phrases

❌ Problem: Endpoint returns data but chatbot doesn't use it

Symptom:

Webhook works and returns response, but chatbot doesn't use data in conversation

Solutions:

  • Check response format
    # Make sure you're returning JSON
    Content-Type: application/json
    
    {"success": true, "result": "Data to pass to the chatbot"}
  • Make sure success = true for successful operations
  • Use response mapping if you return a complex structure
  • Add descriptive fields that AI can use in the response
    # Instead of just IDs:
    {"order_id": 12345}
    
    # Add context:
    {
      "success": true,
      "order_id": "ORD-12345",
      "confirmation_message": "Your order has been accepted. Number: ORD-12345",
      "delivery_date": "2024-01-20"
    }

🆘 Further Steps if the Problem Persists

Need help?
  1. Check Execution History - full request/response logs can reveal the problem
  2. Test locally - using ngrok/webhook.site to see exactly what is being sent
  3. Compare with working examples - see how they are configured in the documentation
  4. Contact support - include the execution ID and problem details

📚 Useful Debugging Tools

  • webhook.site - temporary endpoint for viewing webhooks
  • ngrok - tunnel for testing localhost
  • Postman/Insomnia - testing API requests
  • JSON Schema Validator - validating parameters schema
  • SSL Labs - checking SSL/TLS certificate
  • cURL - testing endpoint from command line
Tip: 90% of Custom Tools problems are caused by incorrect configuration (URL, schema, description). Before debugging code - carefully check the configuration in the panel and execution history!