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

# Update an agent

> Engine agents accept `name`, `status` (`active` or `paused`) and any
configuration block. Sending a configuration block creates a new
version; pass `expected_version` (the `current_version` you last
read) to detect concurrent edits, otherwise the current head is
assumed. Blocks you do not send are carried over from the head.

ElevenLabs agents accept only `name`; any other key answers
`409 unsupported_runtime`. Unknown keys are rejected with
`400 invalid_body`.




## OpenAPI

````yaml /openapi.yaml patch /v1/agents/{id}
openapi: 3.1.0
info:
  title: Ryvo API
  version: 1.0.0
  description: >
    The Ryvo public API lets you drive your AI voice and chat agents from your

    own systems: trigger calls, read conversations and transcripts, manage

    agents, knowledge base documents, phone numbers, outbound campaigns and

    webhooks, and read billing, team and log data. It is the same platform the

    portal uses, exposed over plain HTTPS and JSON.


    **Base URL**: `https://api.ryvo.so`


    ## Authentication


    Every request (except `GET /v1/models`) carries an API key as a Bearer

    token:


    ```

    Authorization: Bearer ryvo_live_...

    ```


    Keys are created in the portal under **Settings > API keys**

    (`https://app.ryvo.so/developers`). The full token is shown once at

    creation time. The API is available from the Starter plan up.


    ## Scopes


    Each key carries a list of scopes in the form `resource:action`, chosen

    when the key is created. An endpoint requires exactly one scope (listed

    on each operation as `x-scope`). A key without it receives

    `403 insufficient_scope`, and the message names the missing scope.


    | Scope | Opens |

    |---|---|

    | `agents:read`, `agents:write` | `/v1/agents` |

    | `calls:read`, `calls:write` | `/v1/calls` |

    | `conversations:read` | `/v1/conversations` |

    | `knowledge:read`, `knowledge:write` | `/v1/knowledge` |

    | `phone_numbers:read`, `phone_numbers:write` | `/v1/phone-numbers` |

    | `campaigns:read`, `campaigns:write` | `/v1/batch-calls` |

    | `webhooks:read`, `webhooks:write` | `/v1/webhooks` |

    | `integrations:read` | `/v1/integrations` |

    | `team:read` | `/v1/team` |

    | `billing:read` | `/v1/billing` |

    | `logs:read` | `/v1/logs` |


    `GET /v1/me` only needs a valid key and reports the scopes it carries.

    Integrations, team and billing are read-only by design: connecting an

    app is a browser OAuth flow, inviting people is persistent access, and

    buying credits from a loop is the failure mode nobody wants.


    ## Pagination


    Every list takes `?limit=` (1 to 100, default 20) and `?cursor=`, and

    answers `{ "data": [...], "has_more": true|false, "next_cursor": "..."|null
    }`.

    The cursor is opaque: pass `next_cursor` back as `?cursor=` to fetch the

    following page, and stop when `has_more` is `false`.


    ## Idempotency


    `POST /v1/calls` accepts an `Idempotency-Key` header (1 to 255 printable

    ASCII characters). Retrying the same key with the same body returns the

    original response for 24 hours without dialing again; the same key with a

    different body returns `409 idempotency_conflict`.


    ## Rate limits


    Two limits apply, in this order, on fixed one-minute windows:


    1. **Per IP address**: 300 requests/min across every `/v1` endpoint,
       except `POST /v1/calls`, which has its own bucket of 10 requests/min
       per IP because it dials a real phone.
    2. **Per account, by plan**: shared by every key of the account
       (Starter 10, Pro 40, Scale and Enterprise 120 requests/min).

    A `429 rate_limit_exceeded` response carries a `Retry-After` header with

    the seconds to wait (always at least 1).


    ## Errors and request ids


    Every error uses the same envelope: `{ "error", "message", "request_id" }`.

    Branch on `error`, which is a stable code; `message` is for humans and

    may change. The stable codes are `unauthorized`, `forbidden`,

    `insufficient_scope`, `rate_limit_exceeded`, `invalid_json`,

    `invalid_body`, `invalid_query`, `not_found`, `conflict`,

    `unsupported_runtime`, `plan_limit`, `payment_required`,

    `upstream_error` and `internal_error`. `POST /v1/calls` keeps a few older

    codes of its own (`agent_not_found`, `agent_disabled`,

    `idempotency_conflict`, `invalid_idempotency_key`, `key_budget_exhausted`,

    `upstream_unreachable`, `service_unavailable`).


    Every response, success or error, carries an `X-Request-Id` header. Quote

    it when you report a problem.


    A `404 not_found` is returned both when a resource does not exist and

    when it belongs to another account, with the same message: the API does

    not confirm which ids exist.
  contact:
    name: Ryvo support
    email: team@ryvo.so
    url: https://ryvo.so
servers:
  - url: https://api.ryvo.so
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Account
    description: Who the key belongs to and what it can do.
  - name: Agents
    description: >-
      Your agents on both runtimes. Engine agents are fully manageable
      (configuration, versions, publish). ElevenLabs agents are listed and can
      be renamed; their configuration lives in the portal.
  - name: Calls
    description: >-
      Voice conversations (phone and web voice) on both runtimes, and the
      endpoint to trigger an outbound call.
  - name: Conversations
    description: >-
      Every conversation on every channel (voice, chat, WhatsApp), with the same
      shape as calls plus the message count.
  - name: Knowledge base
    description: >-
      Documents and folders that Engine agents consult during a conversation.
      Requires the Engine module on your plan.
  - name: Phone numbers
    description: Numbers imported from your Twilio account and their agent assignment.
  - name: Batch calls
    description: >-
      Outbound campaigns on the ElevenLabs runtime. Uses the `campaigns:*`
      scopes.
  - name: Webhooks
    description: Outbound endpoints, their deliveries and the event catalog.
  - name: Integrations
    description: >-
      The integration catalog with the connection status of your account.
      Read-only.
  - name: Team
    description: Members, invitations and seats. Read-only.
  - name: Billing
    description: Plan, credits, wallet ledger, invoices and daily usage. Read-only.
  - name: Logs
    description: Requests made to this API and the account activity log.
  - name: Models
    description: The public model catalog with credits and USD per plan. No authentication.
paths:
  /v1/agents/{id}:
    parameters:
      - $ref: '#/components/parameters/agentId'
    patch:
      tags:
        - Agents
      summary: Update an agent
      description: |
        Engine agents accept `name`, `status` (`active` or `paused`) and any
        configuration block. Sending a configuration block creates a new
        version; pass `expected_version` (the `current_version` you last
        read) to detect concurrent edits, otherwise the current head is
        assumed. Blocks you do not send are carried over from the head.

        ElevenLabs agents accept only `name`; any other key answers
        `409 unsupported_runtime`. Unknown keys are rejected with
        `400 invalid_body`.
      operationId: updateAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentRequest'
      responses:
        '200':
          description: The agent after the update.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDetail'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            `unsupported_runtime` when changing anything but `name` on an
            ElevenLabs agent; `conflict` when the agent is archived or was
            modified after `expected_version`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    agentId:
      name: id
      in: path
      required: true
      description: The Ryvo uuid of the agent.
      schema:
        type: string
        format: uuid
        example: 4d0f3c2a-6b7e-4a1c-9f2d-1e2b3c4d5e6f
  schemas:
    UpdateAgentRequest:
      type: object
      description: >-
        Every field is optional; send at least one besides `expected_version`.
        Unknown keys are rejected.
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 2
          maxLength: 80
        status:
          type: string
          enum:
            - active
            - paused
        expected_version:
          type: integer
          minimum: 0
          description: The `current_version` you last read. Defaults to the current head.
        system_prompt:
          type: string
          minLength: 1
          maxLength: 40000
        first_message:
          type: string
          maxLength: 2000
        language:
          type: string
          maxLength: 35
        llm:
          $ref: '#/components/schemas/LlmConfig'
        stt:
          $ref: '#/components/schemas/SttConfig'
        voice:
          $ref: '#/components/schemas/VoiceConfig'
        knowledge_base:
          $ref: '#/components/schemas/KnowledgeBaseConfig'
        dynamic_variables:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/DynamicVariable'
        conversation:
          $ref: '#/components/schemas/ConversationConfig'
        tool_ids:
          type: array
          maxItems: 50
          items:
            type: string
            format: uuid
      example:
        name: Sales assistant
        status: active
        expected_version: 4
        system_prompt: You are the receptionist of Acme Dental.
        llm:
          provider: openai
          model: gpt-4o-mini
          temperature: 0.5
    AgentDetail:
      allOf:
        - $ref: '#/components/schemas/Agent'
        - type: object
          required:
            - config
          properties:
            config:
              description: >-
                The head definition (`engine`), the stored summary
                (`elevenlabs`), or `null` when nothing is saved yet.
              oneOf:
                - $ref: '#/components/schemas/EngineConfig'
                - $ref: '#/components/schemas/LegacyConfig'
                - type: 'null'
    Error:
      type: object
      required:
        - error
        - message
        - request_id
      properties:
        error:
          type: string
          description: A stable error code. Branch on this, not on `message`.
          example: unauthorized
        message:
          type: string
          description: A human-readable description. May change between versions.
          example: Invalid or revoked API key.
        request_id:
          type: string
          format: uuid
          description: The request id. Quote it when you report a problem.
          example: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
    LlmConfig:
      type: object
      description: >-
        Which model answers. `provider` and `model` are validated against the
        catalog and your plan when saving (see `GET /v1/models`).
      properties:
        provider:
          type: string
          maxLength: 60
          example: openai
        model:
          type: string
          maxLength: 120
          example: gpt-4o-mini
        temperature:
          type: number
          minimum: 0
          maximum: 2
          default: 0.7
        routing:
          type: string
          enum:
            - auto
            - gateway
            - direct
            - custom
          default: auto
        fallback:
          type: array
          maxItems: 5
          items:
            type: object
            required:
              - provider
              - model
            properties:
              provider:
                type: string
              model:
                type: string
        endpoint:
          type: string
          format: uri
          description: 'Only with `routing: custom`.'
      additionalProperties: true
    SttConfig:
      type: object
      description: >-
        Speech recognition. `settings` and `sample_rate` are provider-specific
        and validated when saving.
      properties:
        provider:
          type: string
          example: deepgram
        model:
          type: string
          example: nova-3
        language:
          type: string
          maxLength: 35
        settings:
          type: object
          additionalProperties: true
        sample_rate:
          type: integer
      additionalProperties: true
    VoiceConfig:
      type: object
      description: Text to speech. A `voice_id` is required to publish a voice agent.
      properties:
        provider:
          type: string
          example: elevenlabs
        model:
          type: string
          example: eleven_flash_v2_5
        voice_id:
          type: string
          maxLength: 120
        fallback_voices:
          type: object
          description: Backup voice per `provider:model` or per `provider`.
          additionalProperties:
            type: string
        language:
          type: string
          maxLength: 35
        settings:
          type: object
          additionalProperties: true
        sample_rate:
          type: integer
      additionalProperties: true
    KnowledgeBaseConfig:
      type: object
      description: >-
        Which documents and folders the agent consults. Ownership is verified
        when saving.
      properties:
        doc_ids:
          type: array
          maxItems: 200
          items:
            type: string
            format: uuid
        folder_ids:
          type: array
          maxItems: 50
          items:
            type: string
            format: uuid
        mode:
          type: string
          enum:
            - 'off'
            - auto
            - always
        top_k:
          type: integer
          minimum: 1
          maximum: 4
          description: Chunks per lookup. Absent means the plan maximum.
      additionalProperties: true
    DynamicVariable:
      type: object
      required:
        - default
      properties:
        default:
          type: string
          maxLength: 500
    ConversationConfig:
      type: object
      description: >-
        Session limits, recording, turn policy, audio, pronunciation and calling
        hours. Unknown keys are kept.
      properties:
        max_duration_s:
          type: integer
          minimum: 1
          maximum: 86400
        recording:
          type: object
          properties:
            enabled:
              type: boolean
            consent_text:
              type: string
              maxLength: 1000
              description: Required when `enabled` is true.
          additionalProperties: true
        turns:
          type: object
          additionalProperties: true
        audio:
          type: object
          additionalProperties: true
        pronunciation:
          type: array
          maxItems: 50
          items:
            type: object
            properties:
              term:
                type: string
              say:
                type: string
              match_case:
                type: boolean
        calling_hours:
          type: object
          properties:
            timezone:
              type: string
            days:
              type: array
              items:
                type: integer
            start:
              type: string
            end:
              type: string
          additionalProperties: true
      additionalProperties: true
    Agent:
      type: object
      required:
        - id
        - runtime
        - name
        - status
        - language
        - created_at
        - updated_at
        - phone_numbers
      properties:
        id:
          type: string
          format: uuid
          example: 4d0f3c2a-6b7e-4a1c-9f2d-1e2b3c4d5e6f
        runtime:
          $ref: '#/components/schemas/Runtime'
        name:
          type: string
          example: Sales assistant
        status:
          $ref: '#/components/schemas/AgentStatus'
        language:
          type:
            - string
            - 'null'
          example: es-MX
        created_at:
          type: string
          format: date-time
          example: '2026-09-01T15:04:05.000Z'
        updated_at:
          type: string
          format: date-time
          example: '2026-09-20T10:11:12.000Z'
        provider_agent_id:
          type:
            - string
            - 'null'
          description: >-
            Only on `elevenlabs` agents. The agent id at the provider
            (`agent_...`).
          example: agent_8801kpabc123
        current_version:
          type: integer
          description: Only on `engine` agents. The latest saved version.
          example: 4
        published_version:
          type: integer
          description: >-
            Only on `engine` agents. The version serving traffic; `0` when never
            published.
          example: 3
        phone_numbers:
          type: array
          description: The numbers assigned to the agent. Empty for Engine agents today.
          items:
            $ref: '#/components/schemas/AgentPhoneNumber'
    EngineConfig:
      allOf:
        - type: object
          required:
            - version
          properties:
            version:
              type: integer
              example: 4
        - $ref: '#/components/schemas/DefinitionFields'
    LegacyConfig:
      type: object
      description: The summary Ryvo keeps of an ElevenLabs agent. Edited from the portal.
      required:
        - system_prompt
        - first_message
        - language
        - type
        - voice_id
        - llm_model
      properties:
        system_prompt:
          type:
            - string
            - 'null'
        first_message:
          type:
            - string
            - 'null'
        language:
          type:
            - string
            - 'null'
        type:
          type:
            - string
            - 'null'
        voice_id:
          type:
            - string
            - 'null'
        llm_model:
          type:
            - string
            - 'null'
    Runtime:
      type: string
      description: '`engine` is the Ryvo runtime; `elevenlabs` is the legacy voice runtime.'
      enum:
        - engine
        - elevenlabs
      example: engine
    AgentStatus:
      type: string
      enum:
        - draft
        - active
        - paused
        - archived
        - error
      example: active
    AgentPhoneNumber:
      type: object
      required:
        - id
        - phone_number
        - label
      properties:
        id:
          type: string
          format: uuid
        phone_number:
          type: string
          example: '+5215555550100'
        label:
          type:
            - string
            - 'null'
          example: Main line
    DefinitionFields:
      type: object
      required:
        - system_prompt
        - first_message
        - language
        - llm
        - stt
        - voice
        - knowledge_base
        - dynamic_variables
        - conversation
        - tool_ids
      properties:
        system_prompt:
          type: string
          example: >-
            You are the receptionist of Acme Dental. Book appointments and
            answer questions about opening hours.
        first_message:
          type: string
          example: Hi, this is Acme Dental. How can I help you?
        language:
          type:
            - string
            - 'null'
          example: es-MX
        llm:
          $ref: '#/components/schemas/LlmConfig'
        stt:
          $ref: '#/components/schemas/SttConfig'
        voice:
          $ref: '#/components/schemas/VoiceConfig'
        knowledge_base:
          $ref: '#/components/schemas/KnowledgeBaseConfig'
        dynamic_variables:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/DynamicVariable'
          example:
            lead_name:
              default: there
        conversation:
          $ref: '#/components/schemas/ConversationConfig'
        tool_ids:
          type: array
          items:
            type: string
            format: uuid
  responses:
    ValidationError:
      description: >-
        The body is not JSON (`invalid_json`), a body field is invalid
        (`invalid_body`), or a query parameter is invalid (`invalid_query`). The
        message names the first offending field.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: invalid_query
            message: limit must be an integer between 1 and 100.
            request_id: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
    Unauthorized:
      description: >-
        Missing or malformed `Authorization` header, or an invalid, expired or
        revoked key (`unauthorized`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: unauthorized
            message: Invalid or revoked API key.
            request_id: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
    Forbidden:
      description: >-
        The key does not carry the scope this endpoint requires
        (`insufficient_scope`), or it is read-only (`forbidden`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: insufficient_scope
            message: This API key does not have the agents:write scope.
            request_id: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
    NotFound:
      description: >-
        The resource does not exist or belongs to another account (`not_found`).
        Both cases return the same message.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: not_found
            message: Resource not found.
            request_id: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
    RateLimited:
      description: >-
        Too many requests for this IP or this account (`rate_limit_exceeded`).
        Wait `Retry-After` seconds.
      headers:
        Retry-After:
          description: Seconds to wait before retrying. Always at least 1.
          schema:
            type: integer
            example: 32
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: rate_limit_exceeded
            message: Rate limit exceeded. Retry in 32s (see the Retry-After header).
            request_id: f0c0a9d2-8b1e-4d8a-9c2f-6e5b7a1d2c3b
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key (ryvo_live_...)
      description: |
        A Bearer token. Create keys in the portal under Settings > API keys
        (`https://app.ryvo.so/developers`). The token has the form
        `ryvo_live_<64 hex characters>` and is shown once.

        **Header:** `Authorization: Bearer ryvo_live_...`

````