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

# Update a hand-off

> Change status or assignment.

**Credential:** user token · **Role:** owner, admin or responder (`reply`)

Mirrors the hand-off console's status and assign controls. A developer or viewer token is refused.



## OpenAPI

````yaml /openapi/v1.json patch /v1/handoffs/{id}
openapi: 3.1.0
info:
  title: Peeve API
  version: '2026-08-17'
  description: >-
    The `/v1` API. Server-to-server only — no CORS headers are sent, no `Origin`
    is checked, and publishable keys are rejected everywhere.


    Most endpoints take a **user token** (`pv_ut_…`), which carries its owner's
    live workspace role. `/v1/users` and `/v1/answer` take the **workspace
    secret key** (`sk_…`). The credential and the required role are stated on
    every operation — they are not uniform.
servers:
  - url: https://api.peeve.ai
security:
  - userToken: []
tags:
  - name: Sessions
    description: Agent conversations and their transcripts.
  - name: Contacts
    description: >-
      Everyone Peeve knows about: **users** your product identified, and
      anonymous **visitors**. Filter with `?kind=`.
  - name: Users
    description: Push your identified users into Peeve from your backend.
  - name: Ask
    description: Ask the agent a question from your backend and get a grounded answer.
  - name: Leads
    description: >-
      Visitors the agent qualified. Read and re-status only — capture happens
      through Peeve's own channels.
  - name: Meetings
    description: Meetings booked through the agent.
  - name: Hand-offs
    description: Escalations to a human, and their status.
  - name: Analytics
    description: Usage and satisfaction results.
  - name: Configuration
    description: Guardrails and the kill switch.
  - name: Webhooks
    description: >-
      Register and manage the endpoints Peeve delivers events to. Equivalent to
      the Connectors UI — an endpoint created here is the same row, with the
      same guarantees.
paths:
  /v1/handoffs/{id}:
    patch:
      tags:
        - Hand-offs
      summary: Update a hand-off
      description: >-
        Change status or assignment.


        **Credential:** user token · **Role:** owner, admin or responder
        (`reply`)


        Mirrors the hand-off console's status and assign controls. A developer
        or viewer token is refused.
      operationId: updateHandoff
      parameters:
        - $ref: '#/components/parameters/id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - pending
                    - resolved
                    - closed
            example:
              status: resolved
      responses:
        '200':
          description: The updated hand-off.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HandoffObject'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    id:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
  schemas:
    HandoffObject:
      allOf:
        - $ref: '#/components/schemas/ObjectEnvelope'
        - type: object
          properties:
            data:
              $ref: '#/components/schemas/Handoff'
    ObjectEnvelope:
      type: object
      required:
        - object
        - data
        - request_id
      properties:
        object:
          type: string
          description: The resource type, e.g. `lead`.
        data:
          type: object
          additionalProperties: true
        request_id:
          type: string
          description: Echoed in the `X-Request-Id` header. Quote it to support.
    Handoff:
      type: object
      properties:
        id:
          type: string
          format: uuid
        object:
          type: string
          const: handoff
        session_id:
          type:
            - string
            - 'null'
          format: uuid
        kind:
          type: string
          enum:
            - ticket
            - chat
        status:
          type: string
          enum:
            - open
            - pending
            - resolved
            - closed
        ask:
          type:
            - string
            - 'null'
          description: The brief written for the teammate.
        stop_reason:
          type:
            - string
            - 'null'
        ticket_ref:
          type:
            - string
            - 'null'
          description: The linked helpdesk ticket, when a routing rule opened one.
        route:
          type:
            - string
            - 'null'
        destructive:
          type:
            - boolean
            - 'null'
        requester:
          type: string
          enum:
            - human
            - agent
        requester_label:
          type:
            - string
            - 'null'
        confidence:
          type:
            - number
            - 'null'
        intent:
          type:
            - string
            - 'null'
        intent_confidence:
          type:
            - number
            - 'null'
        intent_at:
          type:
            - string
            - 'null'
          format: date-time
        resolution:
          type:
            - string
            - 'null'
          enum:
            - solved
            - unsolved
            - partial
            - abandoned
            - null
        resolution_reason:
          type:
            - string
            - 'null'
        resolution_confidence:
          type:
            - number
            - 'null'
        resolution_at:
          type:
            - string
            - 'null'
          format: date-time
        resolved_at:
          type:
            - string
            - 'null'
          format: date-time
        assigned_at:
          type:
            - string
            - 'null'
          format: date-time
        external_responder:
          type:
            - string
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ErrorEnvelope:
      type: object
      required:
        - error
        - request_id
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - authentication_error
                - invalid_request_error
                - not_found_error
                - rate_limit_error
                - api_error
              description: The coarse family. Branch on this.
            code:
              type: string
              description: The stable machine label.
            message:
              type: string
            param:
              type: string
              description: The offending query parameter or dotted body path.
        request_id:
          type: string
  responses:
    BadRequest:
      description: >-
        An unknown or duplicated parameter, a bad value, a backwards date
        window, or a rejected unknown body key. The `param` field names the
        offender.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: invalid_request_error
              code: unknown_parameter
              message: >-
                Unknown query parameter 'since'. This endpoint accepts:
                created_after, created_before, cursor, limit, order, sort.
              param: since
            request_id: req_xxx
    Unauthorized:
      description: >-
        Missing, invalid or revoked credential — or the wrong **kind** of
        credential.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            wrongKind:
              summary: A secret key on a user-token endpoint
              value:
                error:
                  type: authentication_error
                  code: user_token_required
                  message: >-
                    This endpoint requires a user token (pv_ut_…), not a
                    workspace secret key. A shared key cannot express a person's
                    role, so it cannot be used to read workspace data or change
                    configuration. Create your own token in Settings → API keys.
                request_id: req_xxx
            publishable:
              summary: A publishable key
              value:
                error:
                  type: authentication_error
                  code: publishable_key_not_allowed
                  message: >-
                    Publishable keys (pk_…) are public by construction and can
                    never read or write workspace data.
                request_id: req_xxx
            invalid:
              summary: Revoked, unknown, or the owner left the workspace
              value:
                error:
                  type: authentication_error
                  code: invalid_credential
                  message: >-
                    That credential is not valid, has been revoked, or its owner
                    is no longer a member of this workspace.
                request_id: req_xxx
    Forbidden:
      description: >-
        The credential is valid but its **role** is insufficient. The message
        names the role it resolved to and the capability it lacked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: invalid_request_error
              code: insufficient_role
              message: >-
                This credential acts as 'viewer', which cannot change workspace
                settings (owner or admin). A user token carries its owner's
                workspace role, so this needs a teammate whose role holds
                'manage_team'.
            request_id: req_xxx
    NotFound:
      description: >-
        No such record **in this workspace**. Identical whether the id belongs
        to another tenant or does not exist — a distinguishable response would
        be an existence oracle.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: not_found_error
              code: resource_not_found
              message: >-
                No lead with id '00000000-0000-4000-8000-000000000000' in this
                workspace.
              param: id
            request_id: req_xxx
    RateLimited:
      description: >-
        Over the per-credential ceiling. Carries `Retry-After`. All `/v1`
        endpoints share one bucket per credential.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: rate_limit_error
              code: rate_limit_exceeded
              message: Too many requests (key limit). Retry in 60s.
            request_id: req_xxx
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait.
  securitySchemes:
    userToken:
      type: http
      scheme: bearer
      description: >-
        A user token (`pv_ut_…`) as `Authorization: Bearer pv_ut_…`.
        `X-Peeve-Key` is accepted too. **Never a query parameter** — URLs end up
        in proxy logs and `Referer` headers.


        The token carries its owner's **live** workspace role, re-read on every
        call.

````