curl -X POST https://api.telzino.com/v1/agents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent",
"organizationId": "987fcdeb-51a2-3bc4-d567-890123456789",
"ragEnabled": true,
"knowledgeBaseId": "11111111-1111-1111-1111-111111111111",
"ragConfig": { "top_k": 5, "rewrite_query": true }
}'
{
"data": {
"id": "abc12345-e89b-12d3-a456-426614174000",
"organization_id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Customer Support Agent",
"description": "Handles customer inquiries",
"greeting_message": "Hello! How can I help you today?",
"system_prompt": "You are a helpful customer support agent...",
"agent_type": "simple",
"model": "gpt-4.1",
"voice_model": "gpt-4o-realtime-preview",
"stt_model": "deepgram",
"tts_model": "cartesia",
"voice_settings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.0},
"config": {},
"extensions": {},
"integrations": {},
"transfer_enabled": true,
"transfer_rules": "Transfer to +15551234567 if the caller asks for billing support",
"language": "en-US",
"timezone": "America/New_York",
"enable_recording": true,
"echo_detection": false,
"call_detection": false,
"knowledgebase_links": ["https://example.com/faq", "https://example.com/pricing"],
"email": ["[email protected]"],
"email_tool_enabled": false,
"email_server_id": null,
"pronunciations": [],
"status": "active",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
{
"error": "Invalid request data",
"details": [
{"path": ["name"], "message": "Name is required"}
]
}
{
"error": "forbidden",
"error_description": "No access to this organization"
}
{
"error": "forbidden",
"error_description": "Model not available for this organization: claude-sonnet-4-6"
}
{
"error": "forbidden",
"error_description": "TTS provider not available for this organization: minimax"
}
{
"error": "forbidden",
"error_description": "Feature(s) not available for this organization: listings_search"
}
{
"error": "forbidden",
"error_description": "Screenshare requires a model that can read images. Choose a different model, or turn screenshare off."
}
{
"error": "forbidden",
"error_description": "Screenshare is not available for healthcare organizations."
}
Agents
Create Agent
Creates a new agent associated with the specified organization
POST
/
v1
/
agents
curl -X POST https://api.telzino.com/v1/agents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent",
"organizationId": "987fcdeb-51a2-3bc4-d567-890123456789",
"ragEnabled": true,
"knowledgeBaseId": "11111111-1111-1111-1111-111111111111",
"ragConfig": { "top_k": 5, "rewrite_query": true }
}'
{
"data": {
"id": "abc12345-e89b-12d3-a456-426614174000",
"organization_id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Customer Support Agent",
"description": "Handles customer inquiries",
"greeting_message": "Hello! How can I help you today?",
"system_prompt": "You are a helpful customer support agent...",
"agent_type": "simple",
"model": "gpt-4.1",
"voice_model": "gpt-4o-realtime-preview",
"stt_model": "deepgram",
"tts_model": "cartesia",
"voice_settings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.0},
"config": {},
"extensions": {},
"integrations": {},
"transfer_enabled": true,
"transfer_rules": "Transfer to +15551234567 if the caller asks for billing support",
"language": "en-US",
"timezone": "America/New_York",
"enable_recording": true,
"echo_detection": false,
"call_detection": false,
"knowledgebase_links": ["https://example.com/faq", "https://example.com/pricing"],
"email": ["[email protected]"],
"email_tool_enabled": false,
"email_server_id": null,
"pronunciations": [],
"status": "active",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
{
"error": "Invalid request data",
"details": [
{"path": ["name"], "message": "Name is required"}
]
}
{
"error": "forbidden",
"error_description": "No access to this organization"
}
{
"error": "forbidden",
"error_description": "Model not available for this organization: claude-sonnet-4-6"
}
{
"error": "forbidden",
"error_description": "TTS provider not available for this organization: minimax"
}
{
"error": "forbidden",
"error_description": "Feature(s) not available for this organization: listings_search"
}
{
"error": "forbidden",
"error_description": "Screenshare requires a model that can read images. Choose a different model, or turn screenshare off."
}
{
"error": "forbidden",
"error_description": "Screenshare is not available for healthcare organizations."
}
Request Body
Required Fields
string
required
Display name of the agent (1-255 characters)
string
required
Organization ID to create the agent in (UUID format)
Agent Configuration
string
Description of what the agent does
string
Initial message the agent says when call starts. This is spoken immediately when a call connects.Example:
"Hello! Thank you for calling Acme Corp. How can I help you today?"string
Greeting spoken when this agent places an outbound call. Overridden by a per-call
greeting; if empty, the agent auto-generates an opener from the call context (falling back to greetingMessage).Example: "Hi, this is Ava calling from Acme Corp."string
System prompt defining agent behavior and personality. This guides the AI’s responses throughout the conversation.Example:
"You are a helpful customer support agent for Acme Corp. Be friendly, professional, and concise. If you don't know an answer, offer to transfer the call to a human agent."string
default:"simple"
Type of agent configuration.Supported values:
simple- Basic agent with standard configurationadvanced- Agent with additional features enabledcustom- Fully customizable agent configurationappointment_scheduler- Agent optimized for scheduling appointmentsreceptionist- Receptionist agent for call routing and transfersmessage_taker- Agent that takes messages and routes to directory extensionsblank_slate- Inbound agent started from a basic prompt, with no template behaviouroutbound- Agent that places calls rather than answering themopentable_reservation- Restaurant agent that checks OpenTable availability and texts a booking link (see OpenTable Reservations)translator- AI interpreter for a call between two people who do not share a language (English with Spanish, or English with French). RequiresinterpreterConfig(see Translator Agent)
object
Restaurant settings for an
opentable_reservation agent. Ignored by every other agent type.Every field is optional — a half-configured agent still answers calls and degrades honestly rather than failing. See OpenTable Reservations for what each setting changes on a live call.Restaurant identityrestaurant_url(string) — the restaurant’s OpenTable page, e.g.https://www.opentable.com/r/your-restaurantrestaurant_id(string) — the numeric OpenTable ID, as text. Required before the agent can check availabilityrestaurant_name(string) — used on the phone and in the text messagerestaurant_country,restaurant_macro,restaurant_neighborhood(string) — informational, returned when the URL is resolvedtimezone(string) — IANA identifier, e.g.America/New_York. Every spoken date and time is resolved in it
service_windows(array) — up to 21 entries of{ day, opens, closes }, wheredayismon–sunand the times are 24-hourHH:MM. A day with no window is closed. A window whoseclosesis at or before itsopensruns past midnightlast_seating_offset_minutes(integer) — how long before closing the last table goes out. Defaults to30
min_party_size(integer) — defaults to1max_party_size(integer) — defaults to8. Larger parties are routed to the events teammax_advance_days(integer) — defaults to60min_advance_hours(integer) — minimum notice.0(the default) allows same-day booking at any timeblackout_dates(array ofYYYY-MM-DD) — one-off closuresconstraint_violation_message(string) — what the caller hears when a request breaks one of these
default_vague_time(HH:MM) — what “dinner time” resolves to. Defaults to19:00count_kids_in_party_size(boolean) — defaults totrueafter_hours_greeting(string) — replaces the built-in closed-right-now greetingescalation_keywords(array of string) — triggers an immediate handoff offer. Defaults to manager, complaint, sick, allergic reaction, urgent. Matching is on whole words, and the last word may be plural or inflected, socomplaintalso fires on “complaints”private_dining_keywords(array of string) — routes a private-dining or event enquiry to the events team instead of the reservation flow. Defaults to private dining, private room, private party, private event, buyout, wedding, rehearsal dinner, corporate event, holiday party
events_contact_number(string) — the events team’s number, which the agent can text rather than dial
reservation_config. Per-reason destinations are configured with the agent’s ordinary transfer_rules, the same field every agent type uses — write a rule per situation and the agent follows it. With transfer_rules empty the agent does not offer a transfer at all; it offers to take a message instead.Messagingsms_line_type_check(boolean) — check the destination can receive SMS before sending. Defaults totruesms_template_id(string) — which pre-approved template renders the message. One ofbooking_default,booking_brief,booking_with_restaurant. Bodies are not editable; see belowsms_provider(string) —twilioornetsapiens. Which carrier sends this Organization’s messages. Assigned by the Partner, not chosen per restaurant; when unset, the agent sends over whichever carrier is connected to itsms_sender_extension(string) — the PBX extension NetSapiens sends from. Ignored by Twilioemail_fallback_enabled,callback_scheduling_enabled(boolean) — which fallbacks the agent may offer when a text cannot get through
Accepted but not yet acted on. These fields validate and store, and the API will keep accepting them, but nothing reads them on a live call yet:
handoff_timeout_seconds, voicemail_fallback_enabled, voicemail_destination, sms_sender_display_name, tcpa_consent_script_variant. Setting them changes nothing today.{
"agentType": "opentable_reservation",
"reservationConfig": {
"restaurant_url": "https://www.opentable.com/r/your-restaurant",
"restaurant_id": "1272229",
"restaurant_name": "Nerea",
"timezone": "America/New_York",
"service_windows": [
{ "day": "fri", "opens": "17:00", "closes": "23:00" },
{ "day": "sat", "opens": "11:00", "closes": "14:00" },
{ "day": "sat", "opens": "17:00", "closes": "23:00" }
],
"last_seating_offset_minutes": 30,
"min_party_size": 1,
"max_party_size": 8,
"max_advance_days": 60,
"blackout_dates": ["2026-12-25"],
"events_contact_number": "+12125550188"
}
}
max_party_size must be greater than or equal to min_party_size, or the request is rejected with a validation error.SMS message bodies cannot be authored freely. Twilio’s A2P 10DLC registration requires sample content that matches real sends, so one campaign only stays valid across every restaurant under a Partner if the copy is shared.
sms_template_id selects from the pre-approved library; a value that is not in the library is rejected at send time rather than silently replaced with different copy.object
Interpreter settings for a
A request that breaks a rule is rejected with
translator agent. Required when agentType is translator. Validated and stored whenever sent, on any agent type. It only has an effect on a translator agent. The keys inside the object are snake_case, but the stored value is not exactly what you sent: it is normalised first (scripts and names trimmed, empty names and unrecognised keys dropped). See Translator Agent for what each setting changes on a live call.party_a_language(string, required) —en-US,es-USorfr-FR. Party A is the person greeted firstparty_b_language(string, required) — the other language of the pair. The pair must been-USwithes-US, oren-USwithfr-FR, in either orderopening_scripts(object, required) — one entry per pair language, keyed by language code, read word for word when the agent joins the call. Party A’s plays firstclosing_scripts(object, required) — same shape, read once after both people say goodbyebusiness_name(string) — replaces{{business_name}}in the scriptsclient_name(string) — replaces{{client_name}}in the scriptsregister(string, required) —verbatimkeeps the speaker’s wording, including fillers and hedges;plainrenders the same meaning in plain language. The wizards default toverbatim; the API has no defaultspeaker_identification(string, optional) — how the saved transcript labels who said each line:language,voiceorboth. Omitted meanslanguage, andlanguageis stored as an omitted key. It only changes the transcript labels, never what the interpreter says.voiceandbothadd a small speech-to-text charge per minute. See Translator Agent
voice_provider, required and speaker_identification) when a call starts. The agent never rejects a call over speaker_identification: it treats an unrecognised value as language. If the stored settings break any other rule when a call starts (for example, because they were written before a rule existed), the agent ends the call without speaking; it never falls back to answering as an ordinary agent. Each rule has a stable code:| Rule | Code |
|---|---|
party_a_language and party_b_language are en-US with es-US, or en-US with fr-FR | pair |
opening_scripts and closing_scripts each have exactly the two pair languages as keys | scripts_missing |
| Every script is non-empty after trimming spaces | script_empty |
| Every script is at most 2,000 characters | script_too_long |
Scripts contain no placeholder other than {{business_name}} and {{client_name}} (spaces inside the braces are allowed), and no unmatched {{ or }} | token_unknown |
| A name used by a script is filled in: non-empty after trimming spaces | token_value_missing |
business_name and client_name are at most 120 characters | name_too_long |
register is verbatim or plain | register |
speaker_identification, when sent, is language, voice or both. null is accepted and treated as omitted (language) | speaker_identification |
ttsModel is cartesia: a translator switches the voice’s language for every translation, and only Cartesia voices support that | voice_provider |
agentType is translator and no settings are sent (or stored, on a PUT), or interpreterConfig is sent as null for a translator. On any other agent type, null is accepted and stores no settings | required |
400 in the same format as every other validation error on this endpoint: "error": "Invalid request data", and one entry in details per problem. Errors for interpreterConfig rules have a path starting with "interpreterConfig", for example ["interpreterConfig", "opening_scripts", "fr-FR"] — required (the object missing, or null on a translator) has this same ["interpreterConfig"] path. voice_provider is the one exception — it validates ttsModel, not interpreterConfig, so it is reported at ["ttsModel"] instead. Each entry’s message contains the code from the table.Template defaults. When agentType is translator, every field the request does not set gets the same default the wizards write, and language is set to Party A’s language:ttsModelcartesia, with thesonic-3voice model. A translator’s Cartesia voice model is always in thesonic-3family (sonic-3orsonic-3.6): ifvoiceSettings.modelis missing or outside that family (for example the retiredsonic-2orsonic-3.5, or any other value), the API sets it tosonic-3rather than rejecting the request, and a missingvoice_idgets the template voicemodelgpt-4.1,voiceModelgpt-4o-realtime-previewandsttModeldeepgram- end-of-speech timing:
voiceSettings.min_endpointing_delay0.1,max_endpointing_delay2.0,vad_min_silence_duration0.55 - background and thinking sounds off
- echo detection, automated-caller detection and call detection off
- transfers, warm transfer, knowledge base and the email tool off
- no tools
- recording off (
enableRecording: false) - stored with
detached: true, so Connect treats the agent as already detached from its template and shows the full editor
ragEnabled, transferEnabled, warmTransferEnabled, webTransferEnabled, emailToolEnabled, echoDetection, detectAutomatedCallers, callDetection, screenshareEnabled, and background_audio_enabled, thinking_sounds_enabled and response_filler_enabled inside config (its other keys are kept). A Mobile_Call_Detection tool in extensions.tools is dropped; other tools are kept.Send any other field to override its default. Each field is defaulted as a whole object or array, not merged key by key: sending a partial voiceSettings (for example only voice_id) skips the entire default, so include the three timing keys above if you send voiceSettings at all (a missing or non-sonic-3-family voice model is still set to sonic-3 for you, as described above). A ttsModel other than cartesia is rejected with voice_provider.{
"agentType": "translator",
"interpreterConfig": {
"party_a_language": "es-US",
"party_b_language": "en-US",
"opening_scripts": {
"es-US": "Hola y bienvenido(a). Soy la intérprete de voz con inteligencia artificial para esta llamada. {{business_name}} ofrece este servicio de interpretación para ayudarle a comunicarse con su representante de {{client_name}}. Traduciré todo lo que usted y el representante digan de la manera más precisa posible. Por favor, hable con naturalidad y haga una pausa después de cada idea completa.",
"en-US": "Hello. I am the AI interpreter for this call, provided by {{business_name}}. I will translate between English and Spanish. Please speak naturally and pause after each complete thought, and I will translate."
},
"closing_scripts": {
"es-US": "Gracias por comunicarse con {{client_name}}. Esperamos que este servicio de interpretación le haya sido útil. ¡Que tenga un excelente día!",
"en-US": "The interpretation session is now complete. Thank you, and have a wonderful day."
},
"business_name": "Acme Language Services",
"client_name": "Example Health",
"register": "verbatim"
}
}
The agent says the opening scripts to both people on every call, and they are its disclosure that the people are speaking through an AI interpreter. Keep that disclosure when you write your own scripts.
string
default:"active"
Activation status of the agent. Defaults to
active when omitted, so most callers can leave this out.Supported values:active- Agent is live and can take callsinactive- Agent is disabled and will not take callscancelled- Agent has been cancelledpending- Agent is awaiting activation
draft) is rejected with a 400. The same values can be changed later via Update Agent.AI/Voice Model Configuration
string
default:"gpt-4.1"
AI model used for the agent’s language processing. See List LLM Models for the full list with descriptions.Supported values:
gpt-4.1- OpenAI GPT-4.1 (default, recommended, can never be locked out)gpt-4o- OpenAI GPT-4ogpt-4o-mini- OpenAI GPT-4o Minigpt-4.1-mini- OpenAI GPT-4.1 Mini (faster, more cost-effective)gpt-4.1-nano- OpenAI GPT-4.1 Nano (smallest, fastest)grok-3-fast- xAI Grok-3 Fastmoonshotai/Kimi-K2.6- Moonshot AI Kimi K2.6 (served via Telnyx)claude-sonnet-4-6- Anthropic Claude Sonnetgoogle/gemma-4-31b-it- Google Gemma 4 31B, served via LiveKit Inference
model that is locked is rejected with 403 forbidden. Call
List LLM Models with organization_id to get the exact
set of models the target organization may currently use.string
default:"gpt-4o-realtime-preview"
Voice model for real-time speech processing
string
default:"deepgram"
Speech-to-text model.Supported values:
deepgram- Deepgram STTv2 with flux-general-en model (recommended)
string
default:"cartesia"
Text-to-speech model.Supported values:
cartesia- Cartesia TTS (high quality, supports custom voices)minimax- MiniMax TTS (multilingual support)inworld- Inworld TTS (setvoiceSettings.voice_idto a full Inworld voice string, e.g.Inworld.TTS2.Ashley)
403 forbidden. cartesia
can never be locked, so omitting this field is never refused on that ground.object
Voice configuration settings. Structure varies based on For Cartesia (Cartesia voices also accept two optional fields:
English Cartesia voices accept a For MiniMax (Advanced Voice Timing (optional, applies to all TTS models):
Example with advanced timing:
ttsModel.Default when omitted: For a Cartesia agent (the default
ttsModel), omitting voiceSettings creates the agent with the standard voice f786b574-daa5-4673-aa0c-cbe3e8534c02 (Katie – Friendly Fixer) on the sonic-3 model. For a MiniMax agent, omitting voiceSettings leaves it unset.ttsModel: "cartesia"):{
"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02",
"speed": 1.0
}
| Property | Type | Description |
|---|---|---|
model | string | Cartesia TTS model — sonic-3 (the default) or sonic-3.6. The retired sonic-2 and sonic-3.5 are still accepted but are saved as sonic-3. On a Cartesia voice, must be in the sonic-3 family to use custom pronunciations (Inworld supports them on any model; MiniMax supports them on English agents only), and for any custom (cloned) voice_id. For pronunciations, set model explicitly: an omitted model is rejected even though it plays as sonic-3. |
language | string | Locale passed to Cartesia TTS (e.g. es). When set, must match the language registered for the selected voice_id. |
A custom (cloned)
voice_id runs on the sonic-3 family (sonic-3 or sonic-3.6). An omitted model counts as sonic-3, and a retired sonic-2 or sonic-3.5 is saved as sonic-3.model override (e.g. sonic-3.6; sonic-3 is the default for new agents) but not a language override. Non-English voices (e.g. Spanish) require both a model and a matching language:{
"voice_id": "d46e87a1-7c6d-4b18-9359-926f4a35ffdf",
"model": "sonic-3",
"language": "es",
"speed": 1.0
}
ttsModel: "minimax"):{
"voice_id": "English_radiant_girl",
"model": "speech-2.6-turbo",
"speed": 1.0
}
| Property | Type | Default | Description |
|---|---|---|---|
min_endpointing_delay | number | 0.1 | Seconds to wait after the caller stops speaking before the agent responds. Lower values feel more responsive; higher values reduce false interruptions. |
max_endpointing_delay | number | 2.0 | Maximum seconds to wait when the caller pauses mid-sentence. Prevents the agent from cutting in during natural speech pauses. |
vad_min_silence_duration | number | (system default) | Minimum silence duration in seconds for Voice Activity Detection to consider speech ended. Omit to use the system default. |
ai_greeting_interruptible | boolean | false | Let the caller talk over the AI-personalized greeting (aiGreetingPrompt); the agent then responds to what they said. The static greeting is never interruptible. |
{
"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02",
"speed": 1.0,
"min_endpointing_delay": 0.1,
"max_endpointing_delay": 5.0,
"vad_min_silence_duration": 0.55
}
Call Features
boolean
default:"false"
Whether SIP call transfer is enabled. When enabled, the agent can transfer calls to:
- SIP extensions: Format
sip:extension@hostname - US phone numbers: Automatically formatted to
tel:+1XXXXXXXXXX
boolean
default:"false"
Whether the agent can send emails during or after calls
string
Instructions for the agent on how and when to use the email tool
boolean
default:"false"
Mobile calls detection: look up whether the caller’s number is a mobile phone before the call starts. This is not answering machine detection — see
amdConfig under Outbound Calling.boolean
default:"true"
Let a caller on the web-embedded agent be transferred. Only takes effect when
transferEnabled is true; otherwise stored as false.boolean
default:"false"
Whether to enable echo detection during calls
boolean
default:"false"
Enable call recording. Recordings are saved to cloud storage and accessible via the Call Logs API.
boolean
default:"false"
Include a link to the call recording in the post-call summary notification. This is a sub-option of call recording — it requires
enableRecording: true. Sending true without recording enabled returns a 400. Note: the link is delivered via the call-summary email, so it is only actually sent when the agent also has an email recipient configured and a recording was produced; otherwise the flag is stored but has no effect.string
Rules and conditions for call transfers. Describes when and where the agent should transfer calls.Example:
"Transfer to +15551234567 if the caller asks for billing support. Transfer to +15559876543 for technical issues."array
Array of email addresses used for notifications and the email tool.Example:
["[email protected]", "[email protected]"]string | null
UUID of the email server to use for this agent. Must belong to the same account. If omitted or
null, the account’s default email server is used.Use the List Email Servers endpoint to retrieve available IDs.boolean
default:"false"
Whether this agent can ask a web-embedded caller to share their screen and reason about what it sees. See Screenshare. Ignored for SIP/PSTN calls, which have no browser to share.Turning it on also requires the organization to have opted in to Screenshare, which is off by default and which this field does not control — an agent can be created with
screenshareEnabled: true in an organization that has not opted in, and no call will start a share until it does.Returns 403 forbidden when:- Screenshare is locked for the organization via Feature Administration,
- the agent’s
model(this request’s value, or the defaultgpt-4.1if omitted) cannot read images, or - the organization is flagged Healthcare, which can never opt in to Screenshare.
Localization
string
default:"en-US"
Agent language for speech recognition and responses.Supported values:
en-US- English (US)es-US- Spanish (US)multi- Multilingual (English, Spanish, French, German, Hindi, Russian, Portuguese, Japanese, Italian, Dutch)
string
default:"America/New_York"
Agent timezone in IANA format. Used for date/time operations and calendar scheduling.Example:
"America/New_York", "Europe/London", "Asia/Tokyo"Knowledge Base
array
Array of URLs the agent can reference for knowledge. The content at these URLs is ingested and made available to the agent during conversations.This is a legacy field and is not the RAG Knowledge Base — for vector search use
ragEnabled and knowledgeBaseId below.Example: ["https://example.com/faq", "https://example.com/pricing"]RAG Knowledge Base
Attach an organization-level Knowledge Base as part of the create request, so you do not need a follow-up Update Agent call.boolean
Enable live vector search during calls. Requires
knowledgeBaseId — an agent with no knowledge base does not retrieve regardless of this flag. Returns 403 forbidden when Knowledge Base is not enabled for the target organization.string | null
Knowledge base the agent searches. It must belong to the same organization the agent is being created in; a knowledge base from another organization is rejected.Use List Knowledge Bases to retrieve available IDs.
object
RAG retrieval configuration. Unknown keys are rejected.
| Property | Type | Description |
|---|---|---|
top_k | integer (1-50) | Number of chunks to retrieve per query. |
rewrite_query | boolean | Rewrite the caller’s phrasing into a search query before retrieving. |
max_context_tokens | integer (1-200000) | Cap on retrieved context injected into the prompt. |
fallback_message | string | What the agent says when retrieval returns nothing useful. |
curl -X POST https://api.telzino.com/v1/agents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent",
"organizationId": "987fcdeb-51a2-3bc4-d567-890123456789",
"ragEnabled": true,
"knowledgeBaseId": "11111111-1111-1111-1111-111111111111",
"ragConfig": { "top_k": 5, "rewrite_query": true }
}'
rag_enabled is false, knowledge_base_id is null, and rag_config is { "top_k": 5, "rewrite_query": false }.
Business (Google Place)
string
Google Place ID linking the agent to a Google Business Profile listing. The platform fetches the listing’s details (hours, location, phone, website, services, ratings) shortly after creation, and the agent uses them as verified facts when answering callers.Example:
"ChIJVS6XxJPFU4gRoE8c8ql2lCQ"Warm Transfer
boolean
Enable warm transfer routing for this agent.
string
What the AI says to the human agent on the consult leg of a warm transfer, separate from the rules for when to transfer. Blank uses the built-in briefing. See Warm Transfer.
string
Plain-language rules for when and where to warm transfer. Example:
"If the caller asks for billing, confirm first and warm transfer to pbx:1234."string
Public URL of the hold-music file played during warm transfer. The easiest way to set this is Upload Hold Music, which saves the URL for you. Leave empty for built-in hold music.
boolean
Whether the original caller’s number is passed to the transfer destination. Defaults to
true.Pronunciations
array
Custom pronunciation rules for specific words and names, using IPA (International Phonetic Alphabet) format. The agent’s TTS engine will use these rules to pronounce words correctly.Each entry is an object with:
Custom pronunciations are supported on Inworld voices, Cartesia voices using the
sonic-3 or sonic-3.6 model, and MiniMax voices on English agents only. They are not supported on Cartesia sonic-2, nor on MiniMax when language is es-US or fr-FR — MiniMax accepts pronunciation phonemes for English only and mis-renders Spanish and French. Set ttsModel to minimax or inworld, or to cartesia with voiceSettings.model set explicitly to sonic-3 or sonic-3.6 (an omitted model does not count); otherwise a non-empty pronunciations array is rejected with 400. Send an empty array [] to clear rules on any voice.You store each pronunciation once and it works on any supported voice, provided it was generated with Apply — the phonemes must be IPA in Cartesia’s <<…>> form. Hand-typed free-text phonemes work on Cartesia only and are silently ignored on MiniMax and Inworld, and rules generated for a Spanish or French voice are ignored on MiniMax. Multi-word entries such as New York work on Cartesia but are ignored on MiniMax and Inworld, whose syntax covers a single word each.Each entry also accepts an optional lang (en | es | fr) recording which language the phonemes were written for. It decides whether MiniMax can speak the row — MiniMax accepts English phonemes only. Omit it and the agent’s language is used as the fallback.word— The word or name (1–100 characters)phonetic(optional) — A plain-English respelling of how the word should sound (e.g.,jay-kob-sen). Used by the dashboard’s “Apply” helper to generate the IPAphonemes; not used directly by the agent.phonemes— The IPA phoneme string (e.g.,<<ˈ|dʒ|eɪ|k|ə|b|s|ə|n>>)lang(optional) —en|es|fr. Which language the phonemes were written for, recorded when they are generated. Decides whether MiniMax can speak the row (MiniMax accepts English phonemes only); omit it and the agent’slanguageis used as the fallback.
[
{ "word": "Telzino", "phonemes": "<<t|ɛ|l|ˈz|iː|n|oʊ>>" },
{ "word": "Jacobsen", "phonetic": "jay-kob-sen", "phonemes": "<<ˈ|dʒ|eɪ|k|ə|b|s|ə|n>>" }
]
Outbound Calling
These fields apply to agents that place calls. See Outbound Calls.string
A fixed line spoken word for word before the outbound greeting — for warnings or advisories that the AI must never rephrase. Leave it empty and the agent starts from its greeting or instructions.
boolean | object
default:"false"
Answering Machine Detection.
Unknown keys are rejected. Partial objects are fine; unset keys use the defaults.
true enables it with the defaults, false disables it, an object tunes it. Unrelated to callDetection.| Key | Values | Default |
|---|---|---|
enabled | boolean | false |
sensitivity | fast, balanced, precise | balanced |
max_detection_ms | integer, null = the preset’s budget | null |
on_machine | leave_voicemail, hang_up, continue_as_human | leave_voicemail |
on_silence | reprompt, hang_up, continue_as_human | reprompt |
uncertain_prompt | string, max 500 | ”Hello, is this a good time?” |
voicemail_max_duration_ms | integer | 25000 |
voicemail_beep_wait_ms | integer | 2000 |
GET /v1/agents/{id} returns this as amd_config.string
Spoken after the beep when detection reaches a voicemail box and
on_machine is leave_voicemail. Empty means hang up without leaving a message.string
Pre-recorded voicemail audio, played instead of speaking
voicemailMessage when set. If it cannot be fetched on the call, the text is spoken instead.boolean | object
default:"false"
IVR Menu Navigation.
true enables it with the defaults, an object tunes it: enabled, target_intent (reach_human, reach_department, custom), target_department (max 200), spoken_keywords (up to 20 strings), max_attempts (1–10), max_duration_ms (up to 600000), fallback_action (press_zero, hang_up, wait_for_human), hold_max_wait_ms (up to 1800000).Requires detection: a request that would leave navigation on while amdConfig is off is refused with 400, in either direction — send both fields to change them together. dtmf_transport is not accepted (the DTMF method is a trunk-level setting). Returned by GET as ivr_navigation.string | null
The retry policy every call this agent places follows, unless the trigger request sends its own
retry_policy_id. Must belong to the account.object | null
Compliance controls for this agent.
null or omitted means none: nothing is required and nothing is refused. Unknown keys are rejected.require_consent_basis(boolean) — every trigger must send aconsent_basisor is refused with400 consent_requiredcalling_windows(object) —start_localandend_localas 24-hourHH:MMon the callee’s clock (end exclusive, no wrapping midnight),timezone_source(callee_npanxx,agent_timezone,explicit) andtimezone(required forexplicit)abandon_rate_alert_threshold(number 0–1) — the abandon rate above whichGET /v1/metricsflags this agent. Defaults to0.03
contact_policy, which holds per-number attempt caps. Returned by GET as contact_policy.Advanced Configuration
object
General configuration object for additional settings.Example:
{
"enable_recording": true,
"user_timezone": "America/New_York",
"inactivity_timeout": 15
}
| Property | Type | Description |
|---|---|---|
enable_recording | boolean | Enable call recording (saved to S3) |
user_timezone | string | IANA timezone for calendar operations (e.g., America/New_York) |
inactivity_timeout | number | Seconds before “Are you still there?” prompt (default: 15) |
ambient_type | string | Type of ambient background audio |
ambient_volume | number | Volume level for ambient audio |
thinking_sounds_enabled | boolean | Enable thinking/processing sounds during pauses |
thinking_volume | number | Volume level for thinking sounds |
thinking_clip_id | string | Sound clip to play while the agent is thinking. KEYBOARD_TYPING (default) or KEYBOARD_TYPING2 |
background_audio_enabled | boolean | Enable background audio during calls |
confirm_transfers | boolean | When true, the agent confirms a transfer is answered before connecting the caller — via a press-1 keypress, or via BLF presence for registered SIP extensions. Defaults to false. Only applies when call transfer is enabled. |
transfer_announcement_enabled | boolean | Set to false to silence the line the platform speaks right before a transfer (“I am now transferring your call. Please stay on the line while I connect you.”). Defaults to true. When off the platform says nothing: a prompt instruction telling the agent to announce the transfer is not reliably followed, so some callers hear silence. Prefer transfer_announcement when you want your own wording spoken on every call. |
transfer_announcement | string | Replaces the platform’s spoken transfer line with your own short sentence (max 97 characters), e.g. "Of course, transferring you to our customer service team now.". The platform speaks it on every transfer. Blank or omitted keeps the default line. |
dtmf_transfer_enabled | boolean | Let the caller reach a transfer destination by keying it, so a rule that transfers to pbx:100 also fires when they press 100. Applies to call transfer and warm transfer rules. Defaults to false. This does not control whether the agent listens for keypresses at all; that stays driven by the agent’s instructions. See Keypad (DTMF) transfers. |
response_filler_enabled | boolean | When the caller is left waiting on a tool call, speak one short filler line after a delay — at most once per turn, never during a transfer, while ending the call, or on a knowledge-base lookup. Defaults to false. |
response_filler_delay_seconds | number | Seconds of silence before the filler (0.5–10, default 1.5) |
response_filler_phrases | array | Up to 10 lines of up to 200 characters; one is picked at random. Empty uses “One moment, let me check that for you.” |
summary_context | string | Additional instructions for the AI-generated call summary. Use this to tell the summary what to focus on or what data to extract (e.g., "Focus on customer complaints. Extract any mentioned order numbers."). |
object
Extensions configuration for webhooks and MCP servers.Structure:Webhook fields:
Webhook conditions:Listings Search tool (All
Adding or changing the source means adding the tool, changing its canonical
On create, a
{
"tools": [
{
"type": "webhook",
"name": "Create_Support_Ticket",
"friendlyName": "Create Ticket",
"description": "Creates a support ticket when the customer reports an issue",
"condition": "after_call",
"method": "POST",
"url": "https://api.helpdesk.com/v2/tickets",
"headers": [
{ "key": "Authorization", "value": "Bearer YOUR_API_TOKEN" },
{ "key": "Content-Type", "value": "application/json" }
],
"parameters": [
{ "key": "subject", "value": "" },
{ "key": "description", "value": "" },
{ "key": "customer_email", "value": "[email protected]" },
{ "key": "priority", "value": "normal" }
]
}
],
"mcp_servers": [
{
"url": "https://mcp.acme.com/mcp",
"name": "Acme CRM MCP"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "webhook" |
name | string | Yes | Unique identifier (e.g., Create_Ticket) |
friendlyName | string | Yes | Human-readable display name |
description | string | Yes | What the webhook does - helps AI understand when to use it |
condition | string | Yes | When to trigger: before_call, during_call, or after_call |
method | string | Yes | HTTP method: GET, POST, PUT, PATCH, or DELETE |
url | string | Yes | Endpoint URL to call |
headers | array | No | Array of {key, value} objects for HTTP headers |
parameters | array | No | Array of {key, value} objects - AI extracts values from conversation |
include_agent_name | boolean | No | Default true. after_call and other event-triggered webhooks (every condition except during_call and before_call) add an agent_name field to the request body. Set false to omit it for APIs that reject unknown fields (e.g. Freshdesk) |
enabled | boolean | No | Default true. Set false to switch the webhook off without deleting it: it is never called and never offered to the agent. extensions is replaced as a whole on update, so leaving enabled out of a webhook you send switches it back on |
before_call- Execute before the agent answers (pre-fetch caller info)during_call- Execute while call is active (create tickets, real-time lookups)after_call- Execute after call ends (log to CRM, send summaries)
Parameters: Leave the
value empty for fields you want the AI to extract from the conversation. Provide default values for fallbacks.type: "listings_search"). Lets the agent search a listings page during a call; see Listings Search. An agent can have one.{
"type": "listings_search",
"name": "search_listings",
"friendlyName": "Listings_Search",
"description": "When a caller asks what rentals are available, search listings as soon as they give a location, budget or bedroom count, and read two or three that match.",
"condition": "during_call",
"parameters": [
{ "key": "listings_url", "value": "https://yourcompany.appfolio.com/listings" },
{ "key": "attested_by", "value": "[email protected]" },
{ "key": "business_name", "value": "Your Company" }
]
}
parameters values are strings. Keys you leave out take their defaults. The stored tool always carries all ten keys, and name, friendlyName and condition are always stored as shown above, so a GET returns the tool in this full form.| Key | Required | Meaning |
|---|---|---|
listings_url | yes | Your listings page, https:// only. Stored in canonical form: an AppFolio page becomes https://<company>.appfolio.com/listings; any other page keeps its path and query. The address rules and messages are listed on Listings Search. |
attested_by | when adding or changing the source | The email address of the person attesting that the business owns, or is authorized to display, the page. Recorded as declared by the API caller. |
attested_at | no | Always set by the server to the time of the write that added or changed the source. A value you send is ignored. |
adapter | no | Derived from listings_url: appfolio or generic. A value that contradicts the address is rejected. |
profile | no | rentals (the only value). |
refresh_minutes | no | 10 (default), 15, 30 or 60. |
fetch_detail_pages | no | "true" (default for AppFolio) reads each listing’s own page for deposit and application fee. Always stored "false" for a non-AppFolio page. |
business_name | no | The name listing texts are “sent by”, at most 100 characters. Empty (default) uses the agent’s name. |
sms_provider | no | twilio, netsapiens, or empty (default: whichever carrier is connected to the agent). |
sms_sender_extension | no | 2 to 10 digits, or empty (default: the agent’s own extension). |
listings_url, or turning fetch_detail_pages from "false" to "true" on an AppFolio page. Only that needs attested_by, sets a new attested_at, and is checked against the Listings Search gate (403). Any public https:// listings page can be saved this way once attested; a non-AppFolio page needs no extra step. Re-sending the tool unchanged (a different URL casing, a trailing / or a query string on an AppFolio address counts as unchanged) keeps the stored attested_by and attested_at whatever you send, but the tool is still validated against the rules below.enabled: false is refused: to switch Listings Search off, remove the tool. Another tool in extensions.tools whose function name is search_listings or send_listing_link is refused too, because Listings Search registers those names.An invalid tool is rejected with 400 Invalid request data, and details names the problem:details[].message | Cause |
|---|---|
Adding or changing a listings source requires the attestation: attested_by (the email address of the person attesting). | attested_by is missing or not an email address on an add or change. |
An agent can have only one Listings Search function. | More than one listings_search tool. |
Another function is named "search_listings", which Listings Search needs. Rename or remove that function to use Listings Search. (or "send_listing_link") | Another tool registers one of the Listings Search function names. |
Listings Search can't be switched off with enabled: false. Remove the Listings Search function instead. | The tool was sent with enabled: false. |
adapter must be "appfolio" for this listings_url (it is derived from the address). (or "generic") | adapter contradicts the address. |
profile must be "rentals"., refresh_minutes must be 10, 15, 30 or 60., fetch_detail_pages must be "true" or "false"., sms_provider must be "twilio", "netsapiens" or empty., sms_sender_extension must be 2 to 10 digits., business_name must be 100 characters or fewer. | That parameter has an invalid value. |
Listings Search instructions must be 4,000 characters or fewer. | description is too long. |
An address message, e.g. The listings address must start with https:// | listings_url is refused; the messages are listed on Listings Search. |
listings_search tool always counts as adding the source: attested_by is required and the Listings Search gate applies.MCP Servers Request Body
Each entry in themcp_servers array inside extensions is validated at write time. The request shape is different from the stored shape returned by GET.
Validated request fields for each mcp_servers entry:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Friendly label for the connection (displayed in the Portal) |
url | string | Yes | MCP endpoint URL. Use your streamable HTTP endpoint when transport_type is "http", or your SSE endpoint when transport_type is "sse" |
transport_type | string | No | "sse" (default) or "http" |
auth_headers | array | No | Array of {key, value} objects sent as HTTP headers to the MCP server |
initialization_options | object | No | Optional MCP initialization options stored with the connection |
allowed_tool_names | array of strings | No | Tools the agent may use from this server. Only these tool definitions are sent to the model, and calls to any other tool are rejected. Omitted or empty means every tool the server publishes (the default). Tools the server adds later stay off while a list is set. On an OpenTable reservation agent’s StayAPI server, only OpenTable tools are kept and the availability, availability-slots and menu tools are always included. Names must match the server’s tool names exactly (as returned by its tools/list). When the server can be reached, names it doesn’t publish are removed and reported in warnings, and a list in which no name matches is rejected with 400 (it would otherwise give the agent zero tools from that server). Up to 500 names, 200 characters each; duplicates are removed |
pre_answer_calls | array of objects | No | Tools the platform calls itself while the phone is ringing, so their results can reach the agent before the greeting (see Pre-Answer). Each item is { "tool_name": string, "arguments"?: object, "input_schema"?: object } and accepts no other fields; arguments defaults to {} and tool_name is up to 200 characters. tool_name must be a tool the agent may use from this server — checked against allowed_tool_names when it is set, otherwise against the server’s published tools when the server can be reached at save time — and may appear only once. String values in arguments, including nested ones, may contain {{caller_id}}, {{agent_id}}, {{direction}}, {{room_name}} or {{timestamp}}, filled in for each call; any other placeholder, or a {{ / }} without its pair, is rejected with 400. A call whose placeholder has no value (for example a withheld caller ID) is skipped. input_schema is the tool’s input schema, stored so the Portal can redraw the form; when present, its required inputs must be filled. All calls run at once and the greeting uses their results only if they all arrive before it would be heard — keep it to 1–2. Each result is limited to 4,000 characters in the agent’s context (the transcript keeps it in full). Omitted or empty means none. Up to 50 items |
Pre-answer lookup on an MCP connection
{
"name": "Acme CRM",
"url": "https://mcp.acme-crm.com/mcp",
"transport_type": "http",
"pre_answer_calls": [
{ "tool_name": "contacts_search", "arguments": { "phone": "{{caller_id}}", "locationId": "loc_123" } }
]
}
name → connection_name, url → mcp_endpoint_url, auth_headers array → header object, and assigns a generated id. See Get Agent — Stored MCP Connection Shape for the full list of stored fields.
The Get Agent response returns the stored shape with snake_case keys:
id, connection_name, mcp_endpoint_url, transport_type (lowercase sse or http), and optional tools entries with name and description only. Do not paste a GET mcp_servers array into a create or update body without transforming it to match the request fields above.The schema is strict — any unrecognized field in an mcp_servers entry (for example, fields from the stored GET shape such as connection_name, mcp_endpoint_url, or id) will be rejected with a 400 error. Only url, name, transport_type, auth_headers, initialization_options, allowed_tool_names, and pre_answer_calls are accepted.MCP servers are auto-tested on save. When you include To validate a configuration before creating an agent, use Test MCP Connection.
extensions.mcp_servers, the API tests each server (opening the transport and listing tools). Reachable servers are stored with a fingerprint and discovered tools, and appear with a green Connected badge in the Portal (agent → Integrations → MCP). Servers that are unreachable at save time are still stored, but without a fingerprint — they appear with a yellow Not tested badge, and the response includes a non-fatal warnings array:{ "data": { "...": "..." }, "warnings": [{ "connection_name": "Acme CRM MCP", "message": "Connection failed" }] }
The
integrations field cannot be set on create. After creating the agent, configure integrations (ModMed, Google/Outlook Calendar, NetSapiens) via the Dashboard or with the integrations field on Update Agent. Twilio is connected once per organization in the Dashboard or Connect (Twilio); Update Agent accepts only the agent’s twilio.fromNumber. Integration secrets are write-only — they never appear in API responses, including this endpoint’s response. MCP servers can be configured in the Dashboard under agent Integrations, or via extensions.mcp_servers on this endpoint and on Update Agent.curl -X POST https://api.telzino.com/v1/agents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Support Agent",
"organizationId": "123e4567-e89b-12d3-a456-426614174000",
"description": "Handles customer inquiries and support requests",
"greetingMessage": "Hello! Thank you for calling Acme Corp. How can I help you today?",
"systemPrompt": "You are a helpful customer support agent for Acme Corp. Be friendly and professional. If the customer needs to speak with a human, use the transfer_call function.",
"agentType": "simple",
"model": "gpt-4.1",
"ttsModel": "cartesia",
"voiceSettings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.0},
"transferEnabled": true,
"transferRules": "Transfer to +15551234567 if the caller asks for billing support",
"language": "en-US",
"timezone": "America/New_York",
"enableRecording": true,
"knowledgebaseLinks": ["https://example.com/faq", "https://example.com/pricing"],
"email": ["[email protected]"]
}'
curl -X POST https://api.telzino.com/v1/agents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Agent with Integrations",
"organizationId": "123e4567-e89b-12d3-a456-426614174000",
"systemPrompt": "You are a helpful customer support agent. After every call, create a ticket using the Create_Ticket tool.",
"model": "gpt-4.1",
"ttsModel": "cartesia",
"voiceSettings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02"},
"language": "en-US",
"timezone": "America/New_York",
"extensions": {
"tools": [
{
"type": "webhook",
"name": "Create_Ticket",
"friendlyName": "Create Support Ticket",
"description": "Creates a support ticket in Freshdesk after every call",
"condition": "after_call",
"method": "POST",
"url": "https://yourcompany.freshdesk.com/api/v2/tickets",
"headers": [
{ "key": "Authorization", "value": "Basic YOUR_FRESHDESK_API_KEY" },
{ "key": "Content-Type", "value": "application/json" }
],
"parameters": [
{ "key": "subject", "value": "" },
{ "key": "description", "value": "" },
{ "key": "customer_email", "value": "" },
{ "key": "priority", "value": "2" }
]
}
],
"mcp_servers": [
{
"name": "Acme CRM",
"url": "https://mcp.acmecrm.com/sse",
"transport_type": "sse",
"auth_headers": [
{ "key": "Authorization", "value": "Bearer YOUR_CRM_TOKEN" }
]
}
]
}
}'
const response = await fetch('https://api.telzino.com/v1/agents', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Customer Support Agent',
organizationId: '123e4567-e89b-12d3-a456-426614174000',
description: 'Handles customer inquiries',
greetingMessage: 'Hello! How can I help you today?',
systemPrompt: 'You are a helpful customer support agent...',
agentType: 'simple',
model: 'gpt-4.1',
ttsModel: 'cartesia',
voiceSettings: { voice_id: 'f786b574-daa5-4673-aa0c-cbe3e8534c02', speed: 1.0 },
transferEnabled: true,
transferRules: 'Transfer to +15551234567 if the caller asks for billing support',
language: 'en-US',
timezone: 'America/New_York',
enableRecording: true,
knowledgebaseLinks: ['https://example.com/faq', 'https://example.com/pricing'],
email: ['[email protected]']
})
});
const { data } = await response.json();
import requests
response = requests.post(
'https://api.telzino.com/v1/agents',
headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
json={
'name': 'Customer Support Agent',
'organizationId': '123e4567-e89b-12d3-a456-426614174000',
'description': 'Handles customer inquiries',
'greetingMessage': 'Hello! How can I help you today?',
'systemPrompt': 'You are a helpful customer support agent...',
'agentType': 'simple',
'model': 'gpt-4.1',
'ttsModel': 'cartesia',
'voiceSettings': {'voice_id': 'f786b574-daa5-4673-aa0c-cbe3e8534c02', 'speed': 1.0},
'transferEnabled': True,
'transferRules': 'Transfer to +15551234567 if the caller asks for billing support',
'language': 'en-US',
'timezone': 'America/New_York',
'enableRecording': True,
'knowledgebaseLinks': ['https://example.com/faq', 'https://example.com/pricing'],
'email': ['[email protected]']
}
)
agent = response.json()['data']
{
"data": {
"id": "abc12345-e89b-12d3-a456-426614174000",
"organization_id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Customer Support Agent",
"description": "Handles customer inquiries",
"greeting_message": "Hello! How can I help you today?",
"system_prompt": "You are a helpful customer support agent...",
"agent_type": "simple",
"model": "gpt-4.1",
"voice_model": "gpt-4o-realtime-preview",
"stt_model": "deepgram",
"tts_model": "cartesia",
"voice_settings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.0},
"config": {},
"extensions": {},
"integrations": {},
"transfer_enabled": true,
"transfer_rules": "Transfer to +15551234567 if the caller asks for billing support",
"language": "en-US",
"timezone": "America/New_York",
"enable_recording": true,
"echo_detection": false,
"call_detection": false,
"knowledgebase_links": ["https://example.com/faq", "https://example.com/pricing"],
"email": ["[email protected]"],
"email_tool_enabled": false,
"email_server_id": null,
"pronunciations": [],
"status": "active",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
{
"error": "Invalid request data",
"details": [
{"path": ["name"], "message": "Name is required"}
]
}
{
"error": "forbidden",
"error_description": "No access to this organization"
}
{
"error": "forbidden",
"error_description": "Model not available for this organization: claude-sonnet-4-6"
}
{
"error": "forbidden",
"error_description": "TTS provider not available for this organization: minimax"
}
{
"error": "forbidden",
"error_description": "Feature(s) not available for this organization: listings_search"
}
{
"error": "forbidden",
"error_description": "Screenshare requires a model that can read images. Choose a different model, or turn screenshare off."
}
{
"error": "forbidden",
"error_description": "Screenshare is not available for healthcare organizations."
}
Built-in Agent Functions
When created, every agent automatically has access to these functions that the AI can call during conversations:| Function | Description |
|---|---|
transfer_call(phone_number) | Transfer call to a SIP extension or US phone number (requires transferEnabled: true) |
end_call() | End the current call gracefully |
get_date_details(date_string) | Parse and get details about a date/time |
Additional functions are added based on enabled integrations (Google Calendar, Outlook, ModMed), configured webhook tools in
extensions.tools, and MCP servers in extensions.mcp_servers.Feature Administration gating
When Feature Administration is enabled, agent creation is validated against the feature gates of the target organization — including locks inherited from its reseller or partner. The API returns403 forbidden when the request:
-
sets
warmTransferEnabled: truewhile Warm Transfer is locked; -
names a
ttsModelthat is locked for the organization.cartesiais never gated — it cannot be locked, and it is the value assigned whenttsModelis omitted, so a create that does not ask for a specific provider is never refused on this ground; -
selects a cloned voice in
voiceSettings.voice_idwhile Custom Voices is locked; -
sets a non-empty
googlePlaceIdwhile Business Listing (Google Places) is locked; -
sets
agentTypetoreceptionist,appointment_scheduler,message_taker,blank_slate,outbound,opentable_reservationortranslatorwhile the matching Template is locked; -
sets
screenshareEnabled: truewhile Screenshare is locked for the organization; -
includes the
ns_reset_passwordtool inextensions.toolswhile NS Password Reset is locked; -
includes a
listings_searchtool inextensions.toolswhile Listings Search is locked. The error isFeature(s) not available for this organization: listings_search, and it is returned even when the tool is also invalid; -
includes a UCaaS function in
extensions.toolswhile that function — or the Netsapiens integration itself — is locked. Each function has its own gate:A Chat Notification tool is recognised by more than its name: any tool withTool name in extensions.toolsFeature that must be available ns_get_timeframeNS Get Timeframe ns_verify_userNS Verify User ns_lookup_directoryNS Lookup Directory ns_set_call_forwarding,ns_read_call_forwarding,ns_delete_call_forwardingNS Set Call Forwarding ns_send_smsNS Send SMS ns_check_sms_repliesNS Check SMS Replies NS_Chat_Messaging,ns_send_chat,ns_check_chat_messagesNS Chat Notification type: "notification"counts if its name beginsns_chat/NS_Chat, if it carries a{"key": "type", "value": "ns_chat"}parameter, or if itsnotificationparameter contains"type": "ns_chat". Those are the same shapes the agent runtime accepts, so a payload that would start a chat is gated whatever it is called. Netsapiens is checked as well as the function’s own feature, so withdrawing the integration blocks all seven at once; when both are locked the error names the integration.You can seedextensions.toolsby copying them from an existing agent’sGETresponse:tools[].typeaccepts the internal types aGETreturns, including thenetsapienstype these UCaaS tools carry. Two exceptions are rejected with400: apaymenttool — enable it afterwards withPUT /v1/agents/{agentId}/payments/tool— and toolparametersthat are not an array of{"key": ..., "value": ...}strings. That includes the Zendesk and Freshdesk template tools (a parameters object; numeric values with atypekey), which can only be sent back exactly asGETreturned them on Update Agent,{"name": ..., "value": ...}parameters, and flow-builder custom function tools.
null, is never blocked — so you can always create an agent without the locked capability.
The Screenshare model check is independent of Feature Administration. Setting
screenshareEnabled: true on an agent whose model cannot read images returns the same 403 forbidden whether or not Feature Administration is enabled for the organization, and whether or not Screenshare itself is locked — see screenshareEnabled above.The Screenshare Healthcare refusal is independent of Feature Administration too. A Healthcare-flagged organization can never opt in to Screenshare, so arming an agent there returns
403 forbidden whether or not Feature Administration is enabled and whether or not Screenshare itself is locked.agentType values of simple, advanced and custom are never gated by a template lock.
They do not identify a single template: simple is the stored type for the Support Agent
(CRM) templates and is also the default when agentType is omitted, so a request using it
cannot be attributed to one template and is treated as not having chosen a template at all.
Callers that never used a template are therefore unaffected by any template lock.