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

# Update and publish an agent

> Send only the fields to change. `guardrails` and `tools` can be partial too: only the keys you send change. The change is published right away as the agent’s next version (a request that changes nothing makes none). A draft someone is working on stays as it is: to save changes without changing what runs, use `PATCH /v1/agents/{agent_id}/draft`.



## OpenAPI

````yaml /api-reference/openapi.json patch /v1/agents/{agent_id}
openapi: 3.1.0
info:
  title: GuidingHand API
  version: 1.0.0
  description: >-
    Create sessions (an invite link with a code for the person at the computer),
    run tasks on their computer with one of your agents, follow them, answer
    their questions and approvals, and fetch history and recordings. Test your
    agents with evals: test cases on fresh machines, run in test sets, scored by
    a check script or a rubric. Agents are versioned (save a draft, publish it),
    have tools (their own file system, and your HTTP endpoints), and every
    change is in the org’s audit log.
servers:
  - url: https://guidinghand.ai
    description: Production
  - url: https://dev.guidinghand.ai
    description: Development (Stripe test mode)
security:
  - bearerAuth: []
tags:
  - name: Agents
  - name: Agent files
  - name: Agent tools
  - name: Sessions
  - name: Tasks
  - name: Webhooks
  - name: Audit log
  - name: Test cases
  - name: Test sets
  - name: Test runs
  - name: Start states
  - name: Runners
paths:
  /v1/agents/{agent_id}:
    parameters:
      - name: agent_id
        in: path
        required: true
        description: The agent’s id, e.g. `billing` or `default`.
        schema:
          type: string
    patch:
      tags:
        - Agents
      summary: Update and publish an agent
      description: >-
        Send only the fields to change. `guardrails` and `tools` can be partial
        too: only the keys you send change. The change is published right away
        as the agent’s next version (a request that changes nothing makes none).
        A draft someone is working on stays as it is: to save changes without
        changing what runs, use `PATCH /v1/agents/{agent_id}/draft`.
      operationId: updateAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentUpdate'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          $ref: '#/components/responses/E400'
        '401':
          $ref: '#/components/responses/E401'
        '403':
          $ref: '#/components/responses/E403'
        '404':
          $ref: '#/components/responses/E404'
components:
  schemas:
    AgentUpdate:
      type: object
      properties:
        name:
          type: string
          maxLength: 80
        instructions:
          type: string
          maxLength: 20000
        model:
          $ref: '#/components/schemas/Model'
        effort:
          $ref: '#/components/schemas/Effort'
        greeting:
          type: string
          maxLength: 500
        display_name:
          type: string
          maxLength: 40
          description: >-
            What the app calls the agent on the person’s screen, in place of
            “GuidingHand”. One line; empty goes back to “GuidingHand”.
        narration:
          type: boolean
          description: >-
            Show the agent’s thoughts and steps on the person’s screen as it
            works, and its summary when it finishes. Off: the banner with the
            Stop button only (and the agent’s questions and approval requests,
            if `customer_answers` and `customer_approvals` are on).
        customer_answers:
          type: boolean
          description: >-
            Let the person at the computer answer the agent’s questions in the
            GuidingHand app.
        customer_approvals:
          type: boolean
          description: >-
            Let the person at the computer approve or deny the agent’s approval
            requests in the GuidingHand app.
        guardrails:
          $ref: '#/components/schemas/GuardrailsInput'
        tools:
          $ref: '#/components/schemas/AgentToolsInput'
    Agent:
      type: object
      properties:
        object:
          const: agent
        agent_id:
          type: string
          example: billing
        name:
          type: string
          example: Billing help
        instructions:
          type: string
          description: Added under GuidingHand’s own rules, which always win.
        model:
          $ref: '#/components/schemas/Model'
        effort:
          $ref: '#/components/schemas/Effort'
        greeting:
          type: string
          description: Shown to the person on the invite page.
        display_name:
          type: string
          description: >-
            What the GuidingHand app calls the agent on the person’s screen
            while it works (“Acme Support is typing”). Empty means
            “GuidingHand”.
          example: Acme Support
        narration:
          type: boolean
          description: >-
            The app shows the agent’s thoughts and steps on the person’s screen
            as it works, and its summary when it finishes. These are the agent’s
            own words, so they can repeat what your team answered. Off: no
            thoughts, steps or summary; they see the banner with the Stop button
            (and the agent’s questions and approval requests, if
            `customer_answers` and `customer_approvals` are on).
        customer_answers:
          type: boolean
          description: >-
            The person at the computer can answer the agent’s questions in the
            GuidingHand app (your team still can too; the first answer is used).
            Off: only your team answers, and the question isn’t shown on the
            person’s screen.
        customer_approvals:
          type: boolean
          description: >-
            The person at the computer can approve or deny the agent’s approval
            requests in the GuidingHand app (your team still can too; the first
            decision is used). Off: only your team decides, and the request
            isn’t shown on the person’s screen.
        guardrails:
          $ref: '#/components/schemas/Guardrails'
        tools:
          $ref: '#/components/schemas/AgentTools'
        is_default:
          type: boolean
        invite_url_template:
          type: string
          example: https://guidinghand.ai/acme/billing/{code}
        version:
          type: integer
          minimum: 0
          example: 3
          description: >-
            The published version: what sessions and tasks run. 1 when the agent
            is made; each publish adds one. 0: the default agent before it was
            first published.
        has_draft:
          type: boolean
          description: It has unpublished changes (`GET /v1/agents/{agent_id}/draft`).
        created_by:
          $ref: '#/components/schemas/Actor'
          description: Who made it.
        updated_by:
          $ref: '#/components/schemas/Actor'
          description: >-
            Who last changed it: saved its draft, published it or restored a
            version.
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    Model:
      type:
        - string
        - 'null'
      enum:
        - claude-sonnet-5-5
        - claude-opus-5-5
        - claude-fable-5-1
        - gpt-6.1-sol
        - gpt-6-astra
        - gemini-3.8-flash
        - null
      description: >-
        The AI model the agent runs on: `claude-sonnet-5-5`, `claude-opus-5-5`
        or `claude-fable-5-1` (Anthropic), `gpt-6.1-sol` or `gpt-6-astra`
        (OpenAI), `gemini-3.8-flash` (Google). Null for GuidingHand’s default,
        GPT-6 Astra (`gpt-6-astra`). With `effort`, it sets the per-minute price
        (see /pricing). Starting a task with a model this server can’t run
        returns 503 with `code: "model_unavailable"`.
    Effort:
      type:
        - string
        - 'null'
      enum:
        - low
        - medium
        - high
        - null
      description: >-
        How much the model thinks per step. Null for GuidingHand’s default
        (Low). With `model`, it sets the per-minute price (see /pricing).
    GuardrailsInput:
      type:
        - object
        - 'null'
      description: >-
        Send only the keys to change: the others keep their value. A key set to
        `null` goes back to its default, and `"guardrails": null` resets them
        all. An invalid value returns 400 with a message saying which.
      properties:
        mode:
          type:
            - string
            - 'null'
          enum:
            - supervised
            - unattended
            - null
          description: >-
            `supervised`: someone can approve (your team, and the person at the
            computer when `customer_approvals` is on). `unattended`: nobody is
            there to approve, so anything that would need an approval is refused
            instead and the agent plans around it, and a safety check stops the
            task. Questions still go to your team (and the person at the
            computer, when `customer_answers` is on) as usual.
        confirm:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/GuardrailCategory'
          uniqueItems: true
          description: >-
            Always ask for an approval before these, described by GuidingHand
            rather than by the model. In `unattended` mode they are refused
            instead.
        block:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/GuardrailCategory'
          uniqueItems: true
          description: >-
            Never do these. A category in both `confirm` and `block` is blocked;
            take it out of `block` later and, if it’s still in `confirm`, the
            agent asks first again. Both lists come back as you set them.
        apps:
          type:
            - object
            - 'null'
          properties:
            allow:
              type:
                - array
                - 'null'
              maxItems: 50
              items:
                type: string
                maxLength: 100
              description: Only these apps (when not empty).
            block:
              type:
                - array
                - 'null'
              maxItems: 50
              items:
                type: string
                maxLength: 100
              description: Never these apps.
          description: >-
            App names or ids (`Google Chrome`, `com.google.Chrome`,
            `chrome.exe`), matched without regard to case. Names differ by
            system, so for Macs and Windows PCs list both. A non-empty `allow`
            means only those apps; on a Mac the Dock, menu bar, Control Center
            and Spotlight stay usable, and on Windows nothing is exempt (the
            taskbar, File Explorer and the Run box are `explorer.exe`). `block`
            always wins. With any app rule, a step GuidingHand can’t place in an
            app is refused. Only the list you send changes; `null` resets it to
            empty.
        sites:
          type:
            - object
            - 'null'
          properties:
            allow:
              type:
                - array
                - 'null'
              maxItems: 50
              items:
                type: string
                maxLength: 100
              description: Only these sites in a browser (when not empty).
            block:
              type:
                - array
                - 'null'
              maxItems: 50
              items:
                type: string
                maxLength: 100
              description: Never these sites.
          description: >-
            Hosts, stored in lowercase without a scheme, path or port
            (`https://Shop.example.com/cart` is kept as `shop.example.com`).
            `example.com` also covers its subdomains. Site rules cover anything
            showing a web page: browsers, and apps with web content such as
            Slack’s desktop app, which counts as the site it shows; an app’s
            local files (`file://`) aren’t a site. A non-empty `allow` means
            only those sites. `block` wins over `allow`. With any site rule, a
            step on a page whose address can’t be read is refused, and what’s
            typed into the address bar since it was last clicked is judged
            together, by where it goes. Site rules need a computer that reports
            page addresses (macOS with GuidingHand 1.0.20 or later, for now): on
            one that can’t, tasks with this agent fail at start. Only the list
            you send changes; `null` resets it to empty.
        safety_checks:
          type:
            - string
            - 'null'
          enum:
            - ask
            - stop
            - null
          description: >-
            When the model raises a safety check on a step: `ask` for an
            approval with `risk: high` (denying it stops the task), or `stop`
            the task. In `unattended` mode a safety check always stops the task.
        scope:
          type:
            - string
            - 'null'
          maxLength: 2000
          description: >-
            What this agent is for, in plain language. When set, each task is
            checked against it before it starts: a task that is clearly
            something else, or one the check can’t give a clear yes or no on,
            fails with the reason in `error`. Empty: no check.
        screen_check:
          type:
            - boolean
            - 'null'
          description: >-
            Check each new app, window or site the agent sees for text that
            tries to give the agent instructions (prompt injection). What
            happens when it finds some, or can’t check, follows `safety_checks`.
        max_steps:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 500
          description: >-
            Model turns per task. A task that reaches it without finishing
            fails.
        max_minutes:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 240
          description: >-
            Running minutes per task, not counting time waiting for answers or
            approvals. A task that reaches it fails. `null`: no limit.
        approval_timeout_minutes:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 1440
          description: >-
            An approval nobody decides within this many minutes is denied, with
            `answered_by: "timeout"`. `null`: wait until someone decides.
    AgentToolsInput:
      type:
        - object
        - 'null'
      description: >-
        Send only what to change. In `files`, only the keys you send change.
        `http`, when sent, is the whole list: it replaces the agent’s HTTP tools
        (`[]` or `null` removes them all). `"files": null` goes back to the
        default, and `"tools": null` resets both. An invalid tool (a bad name,
        no description, a URL that isn’t public https) is a 400 that says which.
      properties:
        files:
          type:
            - object
            - 'null'
          properties:
            enabled:
              type: boolean
              description: >-
                The agent has its own file system: text files GuidingHand keeps
                for this agent, separate from the computer it works on. It can
                list, read and search them during tasks.
            access:
              type: string
              enum:
                - read_write
                - read_only
              description: >-
                `read_write`: it can also write, change and delete files, to
                keep notes its later tasks read. `read_only`: it only reads them
                (files your team put there).
        http:
          type:
            - array
            - 'null'
          maxItems: 20
          items:
            $ref: '#/components/schemas/HttpTool'
    Guardrails:
      type: object
      required:
        - mode
        - confirm
        - block
        - apps
        - sites
        - safety_checks
        - scope
        - screen_check
        - max_steps
        - max_minutes
        - approval_timeout_minutes
      description: >-
        Rules GuidingHand enforces on each action the agent asks for, before it
        runs. They add to GuidingHand’s own rules and can’t loosen them. In
        order, the first that applies decides: text that looks like a secret, or
        typing into a password field, is not typed (the person is asked to type
        it); a match in `apps.block` or `sites.block` is blocked; with a
        non-empty `apps.allow` or `sites.allow`, anything not on it is blocked;
        a step in a `block` category is blocked; a step in a `confirm` category
        waits for an approval; anything else runs. On a Mac (GuidingHand 1.0.20
        or later) a step’s category comes from what it would do: what’s under a
        click; what Space, Return or Enter presses (the focused button, or a
        dialog’s default button); Cmd/Ctrl+Enter, and Enter in a message box,
        count as sending; Delete or Backspace outside a text field counts as
        deleting; both ends of a drag (dropping on the Trash counts as
        deleting); and typed text a line at a time (text holding a secret is
        refused whole). A click or acting key GuidingHand can’t see waits for an
        approval when the agent has any `confirm` or `block` categories. A Mac
        where GuidingHand isn’t allowed Accessibility can’t check guardrails, so
        a task with rules to check fails at start. On Windows, and before
        1.0.20, categories rest on the agent’s own approval requests. Always
        returned whole, with defaults filled in.
      properties:
        mode:
          type: string
          enum:
            - supervised
            - unattended
          default: supervised
          description: >-
            `supervised`: someone can approve (your team, and the person at the
            computer when `customer_approvals` is on). `unattended`: nobody is
            there to approve, so anything that would need an approval is refused
            instead and the agent plans around it, and a safety check stops the
            task. Questions still go to your team (and the person at the
            computer, when `customer_answers` is on) as usual.
        confirm:
          type: array
          items:
            $ref: '#/components/schemas/GuardrailCategory'
          uniqueItems: true
          default:
            - purchase
            - send
            - delete
            - install
            - security
            - terms
          description: >-
            Always ask for an approval before these, described by GuidingHand
            rather than by the model. In `unattended` mode they are refused
            instead.
        block:
          type: array
          items:
            $ref: '#/components/schemas/GuardrailCategory'
          uniqueItems: true
          default: []
          description: >-
            Never do these. A category in both `confirm` and `block` is blocked;
            take it out of `block` later and, if it’s still in `confirm`, the
            agent asks first again. Both lists come back as you set them.
        apps:
          type: object
          required:
            - allow
            - block
          properties:
            allow:
              type: array
              maxItems: 50
              items:
                type: string
                maxLength: 100
              default: []
              description: Only these apps (when not empty).
            block:
              type: array
              maxItems: 50
              items:
                type: string
                maxLength: 100
              default: []
              description: Never these apps.
          description: >-
            App names or ids (`Google Chrome`, `com.google.Chrome`,
            `chrome.exe`), matched without regard to case. Names differ by
            system, so for Macs and Windows PCs list both. A non-empty `allow`
            means only those apps; on a Mac the Dock, menu bar, Control Center
            and Spotlight stay usable, and on Windows nothing is exempt (the
            taskbar, File Explorer and the Run box are `explorer.exe`). `block`
            always wins. With any app rule, a step GuidingHand can’t place in an
            app is refused.
        sites:
          type: object
          required:
            - allow
            - block
          properties:
            allow:
              type: array
              maxItems: 50
              items:
                type: string
                maxLength: 100
              default: []
              description: Only these sites in a browser (when not empty).
            block:
              type: array
              maxItems: 50
              items:
                type: string
                maxLength: 100
              default: []
              description: Never these sites.
          description: >-
            Hosts, stored in lowercase without a scheme, path or port
            (`https://Shop.example.com/cart` is kept as `shop.example.com`).
            `example.com` also covers its subdomains. Site rules cover anything
            showing a web page: browsers, and apps with web content such as
            Slack’s desktop app, which counts as the site it shows; an app’s
            local files (`file://`) aren’t a site. A non-empty `allow` means
            only those sites. `block` wins over `allow`. With any site rule, a
            step on a page whose address can’t be read is refused, and what’s
            typed into the address bar since it was last clicked is judged
            together, by where it goes. Site rules need a computer that reports
            page addresses (macOS with GuidingHand 1.0.20 or later, for now): on
            one that can’t, tasks with this agent fail at start.
        safety_checks:
          type: string
          enum:
            - ask
            - stop
          default: ask
          description: >-
            When the model raises a safety check on a step: `ask` for an
            approval with `risk: high` (denying it stops the task), or `stop`
            the task. In `unattended` mode a safety check always stops the task.
        scope:
          type: string
          maxLength: 2000
          default: ''
          description: >-
            What this agent is for, in plain language. When set, each task is
            checked against it before it starts: a task that is clearly
            something else, or one the check can’t give a clear yes or no on,
            fails with the reason in `error`. Empty: no check.
        screen_check:
          type: boolean
          default: false
          description: >-
            Check each new app, window or site the agent sees for text that
            tries to give the agent instructions (prompt injection). What
            happens when it finds some, or can’t check, follows `safety_checks`.
        max_steps:
          type: integer
          minimum: 1
          maximum: 500
          default: 150
          description: >-
            Model turns per task. A task that reaches it without finishing
            fails.
        max_minutes:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 240
          default: null
          description: >-
            Running minutes per task, not counting time waiting for answers or
            approvals. A task that reaches it fails. `null`: no limit.
        approval_timeout_minutes:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 1440
          default: null
          description: >-
            An approval nobody decides within this many minutes is denied, with
            `answered_by: "timeout"`. `null`: wait until someone decides.
    AgentTools:
      type: object
      required:
        - files
        - http
      description: >-
        The agent’s tools beyond the computer: its own file system (on by
        default) and your HTTP tools. Part of its config, so versioned with it.
        Always returned whole, with defaults filled in.
      properties:
        files:
          type: object
          required:
            - enabled
            - access
          properties:
            enabled:
              type: boolean
              default: true
              description: >-
                The agent has its own file system: text files GuidingHand keeps
                for this agent, separate from the computer it works on. It can
                list, read and search them during tasks.
            access:
              type: string
              enum:
                - read_write
                - read_only
              default: read_write
              description: >-
                `read_write`: it can also write, change and delete files, to
                keep notes its later tasks read. `read_only`: it only reads them
                (files your team put there).
        http:
          type: array
          maxItems: 20
          items:
            $ref: '#/components/schemas/HttpTool'
          default: []
          description: Your own functions the agent can call, up to 20.
    Actor:
      oneOf:
        - title: Person
          type: object
          description: A person in the org (in the console).
          properties:
            type:
              const: user
            email:
              type:
                - string
                - 'null'
              example: jane@acme.com
        - title: API key
          type: object
          description: One of the org’s API keys.
          properties:
            type:
              const: api_key
            key_id:
              type: string
            name:
              type:
                - string
                - 'null'
              example: Zendesk
            prefix:
              type:
                - string
                - 'null'
              description: The key’s first characters.
              example: gh_live_AbC1
        - type: 'null'
      description: >-
        Who did it: a person (in the console) or an API key. Null when nobody
        was recorded: it was made with a session token, by GuidingHand itself,
        or before this was kept.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request
                - authentication
                - payment_required
                - permission
                - not_found
                - conflict
                - rate_limit
                - server_error
            message:
              type: string
            code:
              type: string
              description: >-
                Why it was refused, when there’s more to say than the status.
                Starting a task: `insufficient_balance`,
                `auto_topup_limit_reached` or `card_needs_attention` (402: the
                balance can’t cover a minute; add funds, raise the auto top-up
                limit, or update the card), `concurrency_limit` (429),
                `model_unavailable` (503: the agent’s `model` can’t run on this
                server; choose another). On `/respond`: `already_answered`
                (someone answered or decided first: see `answered_by`) or
                `not_pending` (that question or approval isn’t open any more).
                The GuidingHand app hears two more, which the API never returns:
                `customer_answers_off` and `customer_approvals_off` (the agent
                keeps its questions or approvals for your team). Deleting a
                start state that test cases use: `in_use` (409, with
                `test_case_ids`).
            answered_by:
              type: string
              enum:
                - customer
                - operator
                - timeout
              description: >-
                With `already_answered`: who answered or decided first
                (`customer`: the person at the computer; `timeout`: nobody
                decided an approval within the agent’s
                `approval_timeout_minutes`, so it was denied).
            test_case_ids:
              type: array
              items:
                type: string
              description: 'With `in_use`: the test cases that use the start state.'
    GuardrailCategory:
      type: string
      enum:
        - purchase
        - send
        - delete
        - install
        - security
        - terms
      description: >-
        `purchase`: buying or paying. `send`: sending, posting or submitting.
        `delete`: deleting or removing. `install`: installing software.
        `security`: security and account settings. `terms`: accepting terms or
        agreements.
    HttpTool:
      type: object
      required:
        - name
        - description
        - url
      description: >-
        One of your functions the agent can call. When the model calls it,
        GuidingHand POSTs the call to `url`, signed (see the `tool_call`
        webhook), and gives the model your response body.
      properties:
        name:
          type: string
          pattern: ^[a-zA-Z][a-zA-Z0-9_-]{0,63}$
          example: lookup_order
          description: >-
            What the model calls it: a letter, then letters, numbers, `_` or
            `-`, up to 64. Unique for the agent (in any case), and not one of
            GuidingHand’s own (`ask_user`, `request_approval`, `computer`, or
            the file system’s).
        description:
          type: string
          maxLength: 1000
          example: 'Look up an order by its number: status, items and delivery date.'
          description: What it does and when to use it, for the model.
        url:
          type: string
          format: uri
          example: https://api.acme.com/guidinghand/lookup-order
          description: 'Your endpoint: a public https URL, without a user name or password.'
        parameters:
          type: object
          default:
            type: object
            properties: {}
          description: >-
            A JSON Schema object for its arguments (`{ "type": "object",
            "properties": { … } }`), under 10 KB. Left out: no arguments.
        approval:
          type: string
          enum:
            - never
            - always
          default: never
          description: >-
            `always`: each call waits for an approval first, like any approval
            (the task is `waiting_for_approval`, and the person at the computer
            can decide it when `customer_approvals` is on). In `unattended` mode
            such a call is refused.
  responses:
    E400:
      description: The request is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    E401:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    E403:
      description: Your role can’t do this.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    E404:
      description: Not found in this org.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An org API key (`gh_live_…`) from Settings → API keys in the console.

````