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

# Configuring SLNG voice agents

> Configure SLNG voice agents. Write effective system prompts, wire up tools and webhooks, and use template variables to personalize calls at dispatch time.

The [API reference](/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 [Agent Infra](/dashboard/agent-infra). For working code examples (create, update, delete), see [Agent API examples](/examples/agents-api).

## 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](/voice-agents#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 |
| `tools`                 | array   | `[]`              | Tools available to the agent                       |
| `template_defaults`     | object  | `{}`              | Default values for template 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": {}
  }
}
```

### Supported LLMs

`llm` accepts one of the following model IDs:

| Model          | ID                                            |
| -------------- | --------------------------------------------- |
| Nemotron Super | `bedrock-mantle/nvidia.nemotron-super-3-120b` |
| Nemotron Nano  | `bedrock-mantle/nvidia.nemotron-nano-3-30b`   |
| GPT OSS 120b   | `groq/openai/gpt-oss-120b`                    |

### 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             |

<Accordion title="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.
  ```
</Accordion>

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

Agents support four tool types. You pass them in the `tools` array when creating or updating an agent.

### Template tools

Two built-in templates: `hangup` and `voicemail_detection`. These handle call control without any backend setup.

```json theme={null}
{
  "type": "template",
  "id": "dc4d6391-dc51-44e3-a816-bf65948290a2",
  "template": "hangup",
  "execution_policy": {
    "pre_action_message": {
      "enabled": true,
      "text": "Thank you for your time. Have a great day."
    }
  }
}
```

The `execution_policy.pre_action_message` tells the agent to say something before executing the tool. For `hangup`, the agent will speak the text and then end the call.

You can override the default tool prompt with the `prompt` field if you want to change when the LLM decides to use the tool.

### Current date/time tool

Use the `current_datetime` built-in when the agent needs reliable time-sensitive context without calling your backend.

```json theme={null}
{
  "type": "built_in",
  "id": "f0d84ac5-7988-460c-995d-2ad9621c09bb",
  "built_in": "current_datetime",
  "timezone": "Europe/Madrid",
  "prompt": "Use this whenever you need the current local date, time, day, or UTC offset for the caller."
}
```

At runtime this becomes a `get_current_datetime` tool and returns a JSON object with:

* `timezone`
* `local_datetime`
* `local_date`
* `local_time`
* `day_of_week`
* `utc_offset`
* `is_dst`

Like webhook tools, you can also configure it with `"source": "system"` and `system.triggers` if you want the runtime to inject current date/time context automatically on events like `call_start`.

### Webhook tools

Webhook tools call your backend over HTTP. The LLM decides when to invoke them based on the `name` and `description` you provide. Parameters follow JSON Schema.

```json theme={null}
{
  "type": "webhook",
  "id": "d8f8c1d7-3cf0-4c95-bb06-f28a2a1d2b50",
  "name": "check_order",
  "description": "Look up the status of a customer order",
  "url": "https://api.yourcompany.com/orders/lookup",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "description": "The order ID" },
      "customer_email": {
        "type": "string",
        "description": "Customer email for verification"
      }
    },
    "required": ["order_id", "customer_email"]
  },
  "auth": { "type": "bearer", "token": "your-api-token" },
  "timeout_seconds": 10
}
```

**Authentication**: Two options:

* `"auth": { "type": "bearer", "token": "..." }` sends an `Authorization: Bearer <token>` header
* `"auth": { "type": "hmac", "secret": "..." }` sends an `X-Signature-256` header (HMAC-SHA256 of the request body)

Secrets are write-only. The API never returns them.

**Fire-and-forget**: Set `"wait_for_response": false` when you don't need the result back in the conversation, like logging a call event to your CRM.

**Result visibility**: Use `"show_results_to_llm": false` when a webhook should run but the successful response body should stay hidden from the model. Use `llm_result_instructions` to tell the model how to use a successful response when it is visible. `llm_result_instructions` is static configuration and does not support runtime personalization.

**URL personalization**: Webhook URLs may use `{{variables}}`, but only in path segments and query parameter values. The scheme, host, port, credentials, fragments, and query parameter names must remain literal.

### System-triggered webhooks

By default, webhook tools are `"source": "contextual"`: the LLM calls them during conversation. Set `"source": "system"` to trigger them automatically on specific events instead.

```json theme={null}
{
  "type": "webhook",
  "id": "a1b2c3d4-5678-9abc-def0-123456789abc",
  "name": "post_call_summary",
  "description": "Send a call summary to the CRM after the call ends",
  "url": "https://api.yourcompany.com/call-summaries",
  "parameters": { "type": "object", "properties": {} },
  "source": "system",
  "wait_for_response": false,
  "system": {
    "triggers": [{ "event": "call_end" }],
    "arguments": [
      {
        "name": "call_id",
        "type": "string",
        "required": true,
        "source": { "type": "call_id" }
      },
      {
        "name": "transcript",
        "type": "transcript_messages",
        "required": true,
        "source": { "type": "transcript_messages", "max_messages": 200 }
      }
    ]
  }
}
```

Available trigger events: `call_start`, `first_user_message`, `call_end`, `tool_succeeded`, `tool_failed`. For `tool_succeeded` and `tool_failed`, you also need to set `source_tool_id` to specify which tool's outcome fires the trigger.

Arguments can pull from several sources:

| Source type                      | What it provides                                           |
| -------------------------------- | ---------------------------------------------------------- |
| `constant`                       | A fixed value you define                                   |
| `transcript_messages`            | Recent conversation transcript (bounded by `max_messages`) |
| `call_id`, `room_name`, `job_id` | IDs from the current session                               |
| `agent_id`, `agent_name`         | The agent's own metadata                                   |
| `phone_number`                   | Caller's phone number                                      |
| `first_user_message`             | The first thing the caller said                            |
| `call_end_reason`                | Why the call ended                                         |

System webhook `description`, `llm_result_instructions`, and `execution_policy.pre_action_message.text`
must remain static. Only `url` and `system.arguments[*].source.template` support runtime
personalization for webhook tools.

### Human transfer

Transfers the caller to a phone number. Requires an outbound SIP trunk on the agent.

```json theme={null}
{
  "type": "human_transfer",
  "id": "b2c3d4e5-6789-abcd-ef01-234567890abc",
  "name": "transfer_to_support",
  "description": "Transfer the caller to a live support agent",
  "phone_number": "+14155559999",
  "execution_policy": {
    "pre_action_message": {
      "enabled": true,
      "text": "Let me connect you to a live support specialist."
    }
  }
}
```

The LLM uses the `name` and `description` to decide when to transfer, just like webhook tools.

### 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.

## Tool personalization

Tool personalization uses the same `{{variable}}` syntax as prompts and greetings, but only on runtime-safe tool fields. Tool fields are validated when the tool runs rather than when the call starts.

* **Prompt and greeting variables** are start-critical. Missing values can block the session.
* **Tool-only variables** are runtime-only. Missing or invalid values fail the tool, not the call.

Supported surfaces in v1:

| Tool surface                             | Supports personalization | Notes                                                                                                                                      |
| ---------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Contextual webhook `url`                 | Yes                      | Only in path segments and query parameter values. Scheme, host, port, credentials, fragments, and query parameter names must stay literal. |
| System webhook `url`                     | Yes                      | Only in path segments and query parameter values. Scheme, host, port, credentials, fragments, and query parameter names must stay literal. |
| System webhook argument values           | Yes                      | Scalar values only in v1 (`string`, `integer`, `number`, `boolean`)                                                                        |
| System webhook `description`             | No                       | Must stay static                                                                                                                           |
| System webhook `llm_result_instructions` | No                       | Must stay static                                                                                                                           |
| System webhook pre-action message text   | No                       | Must stay static                                                                                                                           |
| Human transfer `phone_number`            | Yes                      | Must resolve to E.164                                                                                                                      |
| Built-in `timezone`                      | Yes                      | Must resolve to an IANA timezone                                                                                                           |
| Auth secrets                             | No                       | Secrets remain literal-only                                                                                                                |

```json theme={null}
{
  "tools": [
    {
      "type": "webhook",
      "id": "2e4e8759-8467-4a53-9c96-0e08112ea13d",
      "name": "lookup_catalog_item",
      "description": "Load item details before continuing.",
      "url": "https://api.example.com/catalog/{{item_id}}/webhook?case={{case_id}}",
      "parameters": { "type": "object", "properties": {} },
      "source": "system",
      "system": {
        "triggers": [{ "event": "call_end" }],
        "arguments": [
          { "name": "retry_count", "type": "integer", "source": { "type": "template", "template": "{{max_retries}}" } },
          { "name": "vip", "type": "boolean", "source": { "type": "template", "template": "{{is_vip}}" } }
        ]
      }
    },
    {
      "type": "human_transfer",
      "id": "af5265b7-4a93-4bf5-a56a-7ccb0f3c0145",
      "name": "transfer_to_support",
      "phone_number": "+34{{local_number}}"
    },
    {
      "type": "built_in",
      "id": "0d2fdfe8-0375-4283-bfe0-e6013ed1df59",
      "built_in": "current_datetime",
      "timezone": "Europe/{{timezone_city}}"
    }
  ]
}
```

Validation rules:

* URLs must resolve to a full `http://` or `https://` URL while preserving the configured origin.
* URL placeholders are allowed only in path segments and query parameter values, and rejected in scheme, host, port, user info, fragments, and query parameter names.
* `phone_number` must resolve to E.164, for example `+34600111222`.
* `timezone` must resolve to an IANA timezone, for example `Europe/Madrid`.
* Templated system argument values are scalar-only in v1. Arrays must remain literal constants.
* `description`, `llm_result_instructions`, and pre-action message text are static configuration and do not support runtime personalization.

Runtime `arguments` are bounded:

* Maximum 32 keys
* Maximum key length 64 characters
* Maximum value length 1024 characters
* Maximum combined value length 8192 characters
* Template substitution only. Expressions and formulas are not supported.

## 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](/examples/agents-calls#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.

## 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 webhook URLs and system 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."
    }
  ]
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="Agent API examples" icon="code" href="/examples/agents-api">
    Create, test, update, and delete agents with code.
  </Card>

  <Card title="Dispatching calls" icon="phone" href="/examples/agents-calls">
    Outbound calls, template variables, and batch dispatch.
  </Card>
</CardGroup>
