Appearance
JSON Import & AI-Assisted Agent Creation
The fastest way to create an agent is to describe it to an AI (Claude, ChatGPT, Gemini) and ask it to produce the import JSON. Paste the schema from this document as context, describe your use case, and upload the result.
Contents
- How to Use AI to Create an Agent
- Full JSON Schema
- Field Rules
- Example 1 — Standard Agent (Freeform)
- Example 2 — Flow Agent (Loan Collection)
- AI Prompt Template
- Uploading via API
- Common Mistakes
How to Use AI to Create an Agent
- Copy everything in the Full JSON Schema section
- Paste it into your AI chat (Claude, ChatGPT, etc.)
- After pasting, write: "Using the schema above, create an agent JSON for: [describe your use case]"
- The AI produces the JSON
- Go to Dashboard → Agents → ↑ Import → upload the file
Or upload via API:
bash
curl -X POST https://your-domain.com/api/agents/import \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d @my_agent.jsonFull JSON Schema
json
{
"version": "1",
"overwrite": false,
"agent": {
"name": "string — unique, no spaces, use hyphens (e.g. 'loan-collection-bot')",
"prompt": "string — the LLM system prompt. Who the agent is, how to behave, what it knows.",
"greeting": "string — first thing the agent says. Played instantly via TTS. Can use {{variable}} placeholders.",
"stt_provider": "deepgram | sarvam | openai | elevenlabs",
"stt_model": "nova-3-general (deepgram) | saaras:v3 (sarvam) | whisper-1 (openai) | scribe_v1 (elevenlabs)",
"stt_language": "en | hi-IN | ta-IN | te-IN | kn-IN | ml-IN | bn-IN | mr-IN | gu-IN | pa-IN",
"stt_mode": "transcribe | translate — sarvam only, omit for others",
"stt_settings": {},
"llm_provider": "openai | google | grok",
"llm_model": "gpt-4.1-nano (openai) | gemini-2.5-flash (google) | grok-3-beta (grok)",
"llm_settings": {},
"tts_provider": "cartesia | elevenlabs | sarvam | deepgram | openai",
"tts_model": "sonic-3 (cartesia) | eleven_flash_v2_5 (elevenlabs) | bulbul:v2 (sarvam) | aura-2-thalia-en (deepgram) | tts-1 (openai)",
"tts_language": "en | hi-IN | ta-IN ...",
"voice": "string — voice ID or speaker name for the TTS provider",
"tts_settings": {},
"pipeline_settings": {
"idle_warn_secs": 25,
"idle_end_secs": 50,
"vad_confidence": 0.5,
"vad_start_secs": 0.2,
"vad_stop_secs": 0.2,
"vad_min_volume": 0.4,
"min_words": 3
},
"tool_ids": ["tool-id-1", "tool-id-2"],
"pre_call_tool_ids": ["tool-id-3"],
"context_variables": {
"variable_name": {
"type": "string | number | boolean",
"description": "What this variable represents"
}
},
"webhook_url": "https://your-server.com/webhook",
"webhook_events": ["call_started", "call_ended", "no_answer", "failed"]
},
"tools": [
{
"id": "tool-id-1",
"name": "string — snake_case, unique",
"description": "string — what this tool does. The LLM reads this to decide when to call it.",
"webhook_url": "https://your-server.com/tool-endpoint",
"webhook_method": "POST | GET",
"headers": { "Authorization": "Bearer secret" },
"timeout_secs": 10,
"parameters": {
"properties": {
"param_name": {
"type": "string | number | boolean | object | array",
"description": "What this parameter is"
}
},
"required": ["param_name"]
}
}
],
"flow_nodes": [
{
"node_key": "string — unique within this agent (e.g. 'greeting', 'collect-name')",
"position": 0,
"is_initial": true,
"is_terminal": false,
"role_messages": [
{ "role": "system", "content": "You are [who the agent is]. [Personality and tone.]" }
],
"task_messages": [
{ "role": "system", "content": "Your current task: [what to do at this stage]." }
],
"functions": [
{
"name": "function_name",
"description": "When the LLM should call this",
"next_node_key": "the node_key of the next node",
"properties": {
"collected_value": {
"type": "string",
"description": "A value to collect before calling this function"
}
},
"required": []
}
],
"tool_ids": ["tool-id-1"],
"builtin_tools": ["end_call"],
"pre_actions": [
{ "type": "tool_call", "tool_id": "tool-id-1" }
],
"position_xy": { "x": 100, "y": 200 }
}
]
}Field Rules
Agent
name— required. No spaces (use hyphens). Used in the telephony webhook URL.prompt— required. Global system prompt. In flow mode this is prepended to every node's prompt.greeting— keep it short (1–2 sentences). Usefor outbound call context.- STT/LLM/TTS fields — provider and model must be compatible. Default stack:
deepgram+gpt-4.1-nano+cartesia. tool_ids— webhook tool UUIDs the LLM can call throughout the call (freeform agents only). Mention each tool in the prompt.pre_call_tool_ids— fire automatically before the LLM's first response. Results injected as call context.context_variables— define for outbound agents. Keys must matchcustom_parametersin the outbound call request.
Tools
id— placeholder ID (e.g."tool-lookup-caller"). Used to reference the tool from nodes and agent. Gets remapped to a real UUID on import.name— the function name the LLM sees. Make it descriptive.description— critical. The LLM decides when to call the tool based entirely on this.parameters.properties— the arguments the LLM must provide.parameters.required— list of mandatory property names.
Flow Nodes
- Exactly one node must have
is_initial: true. - At least one node must have
is_terminal: true. role_messages— who the agent is. Usually the same across all nodes.task_messages— what the agent must do at this specific node. Changes every node.functions— how the agent advances. Each has anext_node_key. If empty, the LLM stays on the current node.tool_ids— tools the LLM can call at this node. Reference theidfrom the tools array.builtin_tools— add["end_call"]to every node so the LLM can end the call at any stage.pre_actions— fire automatically on node entry, before the LLM speaks.- Terminal nodes must have
functions: []— the platform providesend_callautomatically. - Escape hatches: Add transition functions for "busy/call later" and "not interested" to every non-terminal node.
Example 1 — Standard Agent (Freeform)
Customer support for an e-commerce store. No flow — the agent handles any question.
json
{
"version": "1",
"agent": {
"name": "support-agent",
"prompt": "You are Maya, a friendly customer support agent for ShopEasy, an online electronics store.\n\nYou help customers with:\n1. Order status — ask for their order ID, then call check_order_status.\n2. Scheduling callbacks — collect name, phone, preferred time, then call schedule_callback.\n\nBe concise and warm. Relay tool results naturally.",
"greeting": "Hi, this is Maya from ShopEasy support. How can I help you today?",
"stt_provider": "deepgram",
"stt_model": "nova-3-general",
"stt_language": "en",
"llm_provider": "openai",
"llm_model": "gpt-4.1-nano",
"tts_provider": "cartesia",
"tts_model": "sonic-3",
"voice": "694f9389-aac1-45b6-b726-9d9369183238",
"tool_ids": ["tool-order-status", "tool-schedule-callback"]
},
"tools": [
{
"id": "tool-order-status",
"name": "check_order_status",
"description": "Look up the current status of a customer's order. Call this when the customer asks about their order.",
"webhook_url": "https://your-api.com/orders/status",
"webhook_method": "POST",
"headers": {},
"timeout_secs": 8,
"parameters": {
"properties": {
"order_id": { "type": "string", "description": "The order ID provided by the customer" }
},
"required": ["order_id"]
}
},
{
"id": "tool-schedule-callback",
"name": "schedule_callback",
"description": "Schedule a callback for a customer. Call this when the customer asks to be called back.",
"webhook_url": "https://your-api.com/callbacks/schedule",
"webhook_method": "POST",
"headers": {},
"timeout_secs": 10,
"parameters": {
"properties": {
"customer_name": { "type": "string", "description": "Customer's full name" },
"phone_number": { "type": "string", "description": "Customer's phone number" },
"preferred_time": { "type": "string", "description": "When they want to be called back" }
},
"required": ["customer_name", "phone_number", "preferred_time"]
}
}
],
"flow_nodes": []
}Example 2 — Flow Agent (Loan Collection)
Outbound agent for collecting overdue loan payments. Full 5-node flow.
json
{
"version": "1",
"agent": {
"name": "loan-collection-bot",
"prompt": "You are Arjun, a polite collections executive at FinCare. Your goal is to remind customers about their overdue loan payment and arrange a repayment. Always be respectful and never threaten.",
"greeting": "Hello, may I speak with {{customer_name}}?",
"stt_provider": "deepgram",
"stt_model": "nova-3-general",
"stt_language": "en",
"llm_provider": "openai",
"llm_model": "gpt-4.1-nano",
"tts_provider": "cartesia",
"tts_model": "sonic-3",
"voice": "694f9389-aac1-45b6-b726-9d9369183238",
"context_variables": {
"customer_name": { "type": "string", "description": "Customer full name" },
"loan_id": { "type": "string", "description": "Loan account number" },
"amount_due": { "type": "number", "description": "Overdue amount in INR" },
"due_date": { "type": "string", "description": "Original due date (YYYY-MM-DD)" }
}
},
"tools": [
{
"id": "tool-verify",
"name": "verify_customer",
"description": "Verify the customer's identity by loan ID and date of birth. Call this after the customer provides their date of birth.",
"webhook_url": "https://your-api.com/collections/verify",
"webhook_method": "POST",
"headers": { "Authorization": "Bearer your-secret" },
"timeout_secs": 10,
"parameters": {
"properties": {
"loan_id": { "type": "string" },
"date_of_birth": { "type": "string", "description": "YYYY-MM-DD" }
},
"required": ["loan_id", "date_of_birth"]
}
},
{
"id": "tool-record-promise",
"name": "record_payment_promise",
"description": "Record the customer's commitment to pay by a specific date. Call this after the customer agrees to a payment date.",
"webhook_url": "https://your-api.com/collections/promise",
"webhook_method": "POST",
"headers": { "Authorization": "Bearer your-secret" },
"timeout_secs": 10,
"parameters": {
"properties": {
"loan_id": { "type": "string" },
"payment_date": { "type": "string", "description": "Promised payment date (YYYY-MM-DD)" },
"amount": { "type": "number" }
},
"required": ["loan_id", "payment_date", "amount"]
}
}
],
"flow_nodes": [
{
"node_key": "greeting",
"position": 0,
"is_initial": true,
"is_terminal": false,
"role_messages": [
{ "role": "system", "content": "You are Arjun, a polite collections executive at FinCare." }
],
"task_messages": [
{ "role": "system", "content": "Confirm you are speaking with {{customer_name}}. If they confirm, call confirmed_correct_person. If wrong person or unavailable, call wrong_person_or_unavailable." }
],
"functions": [
{ "name": "confirmed_correct_person", "description": "Person confirms they are {{customer_name}}", "next_node_key": "verify_identity", "properties": {}, "required": [] },
{ "name": "wrong_person_or_unavailable", "description": "Wrong person or they are not available", "next_node_key": "close_call", "properties": {}, "required": [] }
],
"tool_ids": [],
"builtin_tools": ["end_call"],
"pre_actions": [],
"position_xy": { "x": 100, "y": 100 }
},
{
"node_key": "verify_identity",
"position": 1,
"is_initial": false,
"is_terminal": false,
"role_messages": [],
"task_messages": [
{ "role": "system", "content": "Ask for the customer's date of birth to verify their identity. Call verify_customer with their loan_id ({{loan_id}}) and the date of birth they provide. If verified, call identity_verified. If verification fails after two attempts, call identity_failed." }
],
"functions": [
{ "name": "identity_verified", "description": "verify_customer tool returned successful verification", "next_node_key": "present_dues", "properties": {}, "required": [] },
{ "name": "identity_failed", "description": "Verification failed after two attempts", "next_node_key": "close_call", "properties": {}, "required": [] }
],
"tool_ids": ["tool-verify"],
"builtin_tools": ["end_call"],
"pre_actions": [],
"position_xy": { "x": 300, "y": 100 }
},
{
"node_key": "present_dues",
"position": 2,
"is_initial": false,
"is_terminal": false,
"role_messages": [],
"task_messages": [
{ "role": "system", "content": "Inform the customer that their loan {{loan_id}} has an overdue balance of {{amount_due}} rupees, due on {{due_date}}. Offer options: (1) pay full amount now, (2) partial today, rest by month-end, (3) request a senior advisor callback.\n\nIf they agree to pay: call customer_agrees_to_pay.\nIf they want a callback: call customer_wants_callback.\nIf they dispute or refuse: call customer_refuses." }
],
"functions": [
{
"name": "customer_agrees_to_pay",
"description": "Customer commits to paying on a specific date",
"next_node_key": "confirm_payment",
"properties": {
"payment_date": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["payment_date", "amount"]
},
{ "name": "customer_wants_callback", "description": "Customer wants a senior advisor callback", "next_node_key": "close_call", "properties": {}, "required": [] },
{ "name": "customer_refuses", "description": "Customer disputes or refuses to pay", "next_node_key": "close_call", "properties": {}, "required": [] }
],
"tool_ids": [],
"builtin_tools": ["end_call"],
"pre_actions": [],
"position_xy": { "x": 500, "y": 100 }
},
{
"node_key": "confirm_payment",
"position": 3,
"is_initial": false,
"is_terminal": false,
"role_messages": [],
"task_messages": [
{ "role": "system", "content": "Record the payment arrangement using record_payment_promise with the loan_id, agreed payment_date, and amount. Then confirm to the customer that their arrangement has been logged and they will receive a reminder. Call arrangement_confirmed." }
],
"functions": [
{ "name": "arrangement_confirmed", "description": "Promise recorded and confirmed to customer", "next_node_key": "close_call", "properties": {}, "required": [] }
],
"tool_ids": ["tool-record-promise"],
"builtin_tools": ["end_call"],
"pre_actions": [],
"position_xy": { "x": 700, "y": 100 }
},
{
"node_key": "close_call",
"position": 4,
"is_initial": false,
"is_terminal": true,
"role_messages": [],
"task_messages": [
{ "role": "system", "content": "Thank the customer for their time. Wish them a good day. Call end_call." }
],
"functions": [],
"tool_ids": [],
"builtin_tools": ["end_call"],
"pre_actions": [],
"position_xy": { "x": 900, "y": 100 }
}
]
}AI Prompt Template
Paste this into Claude or ChatGPT after sharing this document:
I want to create a voice agent. Using the JSON schema from the document above, generate a complete valid import JSON for the following use case:
USE CASE:
[describe what the agent does]
LANGUAGE: [English / Hindi / Tamil / etc.]
FLOW (describe each stage, or write "no flow" for a freeform agent):
1. [first stage — what happens]
2. [second stage — what happens]
3. [third stage — what happens]
...
TOOLS NEEDED:
- [tool name]: [what it does, what data it needs, your API endpoint URL]
INBOUND OR OUTBOUND: [inbound / outbound]
If outbound, CONTEXT VARIABLES:
- [variable name]: [description]
OBJECTION HANDLING (include all that apply):
- call-me-later: yes/no — webhook URL: [your callback endpoint]
- not-interested: yes/no
- is-this-a-bot: disclose / soft-deflect / skip
- transfer-to-human: yes/no — webhook URL: [your notification endpoint]
- do-not-call: yes/no — webhook URL: [your DNC endpoint]
- bad-audio: yes/no
Rules to follow:
- Use gpt-4.1-nano as the LLM model
- Use deepgram + nova-3-general for STT (unless Hindi/Indian language, then use sarvam)
- Use cartesia + sonic-3 for TTS (unless Hindi/Indian language, then use sarvam + bulbul:v2)
- Voice ID for Cartesia: 694f9389-aac1-45b6-b726-9d9369183238
- Tool IDs should be short strings like "tool-verify", "tool-lookup" etc.
- For freeform agents: add tool IDs to agent.tool_ids and mention each tool in the agent prompt
- For flow agents: add tool IDs to each node's tool_ids (not the agent level)
- Every flow must have exactly one is_initial node and at least one is_terminal node
- Terminal node task must instruct the LLM to call end_call
- Add "builtin_tools": ["end_call"] to EVERY node
- Add escape hatch functions to every non-terminal node: "caller_wants_callback" and "caller_not_interested"
- For objection tools (schedule_callback, mark_do_not_call, request_human_transfer): add them to tool_ids on ALL flow nodes
- Return only the JSON — no explanationUploading via API
Upload new agent:
bash
curl -X POST https://your-domain.com/api/agents/import \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d @my_agent.jsonOverwrite existing agent:
bash
curl -X POST https://your-domain.com/api/agents/import \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ ...json..., "overwrite": true }'Tool matching on import:
- Tools are matched by
name. If a tool with the same name already exists in the system, it is reused. - New tools are created automatically.
- Tool ID references in nodes and agent fields are remapped to the local environment's UUIDs.
Common Mistakes
| Mistake | Fix |
|---|---|
Two nodes have is_initial: true | Only one node can be the starting point |
next_node_key points to a key that doesn't exist | Check spelling exactly — node keys are case-sensitive |
Terminal node has functions pointing somewhere | Terminal node's functions must be [] — platform provides end_call |
Tool id in tool_ids doesn't match any tool in the tools array | The id must exactly match a tool's id field |
pre_call_tool_ids references a tool not in the tools array | Add the tool to the tools array, or remove the reference |
| Freeform agent has tools but prompt doesn't mention them | The LLM won't know when to call the tool — add explicit instructions |
task_messages is empty on a node | Every node needs a clear instruction or the LLM won't know what to do |
Greeting uses but context_variables is not defined | Define the variable in context_variables on the agent |
builtin_tools: ["end_call"] missing from non-terminal nodes | Without it, the LLM can't end the call if the user wants to stop mid-flow |
| No escape hatch functions on non-terminal nodes | Add "caller_wants_callback" and "caller_not_interested" to every non-terminal node |