SIP Trunk Configuration (BYOT)
Bring Your Own Telephony — Use your personal SIP trunk
What is BYOT
Neuralai is a SaaS provider: it supplies the AI and platform, but does NOT provide phone lines. The sender number (Caller ID) used for calls comes from the SIP trunk you configure. Without an active SIP trunk, no calls can be made (error 'no_sip_trunk').
Supported SIP Providers
twilioElastic SIP Trunking. Global coverage, ideal for international campaigns.
vonageSIP Trunking. Reliable, good European coverage.
telnyxSIP Trunking. High quality, low latency, great for high volume.
generic_sipAny standard SIP provider. Requires URI, port, protocol and credentials.
Configuration Fields
trunk_nameDescriptive name (e.g. 'Twilio Production')provider_typetwilio | vonage | telnyx | generic_sipsip_uriSIP server address (e.g. sip.provider.com)sip_portSIP port (default 5060, 5061 for TLS)transport_protocolUDP | TCP | TLS (TLS recommended)usernameSIP authentication usernameencrypted_passwordSIP password (AES-256-GCM encrypted)from_numberE.164 Caller ID number (e.g. +393331234567)Caller ID and Destination Country Requirement
The sender number (Caller ID) MUST match the country of the recipients. A non-matching number is blocked by providers under national telecom regulators' anti-spoofing rules (e.g. AGCOM for Italy, FCC for the USA, Ofcom for the UK, ARCEP for France, BNetzA for Germany).
🇮🇹 +39
Italian number
🇩🇪 +49
German number
🇫🇷 +33
French number
Security and Encryption
- ✓SIP passwords encrypted with AES-256-GCM in the database
- ✓No plaintext access to sensitive data
- ✓Only one active SIP trunk per user at a time
- ✓TLS protocol recommended for greater security
Public Documentation Link
Share this link with AI assistants (Vibe Coding, Cursor, Copilot) to give them full context of the webhook API.
💡 Tip: paste this link in your AI coding assistant's prompt to instruct it how to integrate Neuralai's AI webhook calls.
How to use the webhook
Send a POST request to the webhook URL with the phone number in E.164 format. Config parameters (voice, language, KB, etc.) are used as defaults, but you can override them in the payload.
Mandatory KYC verification
To use AI Webhook Calls, you must complete KYC verification (identity, company, and biometric selfie). Verification is shared across all outbound services.
Payload fields
to_numberREQUIREDE.164 phone numberrecipient_nameoptionalRecipient name (if absent, AI asks for name or greets without one)topicoptionalTopic (override)caller_on_behalf_ofoptionalOn behalf of (override)agent_nameoptionalAI agent name (override). Empty = no personal namecustom_kb_idoptionalKB ID (override)knowledge_modesoptionalModes array (override)agent_instructionsoptionalAgent behavioral rules (override)voice_typeoptional"female" | "male"languageoptionalLanguage (override)timezoneoptionalIANA timezone (override). E.g. Europe/Rome, America/New_York, Asia/Tokyo. Controls the agent's Current Time Awareness for time-appropriate greetings.topic_labeloptional⚠️ Config-only (not payload). Widget dropdown labelAI agent name (agent_name)
The agent_name field lets you assign a personal name to the AI agent (e.g. "Lucy", "Marco"). If provided, the agent introduces itself with that name during the call. If empty or omitted, the agent does NOT use a personal name: it introduces itself only as "an assistant from Neuralai" calling on behalf of the configured sender. This ensures the agent never invents a name on its own.
With agent name
"agent_name": "Lucy"
Agent says: "Hello, I'm Lucy, calling on behalf of..."
Without name (empty)
"agent_name": "" // oppure: omesso
Agent says: "Hello, I'm calling on behalf of..." (no invented name)
Agent behavioral instructions (agent_instructions)
The agent_instructions field lets you send mandatory behavioral rules the AI agent must follow during the call. If omitted in the payload, the value configured in the webhook is used. Instructions are injected into the agent prompt as binding rules.
Usage examples
- Never ask for sensitive data (credit cards, tax codes, passwords) by phone
- If you cannot resolve an issue, invite the user to open a ticket
- Maintain a friendly and professional tone at all times
KB API (Knowledge Base API Hub)
The kb_api mode lets the AI agent query your API Hub Knowledge Bases during the call via a voice provider custom function. Token charge like buildKnowledgeBase (15-30 tokens per query).
- Config-only: select API Hub KBs from the webhook config (not an HTTP payload field).
- Payload: include "kb_api" in the knowledge_modes array to activate the mode in the call.
- Exclusions: kb_api is incompatible with topic_only, basic_knowledge, web_search_unlimited, and custom_kb.
{
"to_number": "+393331234567",
"topic": "Supporto tecnico",
"knowledge_modes": ["topic_only", "kb_api"]
}Transfer to human operator
When enabled in the webhook config, the AI agent can transfer the call to a human operator if the recipient explicitly requests it. These are config fields (not HTTP payload): transfer_to_human_enabled, transfer_to_human_number, transfer_sip_method.
Config example (manageWebhookCallConfig API):
{
"transfer_to_human_enabled": true,
"transfer_to_human_number": "+393331234567",
"transfer_sip_method": "sip_invite"
}{
"to_number": "+393331234567"
}Only the phone number is required. All other parameters use the webhook configuration defaults.
{
"to_number": "+393331234567",
"recipient_name": "Mario Rossi",
"topic": "Conferma appuntamento",
"caller_on_behalf_of": "Studio Medico XYZ",
"agent_name": "Lucy",
"agent_instructions": "Non chiedere dati sensibili per telefono. Se non riesci a risolvere, invita ad aprire un ticket.",
"custom_kb_id": "",
"knowledge_modes": ["topic_only"],
"voice_type": "female",
"language": "Italian (Italiano)",
"timezone": "Europe/Rome"
}All optional fields override the webhook configuration defaults for this single call.
Response fields (success)
successbooleanmessagestringcall_idstringoutbound_call_idstring (UUID)recipient_numberstringtimestampstring (ISO 8601){
"success": true,
"message": "Call triggered successfully",
"call_id": "call_abc123def456...",
"outbound_call_id": "a1b2c3d4-e5f6-...",
"recipient_number": "+393331234567",
"timestamp": "2026-07-26T14:30:00.000Z"
}Error payloads
Errors always return success: false with an error field identifying the issue and a human-readable message. The HTTP status code varies by error type.
missing_to_numberThe to_number field is missing or empty in the payload.
{
"success": false,
"error": "missing_to_number",
"message": "Phone number is required"
}invalid_phone_formatThe number is not in E.164 format (must start with + and country code).
{
"success": false,
"error": "invalid_phone_format",
"message": "Phone number must be in E.164 format (e.g. +393331234567)"
}kyc_not_verifiedThe user has not completed KYC verification. Required for all outbound services.
{
"success": false,
"error": "kyc_not_verified",
"message": "KYC verification required to use webhook calls"
}no_sip_trunkNo active SIP trunk configured. Calls cannot be placed without a sender number.
{
"success": false,
"error": "no_sip_trunk",
"message": "No active SIP trunk configured"
}webhook_inactiveThe webhook configuration has been deactivated. Reactivate it from the dashboard.
{
"success": false,
"error": "webhook_inactive",
"message": "This webhook configuration is not active"
}rate_limit_exceededThe per-minute call limit for this webhook has been exceeded. Wait before retrying.
{
"success": false,
"error": "rate_limit_exceeded",
"message": "Too many requests. Please wait before retrying.",
"retry_after_seconds": 30
}Topic label (topic_label)
The topic_label field is a human-readable label (e.g. "Tech Support", "Sales") shown in the widget dropdown when topic selection is enabled. It lets your website visitors choose a department or topic, and automatically routes the call to the corresponding specialized webhook — each with its own AI agent, Knowledge Base and behavioral instructions.
How routing works
Security badges
Green shield
Authorized domains configured: only approved domains can trigger calls.
Red shield
No domains configured: anyone can trigger calls at your expense. Configure authorized domains!
Code: config structure and embed
The widget automatically loads the configuration (including topic labels) from the server on page load. The embed script is a single line:
<script src="https://app.neuralaiweb.com/functions/serveWebhookWidget"
data-token="YOUR_WEBHOOK_TOKEN"></script>The server responds with a JSON object that includes the widget configuration and the topics array (with labels and security status):
{
"topic_selection": {
"enabled": true,
"placeholder": "Select department...",
"webhook_tokens": ["token_a", "token_b"]
},
"topics": [
{
"token": "MAIN_TOKEN",
"label": "Reception",
"secured": true
},
{
"token": "token_a",
"label": "Tech Support",
"secured": true
},
{
"token": "token_b",
"label": "Sales",
"secured": false
}
]
}The topic_label field is set in the webhook configuration ("Webhook" tab → edit → "Topic label"). It is not an HTTP payload field: it's configuration metadata used by the widget for the dropdown. Routing happens by replacing the token in the webhook URL.
curl -X POST 'https://neuralai.pro/functions/triggerWebhookCall?token=YOUR_WEBHOOK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"to_number": "+393331234567",
"recipient_name": "Mario Rossi",
"topic": "Conferma appuntamento",
"caller_on_behalf_of": "Studio Medico XYZ",
"agent_name": "Lucy",
"agent_instructions": "Non chiedere dati sensibili per telefono. Se non riesci a risolvere, invita ad aprire un ticket."
}'curl -X POST "https://neuralai.pro/functions/triggerWebhookCall?token=YOUR_WEBHOOK_TOKEN" -H "Content-Type: application/json" -d "{\"to_number\": \"+393331234567\", \"recipient_name\": \"Mario Rossi\", \"topic\": \"Conferma appuntamento\", \"caller_on_behalf_of\": \"Studio Medico XYZ\", \"agent_name\": \"Lucy\", \"agent_instructions\": \"Non chiedere dati sensibili per telefono.\"}"import requests
WEBHOOK_URL = "https://neuralai.pro/functions/triggerWebhookCall?token=YOUR_WEBHOOK_TOKEN"
response = requests.post(WEBHOOK_URL, json={
"to_number": "+393331234567",
"recipient_name": "Mario Rossi",
"topic": "Conferma appuntamento",
"caller_on_behalf_of": "Studio Medico XYZ",
"agent_name": "Lucy",
"agent_instructions": "Non chiedere dati sensibili per telefono. Se non riesci a risolvere, invita ad aprire un ticket."
})
result = response.json()
print(f"Call ID: {result.get('call_id')}")
print(f"Success: {result.get('success')}")const WEBHOOK_URL = "https://neuralai.pro/functions/triggerWebhookCall?token=YOUR_WEBHOOK_TOKEN";
const response = await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to_number: "+393331234567",
recipient_name: "Mario Rossi",
topic: "Conferma appuntamento",
caller_on_behalf_of: "Studio Medico XYZ",
agent_name: "Lucy",
agent_instructions: "Non chiedere dati sensibili per telefono. Se non riesci a risolvere, invita ad aprire un ticket."
})
});
const result = await response.json();
console.log("Call ID:", result.call_id);
console.log("Success:", result.success);$webhookUrl = "https://neuralai.pro/functions/triggerWebhookCall?token=YOUR_WEBHOOK_TOKEN"
$body = @{
to_number = "+393331234567"
recipient_name = "Mario Rossi"
topic = "Conferma appuntamento"
caller_on_behalf_of = "Studio Medico XYZ"
agent_name = "Lucy"
agent_instructions = "Non chiedere dati sensibili per telefono."
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri $webhookUrl -Method Post \
-ContentType "application/json" -Body $body
Write-Host "Call ID: $($response.call_id)"
Write-Host "Success: $($response.success)"Example response
{
"success": true,
"message": "Call triggered successfully",
"call_id": "call_abc123...",
"outbound_call_id": "uuid-...",
"recipient_number": "+393331234567",
"timestamp": "2026-07-08T10:30:00.000Z"
}