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

# Configuration

> Configure SLNG voice agents. Write effective system prompts, attach shared tools and MCP servers, and use template variables to personalize calls at dispatch time.

The [API reference](https://slng.mintlify.site/api-reference) covers every field on the agent object. This page covers the parts that need more context than a schema can give: agent fields, model configuration, system prompt structure, tools, and template variables.

You can manage agents through the API or through [Managed Agents](https://slng.mintlify.site/dashboard/agent-infra.md). For working code examples (create, update, delete), see [API examples](https://slng.mintlify.site/examples/agents-api.md).

## Agent fields

### Required fields

| Field | Type | Description |
| - | - | - |
| `name` | string | Unique agent name (max 255 chars) |
| `system_prompt` | string | System prompt with Handlebars template support |
| `greeting` | string | Default greeting |
| `language` | string | Language code (e.g., "en", "es", "fr") |
| `region` | string | Deployment region. See [Agent regions](https://slng.mintlify.site/voice-agents.md#agent-regions) |
| `models` | object | Model configuration (see [Models](#models)) |

### Common optional fields

| Field | Type | Default | Description |
| - | - | - | - |
| `inbound_greeting` | string | `null` | Greeting override for inbound calls |
| `outbound_greeting` | string | `null` | Greeting override for outbound calls |
| `sip_inbound_trunk_id` | string | `null` | Inbound connection ID (for inbound calls) |
| `sip_outbound_trunk_id` | string | `null` | Outbound connection ID (for outbound calls) |
| `enable_interruptions` | boolean | `true` | Allow user to interrupt agent speech |
| `idle_nudges` | object | language defaults | Silence recovery prompts and final hangup behavior |
| `tool_mode` | string | `"shared"` | Shared organisation tool library used by new agents |
| `tool_refs` | array | `[]` | Published organisation or curated tool versions attached to the agent |
| `mcp_refs` | array | `[]` | MCP tools attached with their reviewed schema hashes |
| `template_defaults` | object | `{}` | Default values for template variables |
| `template_variable_options` | object | `{}` | Per-variable options, such as marking a variable optional (see [Optional variables](#optional-variables)) |

## Models

The `models` object configures the STT, LLM, and TTS models used by the agent runtime:

```json theme={null}
{
  "models": {
    "stt": "slng/deepgram/nova:3-en",
    "llm": "groq/openai/gpt-oss-120b",
    "tts": "slng/deepgram/aura:2-en",
    "tts_voice": "aura-2-thalia-en",
    "stt_kwargs": {},
    "llm_kwargs": {},
    "tts_kwargs": {}
  }
}
```

### Finding models

Browse STT and TTS models with the [Catalog API](https://slng.mintlify.site/api-reference/overview.md): `GET https://api.slng.ai/v1/catalog/models` lists the catalog and supports `service_type`, `language`, and `region` query parameters, and `GET https://api.slng.ai/v1/catalog/models/{model_code}` returns a single model's details, including its voices for TTS models. The [Models pages](https://slng.mintlify.site/models.md) on this site show the same catalog.

For LLM options, open your agent in the Dashboard and check the Models tab of the agent editor — it lists the LLMs available for your agent's language and region combination.

### Fallbacks and timeouts

```json theme={null}
{
  "models": {
    "stt": "slng/deepgram/nova:3-en",
    "llm": "groq/openai/gpt-oss-120b",
    "tts": "slng/deepgram/aura:2-en",
    "tts_voice": "aura-2-thalia-en",
    "fallbacks": {
      "stt": ["slng/deepgram/nova:3-multi"],
      "llm": ["groq/openai/gpt-oss-120b"],
      "tts": [
        { "model": "slng/deepgram/aura:2-en", "voice": "aura-2-asteria-en" }
      ]
    },
    "stt_final_timeout_s": 4.0,
    "llm_first_token_timeout_s": 6.0,
    "tts_first_audio_timeout_s": 4.0,
    "failure_audio_enabled": true
  }
}
```

## Writing a system prompt

A system prompt tells the agent who it is, how to talk, and what to do on the call. We recommend splitting it into five sections:

| Section | Purpose |
| - | - |
| **Identity** | Name, role, and reason for calling |
| **Style** | Tone, sentence length, filler words |
| **Response guidelines** | One question at a time, how to handle mishearing, interruptions |
| **Task and conversation flow** | Step-by-step script with `<wait for response>` markers |
| **Guardrails** | Topics to avoid, how to handle off-script questions |

> **Full example: healthcare outreach agent**
>
> ```text theme={null}
> [Identity]
> - Your name is Sarah, and you are a patient outreach coordinator for {{practice_name}}.
> - You are calling existing patients who are due for upcoming or overdue appointments, including annual physicals, follow-up visits, and preventive screenings.
> - Your goal is to help the patient schedule their next visit conveniently and answer basic questions about appointment availability.
>
> [Style]
> - Warm, caring, and conversational. You genuinely want to help the patient stay on top of their health.
> - Keep every response to 1-2 sentences. This is a phone call, not a lecture.
> - Use contractions and natural speech. Say "you're" not "you are," say "it's" not "it is."
> - Use softeners like "of course," "absolutely," "no problem," and "sure thing."
> - You may use light filler phrases like "Let me see..." or "So..." sparingly to sound natural.
> - Never use markdown, bullet points, numbered lists, or any text formatting in your responses. Everything you say will be read aloud by a text-to-speech engine and must sound completely natural as spoken language.
>
> [Response Guidelines]
> - Ask only one question at a time. Never stack multiple questions in a single response.
> - Wait for the patient to respond before continuing.
> - If something is unclear, politely ask for clarification.
> - Never fabricate appointment availability, medical information, or provider details.
> - Speak dates and times naturally. Say "Tuesday, March eleventh at two thirty in the afternoon" not "3/11 at 14:30."
> - When reading back any information like phone numbers or dates, normalize for speech. Say "five five five, eight six seven, five three oh nine" not "5558675309."
> - If you are not sure you heard the patient correctly, repeat back what you understood and ask for confirmation before proceeding. Speech-to-text can mishear, so always verify names, dates, and times.
> - If the patient interrupts you mid-sentence, stop speaking immediately and listen. Do not try to finish your previous thought. Acknowledge their input and adjust accordingly.
>
> [Task & Conversation Flow]
>
> 1. Greet the patient by name using the greeting message. Confirm you are speaking to the right person. <wait for response>
>
> 2. If the person confirms they are the patient, continue to step 3. If they say it is someone else or a wrong number, apologize politely and end the call.
>
> 3. Let the patient know they are due for a visit. Say something like "I see it's been a while since your last appointment, and we'd love to get you in for a checkup." Ask if they would be interested in scheduling. <wait for response>
>
> 4. If the patient says yes, ask what days of the week generally work best for them. <wait for response>
>
> 5. Based on their preference, offer two specific options. Use realistic example slots such as "We have a Tuesday, March eleventh at ten in the morning, or a Thursday, March thirteenth at two thirty in the afternoon. Either of those work for you?" <wait for response>
>
> 6. Once the patient picks a time, repeat back the full appointment details including the day, date, and time and ask them to confirm. This step is important. <wait for response>
>
> 7. If confirmed, let them know they are all set. Mention that they may receive a reminder closer to the date. Ask if they have any questions before you go. <wait for response>
>
> 8. If the patient says they are not interested or want to wait, respect their decision. Let them know they can call the office anytime when they are ready, and wish them well.
>
> 9. If the patient asks about a specific doctor or provider, let them know you can check availability for that provider and adjust the offered times accordingly.
>
> 10. If the patient asks medical questions such as symptoms, medications, or test results, let them know you are not able to provide medical advice but their care team would be happy to help with that during their visit.
>
> [Guardrails]
> - Never provide medical advice, diagnose symptoms, or discuss test results.
> - Never access, share, or discuss specific medical records, prescriptions, or treatment details.
> - Never pressure the patient into scheduling. If they decline, accept it gracefully.
> - Do not discuss billing, insurance coverage, or payment information. If asked, let them know the office billing team can help with those questions.
> - If the patient asks about something outside your scope, say something like "That's a great question. The team at the office would be the best ones to help with that."
> - If the conversation drifts off topic, gently guide it back by saying "I appreciate that. So regarding getting your visit on the calendar, would you like to pick a time?"
> - If the patient repeats "hello" or "hi" multiple times, respond with "I'm here, can you hear me alright?"
> - If there is prolonged silence, prompt gently with "Are you still there?" or "Take your time, I'm here whenever you're ready."
> - Never mention or imply that you are an AI, a bot, or an automated system unless directly asked.
> ```

A few things worth calling out:

* **Keep it spoken.** The output goes through TTS, so write the way you'd actually talk. No markdown formatting, no numbered lists in the agent's responses.
* **One question per turn.** Stacking questions confuses both the caller and the STT model.
* **Use `<wait for response>` markers.** They make the conversation flow explicit for the LLM.
* **Normalize for speech.** Phone numbers, dates, and times should be written out the way you'd say them aloud.

## Tools

Tools live in your organisation library. You create a draft once, test it, publish an immutable version, and attach that version to one or more agents. New agents use `tool_mode: "shared"` and reference tools through `tool_refs`. MCP tools use `mcp_refs`.

| Resource | Use it for |
| - | - |
| Custom Code | Run typed Python in an isolated, offline sandbox |
| API Request | Call an HTTPS endpoint with typed arguments and Vault-backed authentication |
| Capability tool | End a call, detect voicemail, transfer a call, send SMS, get the current time, or get the caller number |
| MCP server | Connect a remote MCP server and attach selected MCP tools |

> [!NOTE]
>
> Tool drafts are never attached directly. An attachment always pins a published version, so editing or publishing a new version cannot change a live agent until you upgrade its attachment.

### Create a Custom Code tool

Custom Code uses Pydantic models for its input and output contract and a `handler(input)` entry point. The sandbox has no internet access. Add exact pinned Python dependencies when you need packages beyond the runtime.

```bash cURL theme={null}
curl -X POST https://api.agents.slng.ai/v1/agents/tools \
  -H "Authorization: Bearer SLNG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "normalize_timestamp",
    "tool_type": "code",
    "description": "Normalize an ISO timestamp to UTC",
    "code_src": "from datetime import timezone\nfrom dateutil.parser import isoparse\nfrom pydantic import BaseModel\n\nclass Input(BaseModel):\n    timestamp: str\n\nclass Output(BaseModel):\n    normalized_utc: str\n\ndef handler(input: Input) -> Output:\n    parsed = isoparse(input.timestamp)\n    if parsed.tzinfo is None:\n        raise ValueError(\"timestamp must include a timezone\")\n    value = parsed.astimezone(timezone.utc).isoformat().replace(\"+00:00\", \"Z\")\n    return Output(normalized_utc=value)",
    "config": { "type": "code" },
    "declared_secrets": [],
    "dependencies": ["python-dateutil==2.9.0.post0"]
  }'
```

The response contains the tool `id`. Use it for the remaining steps:

```bash cURL theme={null}
# Build the sandbox and derive the Pydantic input and output schemas.
curl -X POST https://api.agents.slng.ai/v1/agents/tools/TOOL_ID/introspect \
  -H "Authorization: Bearer SLNG_API_KEY"

# Test the exact draft.
curl -X POST https://api.agents.slng.ai/v1/agents/tools/TOOL_ID/run \
  -H "Authorization: Bearer SLNG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sample_input": { "timestamp": "2037-11-19T12:34:56Z" },
    "confirm_side_effects": true
  }'

# Publish an immutable version after the checks pass.
curl -X POST https://api.agents.slng.ai/v1/agents/tools/TOOL_ID/publish \
  -H "Authorization: Bearer SLNG_API_KEY"
```

A dependency environment can still be preparing when you build, run, publish, attach, or upgrade a tool. Retry the same request after the `Retry-After` delay when the API returns `202` or a retryable preparation response.

### Attach a published tool

Create an agent with the published tool ID and version:

```json theme={null}
{
  "schema_version": 2,
  "tool_mode": "shared",
  "name": "Timestamp Assistant",
  "system_prompt": "Help the caller and use normalize_timestamp when a timestamp needs conversion.",
  "greeting": "Hi, how can I help?",
  "language": "en",
  "region": "eu-central",
  "models": {
    "stt": "slng/deepgram/nova:3-en",
    "llm": "groq/openai/gpt-oss-120b",
    "tts": "slng/deepgram/aura:2-en",
    "tts_voice": "aura-2-thalia-en"
  },
  "tool_refs": [
    {
      "attachment_id": "266f57b3-e9bd-4246-9d42-fd112922e09d",
      "tool_id": "75580a0c-d972-4d4d-a7eb-88af21c42eed",
      "version": 1,
      "invocation": "model"
    }
  ],
  "mcp_refs": []
}
```

The model sees the published tool schema and decides when to call a model-invoked attachment. You can override its model-facing description or lock declared arguments through `argument_overrides`.

To change attachments on an existing agent:

1. Download the complete document with `GET /v1/agents/{agent_id}/config`.
2. Edit `tool_refs` or `mcp_refs`.
3. Send the complete document with `PUT /v1/agents/{agent_id}`.

`PATCH` does not change shared attachments because attachment validation and dependency preparation require the full agent document.

### Run a tool automatically

Custom Code, API Request, Current Date and Time, and User Phone Number tools can run automatically from system events. Set `invocation` to `system`, then provide its triggers and arguments:

```json theme={null}
{
  "attachment_id": "f0d84ac5-7988-460c-995d-2ad9621c09bb",
  "tool_id": "6da4eb89-6449-46d6-8f8d-469b93e15c58",
  "version": 1,
  "invocation": "system",
  "system": {
    "triggers": [{ "event": "call_start" }],
    "arguments": []
  },
  "config_overrides": {
    "type": "current_datetime",
    "timezone": "Europe/Madrid"
  }
}
```

Available events are `call_start`, `first_user_message`, `call_end`, `tool_succeeded`, and `tool_failed`. Custom Code supports every event except `call_end`. The other supported system tool types support all five events. For tool outcome events, set `source_attachment_id` to the attachment that must succeed or fail.

### Attach an MCP tool

Register an MCP server with `POST /v1/agents/mcp-servers`, then call its `connect` endpoint to discover tools and their schema hashes. Attach one discovered tool by pinning the exact hash you reviewed:

```json theme={null}
{
  "attachment_id": "00b3ce6d-a6d2-43a6-8ef7-7c4fb5d3c141",
  "server_id": "6090c15c-708e-46a7-9c18-6411c11940e3",
  "tool_name": "search_support_docs",
  "observed_schema_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
```

MCP attachments are always model-invoked. If the remote schema changes, reconnect the server, review the new schema, and update the attachment hash before the agent can use it.

### Idle nudges

`idle_nudges` configures what the runtime should do when the caller goes silent for too long.

```json theme={null}
{
  "idle_nudges": {
    "enabled": true,
    "first_nudge_delay_seconds": 15,
    "second_nudge_delay_seconds": 30,
    "hangup_delay_seconds": 15,
    "first_nudge_text": "Are you still there?",
    "second_nudge_text": "I’m still here if you need a moment.",
    "final_hangup_text": "I’m going to disconnect for now. Feel free to call us back anytime."
  }
}
```

If you omit `idle_nudges`, the backend can apply language-specific defaults when returning the agent configuration.

## Runtime behavior

Some behavior is built into the agent runtime and worth knowing about when you design a call flow.

### Turn taking and interruptions

`enable_interruptions` (default `true`) controls whether the caller can talk over the agent. When enabled, the runtime applies adaptive interruption handling: brief sounds and backchannels don't cut the agent off, and if the agent stops for what turns out to be a false interruption, it resumes speaking where it left off. When disabled, the agent finishes what it is saying regardless of caller audio.

### Noise cancellation

Noise cancellation is always on. The runtime applies it automatically to both phone and web calls — there is nothing to configure.

### Failure audio

When model warm-up or a fatal model failure prevents the call from proceeding, the caller hears a short localized "technical difficulties" message before the call is hung up, instead of silence. This is on by default; set `"failure_audio_enabled": false` in the `models` object to hang up without the message.

### Idle nudges and tools

Idle nudge timers pause while a tool is executing, so a slow API Request does not trigger an "are you still there?" prompt during a lookup. The timers resume when the tool completes.

### Human transfer teardown

After a successful transfer, the agent leaves the call and the caller talks to the transfer target directly. The session ends when either remaining party disconnects.

## Attachment values

An attachment can adapt one published tool for a specific agent without changing the shared version.

Use `argument_overrides` to lock a declared tool argument to one value source:

| Value | Source |
| - | - |
| `"eu-central"`, `3`, `true` | Literal |
| `"{{customer_id}}"` | Call input or runtime variable |
| `"{{$DEFAULT_REGION}}"` | Readable Vault variable |
| `"{{@call_id}}"` | Call context |

```json theme={null}
{
  "attachment_id": "266f57b3-e9bd-4246-9d42-fd112922e09d",
  "tool_id": "75580a0c-d972-4d4d-a7eb-88af21c42eed",
  "version": 1,
  "argument_overrides": {
    "region": "{{$DEFAULT_REGION}}",
    "customer_id": "{{customer_id}}",
    "call_id": "{{@call_id}}"
  }
}
```

Use `config_overrides` only for the fields supported by the attached tool type. For example, an API Request attachment can override its URL, and a Current Date and Time attachment can override its timezone. Custom Code has no config overrides.

You can also change the description shown to the model or add a pre-action message:

```json theme={null}
{
  "description": "Look up the order only after the caller provides an order number.",
  "execution_policy": {
    "pre_action_message": {
      "enabled": true,
      "wait": true,
      "text": {
        "segments": [
          { "type": "literal", "value": "Let me check that order for you." }
        ]
      }
    }
  }
}
```

## Template variables

Use `{{variable_name}}` anywhere in the `system_prompt` or `greeting` to inject values at runtime.

There are two layers:

1. **`template_defaults`**: Default values set on the agent itself. These apply to every call unless overridden.
2. **`arguments`**: Per-call overrides passed when [dispatching a call](https://slng.mintlify.site/examples/agents-calls.md#calls-with-template-variables).

```json theme={null}
// On the agent
{
  "system_prompt": "You are an outreach coordinator for {{practice_name}}. You are calling {{patient_name}}.",
  "greeting": "Hi, is this {{patient_name}}?",
  "template_defaults": {
    "practice_name": "Greenfield Family Medicine",
    "patient_name": "there"
  }
}
```

```json theme={null}
// When dispatching a call: overrides template_defaults
{
  "phone_number": "+14155551234",
  "arguments": {
    "patient_name": "Maria"
  }
}
```

In this example, `patient_name` resolves to "Maria" (from the call arguments) and `practice_name` resolves to "Greenfield Family Medicine" (from the agent defaults).

The API response includes a `template_variables` field that lists every `{{variable}}` found in the prompt, along with whether it has a default value. This is useful for validating your templates before dispatching calls.

Per-call `arguments` are limited to 32 keys, with keys up to 64 characters, values up to 1024 characters, and a combined value payload up to 8192 characters.

### Optional variables

Every template variable is required by default: dispatching a call without a value for it (from `arguments` or `template_defaults`) is rejected. Use `template_variable_options` to mark a variable optional:

```json theme={null}
{
  "system_prompt": "You are calling {{patient_name}}. {{extra_context}}",
  "template_variable_options": {
    "extra_context": { "required": false }
  }
}
```

`template_variable_options` maps a variable name to its options — currently `required` (boolean, default `true`). An optional variable does not need a default: you can save the agent and dispatch calls without supplying a value. At dispatch, a missing optional variable renders as an empty string in the prompt and greeting, and a non-required system tool argument that references it is skipped instead of failing the tool. Optional variables are not supported in API Request URLs.

## Runtime variables

Use `runtime_variables` when the model needs to capture call-scoped values during the conversation and reuse them later in tool configuration.

These values:

* exist only for the active call or web session
* are set by the model through the built-in `set_runtime_variables` tool
* can be referenced in API Request URLs and automatic tool template arguments using the same `{{variable_name}}` syntax

```json theme={null}
{
  "runtime_variables": [
    {
      "name": "order_id",
      "description": "Customer order identifier gathered during the call."
    },
    {
      "name": "issue_type",
      "description": "Short label for the support issue."
    }
  ]
}
```

After the call, the captured values are included in the call detail response as `memory_variables`: each runtime variable comes back with its `value` and a `status` of `"set"` or `"unset"`, so you can see what the model captured without parsing the transcript.

## Next steps

> **[API examples](https://slng.mintlify.site/examples/agents-api.md)**
>
> Create, test, update, and delete agents with code.

> **[Dispatching calls](https://slng.mintlify.site/examples/agents-calls.md)**
>
> Outbound calls, template variables, and batch dispatch.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.