---
title: "Save the agent's draft - never changes the running agent"
method: PUT
path: "/v1/agents/{id}/draft"
---

# Save the agent's draft - never changes the running agent

`PUT /v1/agents/{id}/draft`

## Request

- Base URL: `https://volai.cz`
- URL: `https://volai.cz/v1/agents/{id}/draft`
- Auth: HTTP bearer

Saves a draft against `expectedRevision`. Send the body as EXACTLY the object you read from `GET`, with one change applied - the body is `.strict()`, so even a small unknown key returns 400. Saving NEVER changes the running agent. The `GET .../draft` example above shows the state AFTER this PUT (revision 3 -> 4): that's why this PUT sends `expectedRevision: 3` (the revision the edit was based on), and GET above already returns `revision: 4` (the new revision after saving).

## Path parameters

- `id` string, required

## Request body

- object
  - `expectedRevision` integer, required: Current draft revision from GET /v1/agents/{id}/draft. On revision_conflict reload before retrying; never guess or increment it locally.
  - `draft` object, required: Complete replacement snapshot, not a patch. Saving never changes the live agent. Workflow shape can be saved while incomplete; publication validates graph and product limits.
    - `name` string, required: Display name of the agent, 1 to 60 characters.
    - `language` 'cs' | 'sk' | 'en' | 'de' | 'pl', required: Language the agent speaks: cs (Czech), sk (Slovak), en (English), de (German), or pl (Polish).
    - `systemPrompt` string, required: Full instructions the agent follows during the call, 10 to 6000 characters.
    - `firstMessage` string: Opening line the agent speaks first, up to 300 characters. Omit to use the default opening line.
    - `voiceId` string: Voice id from list_voices for provider engine (any catalog id except an ElevenLabs voice such as katty), or katty or a raw ElevenLabs voice id for provider elevenlabs, up to 120 characters.
    - `numberE164` string, nullable: Phone number in E.164 format. Omit the key to leave the agent's number untouched; null disconnects it; a string attaches it. A value equal to what the agent already has live counts as untouched too, re-evaluated on every read; publish reports number_changed_by_publish.
    - `transferTo` string: E.164 number the agent transfers the caller to when they ask for a human. Must not be your own volai number.
    - `transferCondition` string: When to transfer to a human, one or two sentences as an instruction to the model, up to 500 characters.
    - `dataFields` object[]: What the agent should record from the call, at most 10 fields.
      - `key` string, required: Lowercase machine name for this data field, letters, digits and underscores only, starting with a letter, up to 40 characters.
      - `type` 'string' | 'number' | 'boolean', required: What kind of value this field holds: string (free text, or one of enumValues), number, or boolean.
      - `description` string, required: What the agent should extract from the call for this field, read by the model, up to 300 characters.
      - `enumValues` string[]: Allowed answers for a string field, each up to 60 characters, at most 20 values. Only a string field may have them; on a number or boolean field publishing fails with invalid_data_fields.
    - `toolIds` string[]: Ids of webhook tools from list_tools the agent may call during a call, at most 10.
    - `provider` 'engine' | 'elevenlabs', required: engine = volai's own voice engine, elevenlabs = the standard platform; changing it is done by volai, send back the value you received from get_agent_draft.
    - `recordCalls` boolean: Whether to record calls for this agent. Defaults to true when omitted.
    - `goal` string: One sentence describing what a successful call looks like, used by an automated judge after the call, up to 300 characters.
    - `knowledge` string: Extra text the agent knows besides its instructions, such as a price list or FAQ, appended to the end of the prompt, up to 20000 characters.
    - `notifyEmail` boolean: Whether to email the account owner a summary after every call this agent takes. Defaults to true when omitted.
    - `voicemail` object: Voicemail detection for outbound calls. Only meaningful when provider is engine; publishing it on an elevenlabs agent fails with voicemail_requires_engine.
      - `enabled` boolean, required: Whether voicemail detection runs on this agent's outbound calls.
      - `action` 'mark' | 'hangup' | 'message', required: Detection action: "mark" only logs it, "hangup" ends the call, "message" reads the message field then hangs up.
      - `message` string: Message read into the voicemail when action is "message", up to 400 characters. Required when action is "message".
    - `silencePromptSecs` integer, nullable: How many seconds of caller silence trigger a reminder prompt, 3 to 25; null turns it off. Stored the same for either provider, heard only once the agent runs on the volai engine. Absent means unchanged, not disabled and not 10.
    - `laughter` 'auto' | 'on' | 'off': auto follows sensitive-topic detection, on overrides that detection, off always suppresses it. on still needs a Cartesia voice in Czech on the engine. No effect on ElevenLabs agents. Omit to leave unchanged; a new agent normally gets auto explicitly.
    - `firstMessageProtectSecs` number, nullable: Seconds of the first line protected from short reactions, 0 to 20, outbound only, engine only. A reply with no interrupting word (for example stop, no, wait; language-specific) is dropped: not in the transcript, no agent reaction. null or 0 turns it off. Absent means unchanged.
    - `silencePromptText` string, nullable: Your own silence reminder sentence, up to 80 characters, spoken instead of the default; null restores the default. Heard only on the volai engine. Absent means unchanged, not the default.
    - `inboundVariables` object, nullable: Default values for {{variable}} placeholders on inbound calls: up to 10 keys of letters, digits or underscores, values up to 200 characters; names volai supplies itself are rejected. The object replaces the stored values. Omit to leave unchanged; null removes them.
    - `sttLanguageHintsStrict` boolean, nullable: Strict language lock for speech recognition on the volai engine (Soniox language_hints_strict), best effort; for cs agents with outbound calls only, never sk. true turns it on, false or null turns it off. Omit to leave unchanged.
    - `turnDigitTailFloorMs` integer, nullable: Silence floor in ms on the volai engine for a reply ending with 3 to 8 digits (dictated numbers): 0 turns it off, otherwise 400 to 2300, recommended 800. Omit to leave unchanged; null restores the engine default (0).
    - `turnLongSpeechFloorMs` integer, nullable: Silence floor in ms on the volai engine after the caller spoke longer than turnLongSpeechSecs: 0 turns it off, otherwise 400 to 2300, suggested 800. Omit to leave unchanged; null restores the engine default (0).
    - `turnLongSpeechSecs` number, nullable: Speech length in seconds (1 to 30, decimals allowed) above which turnLongSpeechFloorMs applies; no effect while that floor is 0. Omit to leave unchanged; null restores the engine default (5).
    - `workflow` object: Splits the call into phases (nodes) with their own prompt, knowledge and tools, and model-driven transitions between them. Omit for a single-phase agent.
      - `version` 1, required: Workflow format version, currently always 1.
      - `entry` string, required: Id of the node the call starts in. Must exist among nodes.
      - `nodes` object[], required: Call phases, 2 to 8 nodes.
        - `id` string, required: Machine id of this node: lowercase letters, digits and underscores, starting with a letter, up to 32 characters. Must be unique within the workflow. The words start, start_node, end and end_node are reserved.
        - `label` string, required: Human-readable phase name, 1 to 60 characters.
        - `prompt` string: Prompt addition that only applies while the call is in this node, up to 2000 characters. The agent's base systemPrompt should only hold what is true in every phase.
        - `knowledge` string: Extra knowledge that only applies in this node, up to 4000 characters. prompt and knowledge combined must not exceed 4000 characters for one node.
        - `toolIds` string[]: Ids of account tools (from list_tools) visible only while the call is in this node, at most 10. A tool not assigned to any node stays visible in every node.
        - `enterPhrase` string: Sentence spoken immediately on entering this node, at most once per call, up to 160 characters.
        - `enterBehavior` 'reply' | 'wait': Defaults to "reply". "wait" means the model does not answer in the same turn after speaking enterPhrase and waits for the caller instead; requires enterPhrase.
      - `edges` object[], required: Transitions between phases, 1 to 24 edges, at most 5 leaving any one node.
        - `id` string, required: Machine id of this edge: lowercase letters, digits and underscores, starting with a letter, up to 48 characters. Must be unique within the workflow.
        - `from` string, required: Id of the node this edge leaves.
        - `to` string, required: Id of the node this edge enters. Must differ from from.
        - `condition` string, required: When to take this edge, written about what the CALLER said (never about what the agent or a tool did), 5 to 200 characters. Goes verbatim into the description of the switch_to_{to} tool.
    - `calendar` object, nullable: Calendar availability the agent books appointments against. Only takes effect on the volai engine (omit or connect one via /propojeni first). Omit the key to leave the agent's calendar untouched; null disconnects it; an object sets it.
      - `integrationId` string, required: Id of the connected calendar integration (kind calendar) from list_connections.
      - `calendarId` string, required: Id of the specific calendar within that connection, from find_calendars/list_calendars. The agent writes appointments into this calendar, so it must be writable (accessRole owner or writer); a read-only calendar is rejected with calendar_invalid_calendar.
      - `busyCalendarIds` string[]: Other calendars in the same connection whose events also block a slot; nothing is written into them, so read-only calendars are fine. At most 4, without duplicates and without calendarId.
      - `timezone` string, required: IANA time zone the calendar rules are interpreted in, e.g. Europe/Prague.
      - `slotMinutes` union, required: Appointment length in minutes: one of 15, 30, 45, 60, 90, 120.
        - 15
        - 30
        - 45
        - 60
        - 90
        - 120
      - `minNoticeMinutes` number, required: Minimum notice in minutes before the next bookable slot.
      - `horizonDays` number, required: How many days ahead the agent searches for a free slot.
      - `bufferMinutes` number, required: Buffer in minutes kept free before and after every busy event.
      - `windows` object, required: Availability windows per weekday (mon..sun), each HH:MM-HH:MM, at most 4 per day, empty array = no offers that day.
        - `mon` object[], required: Availability windows on Monday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `tue` object[], required: Availability windows on Tuesday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `wed` object[], required: Availability windows on Wednesday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `thu` object[], required: Availability windows on Thursday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `fri` object[], required: Availability windows on Friday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `sat` object[], required: Availability windows on Saturday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `sun` object[], required: Availability windows on Sunday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
    - `missedCallSms` object: Send an SMS to the caller when this agent misses the call. Absent means unchanged, not off; send { enabled: false } to turn it off.
      - `enabled` boolean, required: Whether the missed-call SMS is on.
      - `text` string: Custom SMS template using {firma}/{cislo}/{agent}; any other {...} variable is rejected. Omit for the default text in the agent's language.

## Response `200`

Saved draft

- object
  - `draft` object, required
    - `name` string, required: Display name of the agent, 1 to 60 characters.
    - `language` 'cs' | 'sk' | 'en' | 'de' | 'pl', required: Language the agent speaks: cs (Czech), sk (Slovak), en (English), de (German), or pl (Polish).
    - `systemPrompt` string, required: Full instructions the agent follows during the call, 10 to 6000 characters.
    - `firstMessage` string: Opening line the agent speaks first, up to 300 characters. Omit to use the default opening line.
    - `voiceId` string: Voice id from list_voices for provider engine (any catalog id except an ElevenLabs voice such as katty), or katty or a raw ElevenLabs voice id for provider elevenlabs, up to 120 characters.
    - `numberE164` string, nullable: Phone number in E.164 format. Omit the key to leave the agent's number untouched; null disconnects it; a string attaches it. A value equal to what the agent already has live counts as untouched too, re-evaluated on every read; publish reports number_changed_by_publish.
    - `transferTo` string: E.164 number the agent transfers the caller to when they ask for a human. Must not be your own volai number.
    - `transferCondition` string: When to transfer to a human, one or two sentences as an instruction to the model, up to 500 characters.
    - `dataFields` object[]: What the agent should record from the call, at most 10 fields.
      - `key` string, required: Lowercase machine name for this data field, letters, digits and underscores only, starting with a letter, up to 40 characters.
      - `type` 'string' | 'number' | 'boolean', required: What kind of value this field holds: string (free text, or one of enumValues), number, or boolean.
      - `description` string, required: What the agent should extract from the call for this field, read by the model, up to 300 characters.
      - `enumValues` string[]: Allowed answers for a string field, each up to 60 characters, at most 20 values. Only a string field may have them; on a number or boolean field publishing fails with invalid_data_fields.
    - `toolIds` string[]: Ids of webhook tools from list_tools the agent may call during a call, at most 10.
    - `provider` 'engine' | 'elevenlabs', required: engine = volai's own voice engine, elevenlabs = the standard platform; changing it is done by volai, send back the value you received from get_agent_draft.
    - `recordCalls` boolean: Whether to record calls for this agent. Defaults to true when omitted.
    - `goal` string: One sentence describing what a successful call looks like, used by an automated judge after the call, up to 300 characters.
    - `knowledge` string: Extra text the agent knows besides its instructions, such as a price list or FAQ, appended to the end of the prompt, up to 20000 characters.
    - `notifyEmail` boolean: Whether to email the account owner a summary after every call this agent takes. Defaults to true when omitted.
    - `voicemail` object: Voicemail detection for outbound calls. Only meaningful when provider is engine; publishing it on an elevenlabs agent fails with voicemail_requires_engine.
      - `enabled` boolean, required: Whether voicemail detection runs on this agent's outbound calls.
      - `action` 'mark' | 'hangup' | 'message', required: Detection action: "mark" only logs it, "hangup" ends the call, "message" reads the message field then hangs up.
      - `message` string: Message read into the voicemail when action is "message", up to 400 characters. Required when action is "message".
    - `silencePromptSecs` integer, nullable: How many seconds of caller silence trigger a reminder prompt, 3 to 25; null turns it off. Stored the same for either provider, heard only once the agent runs on the volai engine. Absent means unchanged, not disabled and not 10.
    - `laughter` 'auto' | 'on' | 'off': auto follows sensitive-topic detection, on overrides that detection, off always suppresses it. on still needs a Cartesia voice in Czech on the engine. No effect on ElevenLabs agents. Omit to leave unchanged; a new agent normally gets auto explicitly.
    - `firstMessageProtectSecs` number, nullable: Seconds of the first line protected from short reactions, 0 to 20, outbound only, engine only. A reply with no interrupting word (for example stop, no, wait; language-specific) is dropped: not in the transcript, no agent reaction. null or 0 turns it off. Absent means unchanged.
    - `silencePromptText` string, nullable: Your own silence reminder sentence, up to 80 characters, spoken instead of the default; null restores the default. Heard only on the volai engine. Absent means unchanged, not the default.
    - `inboundVariables` object, nullable: Default values for {{variable}} placeholders on inbound calls: up to 10 keys of letters, digits or underscores, values up to 200 characters; names volai supplies itself are rejected. The object replaces the stored values. Omit to leave unchanged; null removes them.
    - `sttLanguageHintsStrict` boolean, nullable: Strict language lock for speech recognition on the volai engine (Soniox language_hints_strict), best effort; for cs agents with outbound calls only, never sk. true turns it on, false or null turns it off. Omit to leave unchanged.
    - `turnDigitTailFloorMs` integer, nullable: Silence floor in ms on the volai engine for a reply ending with 3 to 8 digits (dictated numbers): 0 turns it off, otherwise 400 to 2300, recommended 800. Omit to leave unchanged; null restores the engine default (0).
    - `turnLongSpeechFloorMs` integer, nullable: Silence floor in ms on the volai engine after the caller spoke longer than turnLongSpeechSecs: 0 turns it off, otherwise 400 to 2300, suggested 800. Omit to leave unchanged; null restores the engine default (0).
    - `turnLongSpeechSecs` number, nullable: Speech length in seconds (1 to 30, decimals allowed) above which turnLongSpeechFloorMs applies; no effect while that floor is 0. Omit to leave unchanged; null restores the engine default (5).
    - `workflow` object: Splits the call into phases (nodes) with their own prompt, knowledge and tools, and model-driven transitions between them. Omit for a single-phase agent.
      - `version` 1, required: Workflow format version, currently always 1.
      - `entry` string, required: Id of the node the call starts in. Must exist among nodes.
      - `nodes` object[], required: Call phases, 2 to 8 nodes.
        - `id` string, required: Machine id of this node: lowercase letters, digits and underscores, starting with a letter, up to 32 characters. Must be unique within the workflow. The words start, start_node, end and end_node are reserved.
        - `label` string, required: Human-readable phase name, 1 to 60 characters.
        - `prompt` string: Prompt addition that only applies while the call is in this node, up to 2000 characters. The agent's base systemPrompt should only hold what is true in every phase.
        - `knowledge` string: Extra knowledge that only applies in this node, up to 4000 characters. prompt and knowledge combined must not exceed 4000 characters for one node.
        - `toolIds` string[]: Ids of account tools (from list_tools) visible only while the call is in this node, at most 10. A tool not assigned to any node stays visible in every node.
        - `enterPhrase` string: Sentence spoken immediately on entering this node, at most once per call, up to 160 characters.
        - `enterBehavior` 'reply' | 'wait': Defaults to "reply". "wait" means the model does not answer in the same turn after speaking enterPhrase and waits for the caller instead; requires enterPhrase.
      - `edges` object[], required: Transitions between phases, 1 to 24 edges, at most 5 leaving any one node.
        - `id` string, required: Machine id of this edge: lowercase letters, digits and underscores, starting with a letter, up to 48 characters. Must be unique within the workflow.
        - `from` string, required: Id of the node this edge leaves.
        - `to` string, required: Id of the node this edge enters. Must differ from from.
        - `condition` string, required: When to take this edge, written about what the CALLER said (never about what the agent or a tool did), 5 to 200 characters. Goes verbatim into the description of the switch_to_{to} tool.
    - `calendar` object, nullable: Calendar availability the agent books appointments against. Only takes effect on the volai engine (omit or connect one via /propojeni first). Omit the key to leave the agent's calendar untouched; null disconnects it; an object sets it.
      - `integrationId` string, required: Id of the connected calendar integration (kind calendar) from list_connections.
      - `calendarId` string, required: Id of the specific calendar within that connection, from find_calendars/list_calendars. The agent writes appointments into this calendar, so it must be writable (accessRole owner or writer); a read-only calendar is rejected with calendar_invalid_calendar.
      - `busyCalendarIds` string[]: Other calendars in the same connection whose events also block a slot; nothing is written into them, so read-only calendars are fine. At most 4, without duplicates and without calendarId.
      - `timezone` string, required: IANA time zone the calendar rules are interpreted in, e.g. Europe/Prague.
      - `slotMinutes` union, required: Appointment length in minutes: one of 15, 30, 45, 60, 90, 120.
        - 15
        - 30
        - 45
        - 60
        - 90
        - 120
      - `minNoticeMinutes` number, required: Minimum notice in minutes before the next bookable slot.
      - `horizonDays` number, required: How many days ahead the agent searches for a free slot.
      - `bufferMinutes` number, required: Buffer in minutes kept free before and after every busy event.
      - `windows` object, required: Availability windows per weekday (mon..sun), each HH:MM-HH:MM, at most 4 per day, empty array = no offers that day.
        - `mon` object[], required: Availability windows on Monday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `tue` object[], required: Availability windows on Tuesday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `wed` object[], required: Availability windows on Wednesday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `thu` object[], required: Availability windows on Thursday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `fri` object[], required: Availability windows on Friday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `sat` object[], required: Availability windows on Saturday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
        - `sun` object[], required: Availability windows on Sunday. Empty array means no offers that day, at most 4 windows.
          - `from` string, required: Start of the window as HH:MM, in the calendar's timezone.
          - `to` string, required: End of the window as HH:MM, must be after from.
    - `missedCallSms` object: Send an SMS to the caller when this agent misses the call (unanswered outbound, missed inbound). text is a custom template (up to 320 characters) using {firma}/{cislo}/{agent}; omit for the default text in the agent's language. Omit the key to leave unchanged.
      - `enabled` boolean, required: Whether the missed-call SMS is on.
      - `text` string: Custom SMS template using {firma}/{cislo}/{agent}; omit for the default text in the agent's language.
  - `revision` number, required
  - `warnings` object[]: Present only when the saved text (agent configuration, draft or tool description), the call variables, the task name or variables, or the trial call prompt contains wording that may come across as impersonating an authority or another institution, or as a request for sensitive information - code agent_misuse_suspected. It never blocks the request; the field is absent when there is nothing to flag.
    - `code` 'agent_misuse_suspected', required
    - `message` object, required
      - `cs` string, required
      - `en` string, required

## Other responses

- `400`: Error codes: validation (request), invalid_draft (request).
- `401`: Error codes: unauthorized (request).
- `403`: Error codes: account_suspended (account), forbidden (account).
- `404`: Error codes: agent_not_found (account).
- `409`: Error codes: revision_conflict (request), publish_in_progress (busy), operation_uncertain (service).
- `429`: Error codes: rate_limited (busy).
- `500`: Error codes: internal_error (service), storage_failed (service).

---

[API](https://skmtc.dev/volai/apis/volai-rest-api.md) · [All operations](https://skmtc.dev/volai/apis/volai-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/volai/apis/volai-rest-api/revisions/12029b05e2fa?raw)
