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

# Update Agent

> Updates an existing agent

## Path Parameters

<ParamField path="agentId" type="string" required>
  The unique identifier of the agent (UUID format)
</ParamField>

## Request Body

All fields are optional. Only include the fields you want to update.

<ParamField body="organizationId" type="string">
  Optional organization guard (UUID). When provided, the request is rejected with `404` unless the target agent already belongs to this organization. It does **not** move the agent between organizations — it only scopes the update so you cannot accidentally modify an agent in a sibling organization under the same account. Omit it to keep the default account-scoped behavior.
</ParamField>

### Basic Information

<ParamField body="name" type="string">
  Display name of the agent (1-255 characters)
</ParamField>

<ParamField body="description" type="string">
  Description of what the agent does
</ParamField>

<ParamField body="greetingMessage" type="string">
  Initial message the agent says when call starts
</ParamField>

<ParamField body="outboundGreetingMessage" type="string">
  Greeting spoken when this agent places an outbound call (see [Outbound Calls](/api-reference/outbound-calls/overview))
</ParamField>

<ParamField body="systemPrompt" type="string">
  System prompt defining agent behavior and personality
</ParamField>

<ParamField body="agentType" type="string">
  Type of agent configuration.

  **Supported values:** `simple`, `advanced`, `custom`, `appointment_scheduler`, `receptionist`, `message_taker`
</ParamField>

<ParamField body="status" type="string">
  Agent activation status.

  **Supported values:**

  * `active` - Agent is available to receive calls
  * `inactive` - Agent is temporarily disabled
  * `cancelled` - Agent is permanently disabled
  * `pending` - Agent is pending activation

  To switch between `active` and `inactive` on their own, use [Update Agent Status](/api-reference/agents/update-status) — it takes just this field, so there is no risk of overwriting other configuration.
</ParamField>

### AI/Voice Model Configuration

<ParamField body="model" type="string">
  AI model used for the agent. See [List LLM Models](/api-reference/llm-models) for the full list with descriptions.

  **Supported values:** `gpt-4.1` (recommended), `gpt-4.1-mini`, `gpt-4o-mini`, `grok-3-fast`, `moonshotai/Kimi-K2.6`, `claude-sonnet-4-6`
</ParamField>

<ParamField body="voiceModel" type="string">
  Voice model for real-time speech
</ParamField>

<ParamField body="sttModel" type="string">
  Speech-to-text model.

  **Supported values:** `deepgram`
</ParamField>

<ParamField body="ttsModel" type="string">
  Text-to-speech model.

  **Supported values:** `cartesia`, `minimax`, `inworld`

  For `inworld`, set `voiceSettings.voice_id` to a full Inworld voice string, e.g. `Inworld.TTS2.Ashley`.
</ParamField>

<ParamField body="voiceSettings" type="object">
  Voice configuration settings. Structure varies based on `ttsModel`:

  **For Cartesia:**

  ```json theme={null}
  {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.0}
  ```

  Cartesia voices also accept two optional fields:

  | Property   | Type   | Description                                                                                                                                                                                                                                                                            |
  | ---------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `model`    | string | Cartesia TTS model — one of `sonic-2`, `sonic-3`, or `sonic-3.5`. On a Cartesia voice, must be `sonic-3` or `sonic-3.5` to use custom `pronunciations` (Inworld supports them on any model; MiniMax supports them on English agents only), **and for any custom (cloned) `voice_id`**. |
  | `language` | string | Locale passed to Cartesia TTS (e.g. `es`). When set, must match the language registered for the selected `voice_id`.                                                                                                                                                                   |

  <Warning>
    A **custom (cloned)** `voice_id` requires `model` `sonic-3` or `sonic-3.5`; set `voiceSettings.model` to one of these or the request is rejected with `400`.
  </Warning>

  Most English Cartesia voices do not accept `model` or `language` overrides. Non-English voices (e.g. Spanish) require both:

  ```json theme={null}
  {
    "voice_id": "d46e87a1-7c6d-4b18-9359-926f4a35ffdf",
    "model": "sonic-3",
    "language": "es",
    "speed": 1.0
  }
  ```

  **For MiniMax:**

  ```json theme={null}
  {"voice_id": "English_radiant_girl", "model": "speech-2.6-turbo", "speed": 1.0}
  ```

  **Advanced Voice Timing** (optional, applies to all TTS models):

  | Property                   | Type   | Default            | Description                                                                                       |
  | -------------------------- | ------ | ------------------ | ------------------------------------------------------------------------------------------------- |
  | `min_endpointing_delay`    | number | `0.1`              | Seconds to wait after the caller stops speaking before the agent responds.                        |
  | `max_endpointing_delay`    | number | `2.0`              | Maximum seconds to wait when the caller pauses mid-sentence.                                      |
  | `vad_min_silence_duration` | number | *(system default)* | Minimum silence duration in seconds for Voice Activity Detection. Omit to use the system default. |

  **Example with advanced timing:**

  ```json theme={null}
  {
    "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
  }
  ```
</ParamField>

### Call Features

<ParamField body="transferEnabled" type="boolean">
  Whether SIP call transfer is enabled. Supports:

  * SIP extensions: `sip:extension@hostname`
  * US phone numbers: Automatically formatted to `tel:+1XXXXXXXXXX`
</ParamField>

<ParamField body="emailToolEnabled" type="boolean">
  Whether the agent can send emails during or after calls
</ParamField>

<ParamField body="emailToolInstructions" type="string">
  Instructions for the agent on how and when to use the email tool
</ParamField>

<ParamField body="callDetection" type="boolean">
  Whether to enable answering machine / voicemail detection
</ParamField>

<ParamField body="echoDetection" type="boolean">
  Whether to enable echo detection during calls
</ParamField>

<ParamField body="enableRecording" type="boolean">
  Enable call recording. Recordings are saved to cloud storage and accessible via the Call Logs API.
</ParamField>

<ParamField body="sendRecordingLinkWithSummary" type="boolean">
  Include a link to the call recording in the post-call summary notification. This is a sub-option of call recording — it requires recording to be enabled (`enableRecording: true` in this request, or already enabled on the agent). Sending `true` while recording is not 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.
</ParamField>

<ParamField body="transferRules" type="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."`
</ParamField>

<ParamField body="email" type="array">
  Array of email addresses used for notifications and the email tool.

  Example: `["support@company.com", "admin@company.com"]`
</ParamField>

<ParamField body="emailServerId" type="string | null">
  UUID of the email server to use for this agent. Must belong to the same account. Pass `null` to revert to the account's default email server.

  Use the [List Email Servers](/api-reference/email-servers/list) endpoint to retrieve available IDs.
</ParamField>

### Localization

<ParamField body="language" type="string">
  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)
</ParamField>

<ParamField body="timezone" type="string">
  Agent timezone in IANA format. Used for date/time operations and calendar scheduling.

  Example: `"America/New_York"`, `"Europe/London"`, `"Asia/Tokyo"`
</ParamField>

### Knowledge Base

<ParamField body="knowledgebaseLinks" type="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.

  Example: `["https://example.com/faq", "https://example.com/pricing"]`
</ParamField>

### RAG Knowledge Base

<ParamField body="ragEnabled" type="boolean">
  Enable live vector search during calls. The account and the agent's organization must both have Knowledge Base (RAG) enabled, otherwise the API returns `403 forbidden`.
</ParamField>

<ParamField body="knowledgeBaseId" type="string | null">
  Organization-level knowledge base ID for the agent to search. The knowledge base must belong to the same organization as the agent. Set to `null` to remove the assignment.

  Use [List Knowledge Bases](/api-reference/knowledge-bases/list-create) to retrieve available IDs.
</ParamField>

<ParamField body="ragConfig" type="object">
  RAG retrieval configuration. Unknown keys are rejected.

  | Property             | Type    | Description                                                                            |
  | -------------------- | ------- | -------------------------------------------------------------------------------------- |
  | `top_k`              | number  | Number of chunks to retrieve. Integer, 1–50.                                           |
  | `rewrite_query`      | boolean | Rewrite the caller's utterance into a cleaner search query before retrieval.           |
  | `max_context_tokens` | number  | Maximum context tokens to pass into the agent. Integer, 1–200000.                      |
  | `fallback_message`   | string  | Optional fallback guidance when retrieval has no useful result. Up to 2000 characters. |
</ParamField>

<RequestExample>
  ```bash Enable RAG theme={null}
  curl -X PUT https://api.telzino.com/v1/agents/123e4567-e89b-12d3-a456-426614174000 \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "ragEnabled": true,
      "knowledgeBaseId": "11111111-1111-1111-1111-111111111111",
      "ragConfig": {
        "rewrite_query": true,
        "top_k": 5
      }
    }'
  ```
</RequestExample>

### Business (Google Place)

<ParamField body="googlePlaceId" type="string | null">
  Google Place ID linking the agent to a [Google Business Profile listing](/integrations/google-business). The platform fetches the listing's details (hours, location, phone, website, services, ratings) and the agent uses them as verified facts when answering callers.

  * Set to a Place ID to link (or relink) the agent's business.
  * Set to `null` or `""` to unlink.
  * **Changing or clearing the id also clears the cached listing data** (`google_place_data`, `google_place_context`, `google_place_fetched_at`), so details from the previously linked business are never served while the new listing is fetched. Re-sending the already-stored id is a no-op and keeps the cache.

  Example: `"ChIJVS6XxJPFU4gRoE8c8ql2lCQ"`
</ParamField>

### Warm Transfer

<ParamField body="warmTransferEnabled" type="boolean">
  Enable warm transfer routing for this agent.
</ParamField>

<ParamField body="warmTransferRules" type="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."`
</ParamField>

<ParamField body="warmTransferMohUrl" type="string">
  Public URL of the hold-music file played during warm transfer. The easiest way to set this is [Upload Hold Music](/api-reference/agents/upload-moh), which saves the URL for you. Leave empty for built-in hold music.
</ParamField>

<ParamField body="warmTransferPassThroughCallerNumber" type="boolean">
  Whether the original caller's number is passed to the transfer destination. Defaults to `true`.
</ParamField>

### Pronunciations

<ParamField body="pronunciations" type="array">
  Custom pronunciation rules for specific words and names in IPA format. Replaces the entire pronunciations list for the agent.

  <Note>
    Custom pronunciations are supported on **Inworld** voices, **Cartesia** voices using the **`sonic-3`** or **`sonic-3.5`** model, and **MiniMax** voices on **English** agents only. They are not supported on Cartesia `sonic-2`, nor on MiniMax when the effective `language` is `es-US` or `fr-FR` — MiniMax accepts pronunciation phonemes for English only and mis-renders Spanish and French. A non-empty `pronunciations` array is rejected with `400` unless the agent's effective voice is `ttsModel: minimax`, `ttsModel: inworld`, or `ttsModel: cartesia` with `voiceSettings.model: sonic-3` or `sonic-3.5` (taking either the values in this request or the agent's existing settings). 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.
  </Note>

  Each entry is an object with:

  * `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 IPA `phonemes`; not used directly by the agent.
  * `phonemes` — The IPA phoneme string
  * `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's `language` is used as the fallback.

  Pass an empty array `[]` to clear all pronunciation rules.

  **Example:**

  ```json theme={null}
  [
    { "word": "Telzino", "phonemes": "<<t|ɛ|l|ˈz|iː|n|oʊ>>" },
    { "word": "Jacobsen", "phonetic": "jay-kob-sen", "phonemes": "<<ˈ|dʒ|eɪ|k|ə|b|s|ə|n>>" }
  ]
  ```
</ParamField>

### Advanced Configuration

<ParamField body="config" type="object">
  General configuration object.

  **Common properties:**

  | Property                   | Type    | Description                                                                                                                                                                                                                       |
  | -------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `enable_recording`         | boolean | Enable call recording                                                                                                                                                                                                             |
  | `user_timezone`            | string  | IANA timezone (e.g., `America/New_York`)                                                                                                                                                                                          |
  | `inactivity_timeout`       | number  | Seconds before inactivity prompt                                                                                                                                                                                                  |
  | `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. |
  | `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."`).                  |
</ParamField>

<ParamField body="extensions" type="object">
  Extensions configuration for webhooks and MCP servers.

  **Webhook structure:**

  ```json theme={null}
  {
    "tools": [
      {
        "type": "webhook",
        "name": "Log_Call_To_CRM",
        "friendlyName": "Log to CRM",
        "description": "Logs call details and transcript to the CRM after the call ends",
        "condition": "after_call",
        "method": "POST",
        "url": "https://api.yourcrm.com/calls/log",
        "headers": [
          { "key": "Authorization", "value": "Bearer YOUR_API_TOKEN" }
        ],
        "parameters": [
          { "key": "caller_phone", "value": "" },
          { "key": "call_summary", "value": "" },
          { "key": "transcript", "value": "" },
          { "key": "sentiment", "value": "" }
        ]
      }
    ],
    "mcp_servers": [
      {
        "url": "https://mcp.acme.com/mcp",
        "name": "Acme CRM MCP"
      }
    ]
  }
  ```

  **MCP server objects** (`mcp_servers`):

  Same **request** validation as [Create Agent — MCP request body](/api-reference/agents/create#mcp-servers-request-body): `name` and `url` are both required on each entry; `transport_type`, `auth_headers` (array of `{key, value}`), and `initialization_options` (object) are optional. The platform transforms each entry on write (`name` → `connection_name`, `url` → `mcp_endpoint_url`) so the stored shape differs from the request shape — see [Get Agent — Stored MCP Connection Shape](/api-reference/agents/get#stored-mcp-connection-shape).

  **Webhook conditions:**

  * `before_call` - Execute before the agent answers
  * `during_call` - Execute while call is active
  * `after_call` - Execute after call ends

  See [Webhooks documentation](/functions/webhooks) for complete details and examples.
</ParamField>

### Integrations

<ParamField body="integrations" type="object">
  Third-party integration configurations. Supported provider keys: `modmed`, `googleCalendar`, `outlookCalendar`, `twilio`, `netsapiens`. Unknown provider keys are stored as-is.

  **Update semantics** — unlike `extensions` (which is a full replace), `integrations` uses a **field-level merge per provider**:

  * Only the fields you send change; omitted fields and omitted providers are preserved.
  * Set a field to `null` to clear that field.
  * Set a whole provider to `null` to remove the provider.

  This example updates only Twilio's `fromNumber`, clears its `testToNumber`, and removes the ModMed integration — everything else is untouched:

  ```json theme={null}
  {
    "integrations": {
      "twilio": { "fromNumber": "+15551234567", "testToNumber": null },
      "modmed": null
    }
  }
  ```

  **Provider fields** (required when creating a new provider entry; partial updates only need to leave the stored object valid):

  | Provider          | Required fields                                                                                        | Optional fields                                                                        |
  | ----------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
  | `twilio`          | `accountSid`, `authToken`, `fromNumber`                                                                | `testToNumber`, `connected_at`                                                         |
  | `modmed`          | `firm_url_prefix`, `username`, `password`, `environment` (`sandbox`, `practice_sandbox`, `production`) | `tokens`, `connected_at`                                                               |
  | `googleCalendar`  | `access_token`, `expires_at`                                                                           | `refresh_token`, `email`, `selected_calendar_id`, `selected_calendar_name`, `timezone` |
  | `outlookCalendar` | `access_token`, `expires_at`                                                                           | `refresh_token`, `email`, `selected_calendar_id`, `selected_calendar_name`, `timezone` |
  | `netsapiens`      | `url`, `username`, `password`, `domain`, `client_id`, `client_secret`, `extension`, `access_token`     | `api_version` (`v1`, `v2`), `refresh_token`, `user_scope`, `connected_at`              |

  **Behavior notes:**

  * Configs are stored as-is — credentials are **not** validated against the provider. ModMed/NetSapiens entries written without valid tokens will not function at runtime until tokens are supplied or the integration is connected via the Dashboard.
  * Unlike the Dashboard flow, writing `twilio` here does **not** auto-create the `send_sms_via_twilio` tool in `extensions` — add the tool yourself if you want the agent to send SMS.
  * Secret fields (passwords, `authToken`, `client_secret`, access/refresh tokens) are **write-only**: they are accepted on PUT but never returned by any agent endpoint. The merge semantics make this safe — a GET → modify → PUT round-trip won't wipe stored secrets.
</ParamField>

<Note>
  **MCP** can be set in the Dashboard or via `extensions.mcp_servers` here (see [Create Agent — MCP request body](/api-reference/agents/create#mcp-servers-request-body)); persisted shape on read is documented under [Get Agent](/api-reference/agents/get#stored-mcp-connection-shape).
</Note>

<Note>
  **MCP servers are auto-tested on save.** Each entry in `extensions.mcp_servers` is tested when you update the agent: reachable servers are stored with a `fingerprint` and discovered `tools` (green **Connected** badge in the Portal); unreachable servers are still stored but without a `fingerprint` (yellow **Not tested** badge), and the response includes a non-fatal `warnings` array (`[{ "connection_name": "...", "message": "..." }]`). Re-testing on update means an MCP server you keep in the payload retains its verified status instead of being reset.

  To validate a configuration before assigning it, use [Test MCP Connection](/api-reference/mcp/test-connection).
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl -X PUT https://api.telzino.com/v1/agents/123e4567-e89b-12d3-a456-426614174000 \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Updated Agent Name",
      "systemPrompt": "Updated system prompt with new instructions...",
      "status": "active",
      "ttsModel": "cartesia",
      "voiceSettings": {"voice_id": "f786b574-daa5-4673-aa0c-cbe3e8534c02", "speed": 1.1},
      "transferEnabled": true,
      "transferRules": "Transfer to +15559876543 for technical issues",
      "language": "multi",
      "timezone": "Europe/London",
      "knowledgebaseLinks": ["https://example.com/updated-faq"]
    }'
  ```

  ```javascript JavaScript theme={null}
  const agentId = '123e4567-e89b-12d3-a456-426614174000';

  const response = await fetch(`https://api.telzino.com/v1/agents/${agentId}`, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Updated Agent Name',
      systemPrompt: 'Updated system prompt...',
      status: 'active',
      transferEnabled: true,
      language: 'multi',
      timezone: 'Europe/London',
      knowledgebaseLinks: ['https://example.com/updated-faq']
    })
  });

  const { data } = await response.json();
  ```

  ```python Python theme={null}
  import requests

  agent_id = '123e4567-e89b-12d3-a456-426614174000'

  response = requests.put(
      f'https://api.telzino.com/v1/agents/{agent_id}',
      headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
      json={
          'name': 'Updated Agent Name',
          'systemPrompt': 'Updated system prompt...',
          'status': 'active',
          'transferEnabled': True,
          'language': 'multi',
          'timezone': 'Europe/London',
          'knowledgebaseLinks': ['https://example.com/updated-faq']
      }
  )

  agent = response.json()['data']
  ```

  ```bash With extensions (webhook + MCP) theme={null}
  curl -X PUT https://api.telzino.com/v1/agents/123e4567-e89b-12d3-a456-426614174000 \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "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" }
            ]
          }
        ]
      }
    }'
  ```
</RequestExample>

<Note>
  **Secrets are never returned.** Responses from this and every other agent endpoint omit secret fields: integration passwords, `twilio.authToken`, `netsapiens.client_secret`, calendar access/refresh tokens, and the SIP password inside `sip_registration_data`. Non-secret metadata (provider keys, usernames, `accountSid`, `connected_at`, calendar names) is still returned so you can see what's configured.
</Note>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "organization_id": "987fcdeb-51a2-3bc4-d567-890123456789",
      "name": "Updated Agent Name",
      "description": "Handles customer inquiries",
      "greeting_message": "Hello! How can I help you today?",
      "system_prompt": "Updated system prompt...",
      "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.1},
      "config": {},
      "extensions": {},
      "integrations": {},
      "transfer_enabled": true,
      "transfer_rules": "Transfer to +15559876543 for technical issues",
      "language": "multi",
      "timezone": "Europe/London",
      "enable_recording": false,
      "echo_detection": false,
      "call_detection": false,
      "knowledgebase_links": ["https://example.com/updated-faq"],
      "email": null,
      "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-16T14:20:00Z"
    }
  }
  ```

  ```json 400 theme={null}
  {
    "error": "Invalid request data",
    "details": [
      {
        "code": "invalid_enum_value",
        "message": "Invalid enum value. Expected 'simple' | 'advanced' | 'custom' | 'appointment_scheduler' | 'receptionist' | 'message_taker', received 'invalid'",
        "path": ["agentType"]
      }
    ]
  }
  ```

  ```json 403 theme={null}
  {
    "error": "forbidden",
    "error_description": "Access denied"
  }
  ```

  ```json 404 theme={null}
  {
    "error": "not_found",
    "error_description": "Agent not found or access denied"
  }
  ```
</ResponseExample>

## Update Behavior

<Info>
  * Only provided fields are updated; omitted fields retain their current values
  * The `updated_at` timestamp is automatically set to the current time
  * The `provider` field cannot be changed after creation
</Info>

## Feature Administration gating

When [Feature Administration](/admin-guide/dashboard/feature-administration) is enabled, updates are validated against the feature gates of the agent's organization — including locks inherited from its reseller or partner. The API returns `403 forbidden` when the request:

* configures an integration under `integrations` (e.g. `googleCalendar`, `twilio`) that is **locked** for the organization — clearing one (setting it to `null`) is always allowed;
* sets `ragEnabled: true` while **Knowledge Base (RAG)** is locked;
* sets `warmTransferEnabled: true` while **Warm Transfer** is locked;
* newly selects a cloned voice in `voiceSettings.voice_id` while **Custom Voices** is locked — an agent already using a clone keeps it, and you can still switch away from it;
* sets a **different** `googlePlaceId` while **Business Listing (Google Places)** is locked. Re-sending the agent's current Place ID is not blocked, so reading an agent and writing the whole object back keeps working for a locked organization.
* **adds** the `ns_send_sms` or `ns_check_sms_replies` tool to `extensions.tools` while **SMS Messaging** is locked. Only the addition is gated: re-sending tools the agent already has is not blocked, and removing an SMS tool always succeeds — a lock can never strand an SMS tool on an agent.

  <Warning>
    Re-sending an agent's existing tools is not blocked by the lock, but do not build that request by echoing a `GET` response back verbatim. `tools[].type` accepts only the values in its enum (`webhook`, `tool`, `notification`, `function`, `transfer_agent`, `end_of_call_action`), which exclude the internal type a `GET` returns for UCaaS tools such as these. Such a request is rejected with `400` by schema validation before any gate runs — a pre-existing limitation of this endpoint, unrelated to Feature Administration. Send only the fields you intend to change.
  </Warning>

Only a genuine change is gated. Clearing a value is never blocked — sending `googlePlaceId: null`, `ragEnabled: false`, `warmTransferEnabled: false` or `integrations.<name>: null` always succeeds — so a lock can never leave an agent with configuration you cannot remove, nor stop you editing an agent's other fields.

The `/v1/knowledge-bases` endpoints apply the same gate and return `403 forbidden` when Knowledge Base is locked for the organization. `POST /v1/custom-voices` returns `403` when Custom Voices is locked, and [`GET /v1/places/search`](/api-reference/places/search) returns `403` when Business Listing is locked for the account.
