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

# Refine a Composed Agent

> Applies one plain-language change to the session's agent — "also ask about relocation", "make it Slovak", "drop the salary question" — and returns the updated preview. Still creates nothing.

Change the agent a composer session is holding, by saying what you want changed.

```javascript theme={null}
POST /agents/composer/9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90/messages
{ "message": "Also ask about relocation, and make the whole call Slovak." }
```

The response is the same shape [the session started with](/api-reference/agents/compose-agent): the updated `agent` preview, and a `message` saying what changed.

<Warning>
  **Still no agent.** Refining changes the preview the session is holding. Nothing exists in
  your account until you post to [`POST /agents`](/api-reference/agents/create-agent).
</Warning>

## You do not send the agent back

The session holds the agent, so a follow-up carries only your instruction. There is nothing to keep in sync on your side, and no way to accidentally send back a stale version of the conversation.

You can refine as many times as you like within the session's 30 days.

## What a message can do

Anything the visual builder can do to a custom agent — the composer and the in-app copilot are the same thing behind different doors:

| You say                                          | What changes                                               |
| ------------------------------------------------ | ---------------------------------------------------------- |
| "Also ask about relocation"                      | A question is added to the right part of the conversation  |
| "Make it Slovak"                                 | The language, and every message and prompt rewritten in it |
| "Drop the salary question"                       | That question is removed                                   |
| "Be more direct, less chatty"                    | The agent's communication style                            |
| "Score the Go question as required"              | How the call is scored afterwards                          |
| "Call it something shorter"                      | The agent's name                                           |
| "Transfer strong candidates to +421 900 123 456" | A handoff step (`PHONE` agents only)                       |

<Info>
  **Renaming is only ever on request.** If you pinned a `name` when you started the session,
  it stays pinned — the composer will not quietly relabel your agent while doing something
  else.
</Info>

## A rejected change stores nothing

Every turn is validated the same way the create route validates a flow. If a change would break the conversation — emptying a required message, leaving a conditional with one branch — the turn is rejected with a `422` and **the session is untouched**: no half-applied edit, and no record of a message that did not land. Send a different instruction and carry on.

This is also what keeps the promise that the preview is postable: the agent a session hands you has passed the create route's checks at every step, not just at the start.

## Errors

| Status | When                                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------------- |
| `400`  | `message` missing, empty, or over 4,000 characters                                                      |
| `404`  | No live session with that id belongs to your company — including one that expired                       |
| `409`  | Another message changed this session while yours was being applied. Fetch the session and send it again |
| `422`  | The change could not be applied without breaking the conversation. Nothing was stored                   |
| `429`  | Composer rate limit reached (10 per minute, 120 per hour)                                               |
| `503`  | The model was unavailable or timed out — the session is unchanged                                       |

<Info>
  An unknown session, an expired one, and one belonging to another company are all a `404`.
  They are deliberately indistinguishable.
</Info>

<Info>
  **A refinement takes a few seconds** — it is a language-model call. Allow up to a minute on
  your client. Asking for one thing at a time is faster than a compound instruction, and much
  easier to review afterwards.
</Info>


## OpenAPI

````yaml POST /agents/composer/{sessionId}/messages
openapi: 3.0.0
info:
  title: InstaView API
  description: |-
    InstaView API Documentation

    ## Authentication

    All endpoints require API key authentication using Bearer token:
    ```
    Authorization: Bearer sk_your_api_key_here
    ```

    ## API Key Management

    The API Key module provides comprehensive key management for:
    - **Direct Client Keys**: Company-scoped keys for your applications
    - **ATS Partner Keys**: Resource-scoped keys for ATS integrations

    ### Key Features
    - HMAC-SHA256 hashing for API key storage
    - Configurable rate limiting
    - Comprehensive audit logging
    - Company-level isolation
    - Resource scoping for ATS partners
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.instaview.sk
    description: Production API Gateway
security: []
tags: []
paths:
  /agents/composer/{sessionId}/messages:
    post:
      tags:
        - Agents
      summary: Refine a composed agent
      description: >-
        Applies one plain-language change to the session's agent — "also ask
        about relocation", "make it Slovak", "drop the salary question" — and
        returns the updated preview. Still creates nothing.
      operationId: PublicAgentComposerController_addMessage_v1
      parameters:
        - name: sessionId
          required: true
          in: path
          description: The composer session id returned by `POST /agents/composer`.
          schema:
            type: string
            format: uuid
        - name: companyId
          required: false
          in: query
          description: >-
            Required for ATS API keys to specify which company to access.
            Ignored for standard company API keys.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicComposerMessageDto'
      responses:
        '201':
          description: The updated preview and the composer's reply.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicComposerSessionDto'
        '404':
          description: No live composer session with that id belongs to your company.
        '422':
          description: >-
            The change could not be applied without breaking the conversation.
            Nothing was stored.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowValidationErrorResponse'
        '429':
          description: Composer rate limit reached (10 per minute, 120 per hour, per key).
      security:
        - bearer: []
components:
  schemas:
    PublicComposerMessageDto:
      type: object
      properties:
        message:
          type: string
          description: What to change, in plain language.
          example: Also ask about relocation, and make the whole call Slovak.
          maxLength: 4000
      required:
        - message
    PublicComposerSessionDto:
      type: object
      properties:
        sessionId:
          type: string
          format: uuid
          description: >-
            The session to send follow-up messages to, and to create the agent
            from.
        agent:
          $ref: '#/components/schemas/PublicComposerAgentPreviewDto'
        message:
          $ref: '#/components/schemas/PublicComposerTurnDto'
        expiresAt:
          type: string
          format: date-time
          description: >-
            When this session is deleted if you never create the agent. Sessions
            live 30 days.
      required:
        - sessionId
        - agent
        - message
        - expiresAt
    FlowValidationErrorResponse:
      type: object
      description: >-
        A 422 from a route that accepts a conversation flow. `errors` is present
        when the flow itself failed structural validation, and lists EVERY
        problem found — fixing a flow should not take one request per fault. It
        is absent on the other 422s these routes can return, which carry a
        `message` only.
      properties:
        statusCode:
          type: integer
          example: 422
        message:
          type: string
          example: Invalid conversation flow.
        errors:
          type: array
          description: Every problem found in the flow, one entry per problem.
          items:
            $ref: '#/components/schemas/FlowError'
        traceId:
          type: string
          description: Trace identifier for this request. Quote it when contacting support.
          example: 6a707aee000000000c1e285eefed9980
      required:
        - statusCode
        - message
    PublicComposerAgentPreviewDto:
      type: object
      description: >-
        The agent the composer would build. There is no `id`: this agent does
        not exist yet. Post this object to `POST /agents` to create it — it is
        accepted verbatim, with no ids or `schemaVersion` to strip first.
      properties:
        name:
          type: string
          example: Senior Developer Phone Screen
        type:
          type: string
          enum:
            - ONLINE
            - PHONE
        language:
          type: string
          enum:
            - EN
            - SK
            - CS
        duration:
          type: number
        voiceId:
          type: string
        backgroundSound:
          type: string
          enum:
            - OFFICE
            - 'OFF'
        companyPhoneNumberId:
          type: string
        metadata:
          type: object
        overrides:
          $ref: '#/components/schemas/AgentOverrides'
        flow:
          $ref: '#/components/schemas/ConversationFlow'
        guardrails:
          $ref: '#/components/schemas/AgentGuardrailsConfig'
        contextConfig:
          $ref: '#/components/schemas/AgentContextConfig'
        analyticsConfig:
          $ref: '#/components/schemas/AgentAnalyticsConfig'
      required:
        - type
        - flow
    PublicComposerTurnDto:
      type: object
      properties:
        id:
          type: string
          format: uuid
        role:
          type: string
          enum:
            - user
            - assistant
        content:
          type: string
          description: >-
            For an assistant turn: what the agent does, in plain language, in at
            most ten sentences. For a user turn: what you asked for.
          example: >-
            Your agent opens by introducing itself and confirming it's speaking
            with the right person. It then works through Go and Postgres
            experience…
        changes:
          type: array
          items:
            type: string
          description: Short labels for what this turn changed. Empty on your own messages.
          example:
            - Added a relocation question
            - Set the language to Slovak
        createdAt:
          format: date-time
          type: string
      required:
        - id
        - role
        - content
        - createdAt
    FlowError:
      type: object
      description: One problem found in a submitted conversation flow.
      properties:
        code:
          type: string
          description: >-
            Machine-readable code identifying what is wrong. Match on the codes
            you handle and fall back on `message` for the rest — the set grows
            as the flow schema does, and a code is added without a major
            version.
          example: UNKNOWN_BLOCK_TYPE
        message:
          type: string
          description: Human-readable explanation of this single problem.
          example: 'Unknown block type: teleport.'
        path:
          type: string
          description: >-
            Path to the offending node in the flow document you sent. This is
            what to show a user, and what to anchor an editor to.
          example: sections[2].branches[0].blocks[1]
      required:
        - code
        - message
    AgentOverrides:
      type: object
      description: >-
        Who the agent presents itself as on a call, when that is not the company
        your API key belongs to. Applies to every agent, template or custom.
        Nothing about ownership changes: billing, analytics and phone-number
        routing keep the real company. You are responsible for having the right
        to speak in the name you send.
      properties:
        companyName:
          type: string
          description: >-
            Company name the agent introduces itself with. Omit to use your own
            company's name.
          minLength: 1
          maxLength: 100
          example: Acme Manufacturing
        companyDescription:
          type: string
          description: >-
            Company description the agent may draw on. Falls back to your own
            company's description independently of `companyName`, so send it
            alongside a name override — otherwise the agent introduces itself as
            one company and describes another.
          minLength: 1
          maxLength: 1000
          example: >-
            Acme makes industrial fasteners and employs 400 people across
            Slovakia.
    ConversationFlow:
      type: object
      description: >-
        The conversation a custom agent runs: an opening message, the sections
        to work through, and a closing message. Sending a flow on an agent makes
        it a custom agent — do not also send `focus`.
      required:
        - firstMessage
        - sections
        - lastMessage
      properties:
        schemaVersion:
          type: string
          readOnly: true
          description: >-
            The flow schema this document is expressed in. Assigned by InstaView
            — sending it is rejected with 400, so an integration written today
            keeps working when the schema moves.
          example: 1.0.0
        firstMessage:
          $ref: '#/components/schemas/FlowFirstMessageBlock'
        sections:
          type: array
          description: >-
            The body of the conversation, worked through in order. At least one
            section is required.
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/FlowBlock'
        lastMessage:
          $ref: '#/components/schemas/FlowLastMessageBlock'
    AgentGuardrailsConfig:
      type: object
      description: Rules the agent must obey during the call. Custom agents only.
      required:
        - rules
      properties:
        rules:
          type: array
          description: >-
            Stated to the agent verbatim as instructions. Replaces the stored
            rules; an empty array removes them all.
          maxItems: 50
          items:
            type: string
            minLength: 1
            maxLength: 500
          example:
            - Never quote a price
            - Never promise a delivery date
    AgentContextConfig:
      type: object
      description: >-
        Who the agent is and what the call is about. Custom agents only. Company
        information is NOT set here — the description on your company profile is
        given to every custom agent automatically and resolved per call.
      properties:
        role:
          type: string
          description: Who the agent should present itself as.
          maxLength: 500
          example: an account executive for Acme
        communicationStyle:
          type: string
          description: >-
            How the agent speaks. The same list the visual builder offers, so an
            agent created over the API stays editable there.
          enum:
            - professional
            - friendly
            - warm and empathetic
            - assertive
            - concise and direct
            - enthusiastic
            - casual
          example: concise and direct
        callToAction:
          type: string
          description: What the call is trying to achieve.
          maxLength: 500
          example: book a 30-minute demo
    AgentAnalyticsConfig:
      type: object
      description: >-
        What the agent extracts, scores, decides and labels after every call.
        Custom agents only — sending it without a `flow` is rejected. The
        results come back on the conversation as `analytics` and on the
        `analysis.completed` webhook.


        Per-question scoring is **not** here: it is set inline on each looping
        question in the `flow` (`importance` / `weight`), because a question is
        addressed by an id InstaView assigns.
      properties:
        extractionTargets:
          type: array
          description: >-
            The values to pull out of every call. A target with an `ideal` also
            counts toward the match score.
          maxItems: 50
          items:
            $ref: '#/components/schemas/AnalyticsExtractionTarget'
        scoringCriteria:
          type: array
          description: >-
            Holistic judgements over the whole call that are not a single field
            or question, e.g. “handled objections”.
          maxItems: 25
          items:
            $ref: '#/components/schemas/AnalyticsScoringCriterion'
        outcomes:
          type: array
          description: >-
            Did the call achieve what it was for? Each outcome is decided met /
            not met per call.
          maxItems: 25
          items:
            $ref: '#/components/schemas/AnalyticsOutcome'
        outputTags:
          type: array
          description: Labels the AI may assign to a call.
          maxItems: 25
          items:
            $ref: '#/components/schemas/AnalyticsOutputTag'
        capture:
          allOf:
            - $ref: '#/components/schemas/AnalyticsCapture'
          description: >-
            Which qualitative artefacts to produce. Every flag defaults to
            `true`, so an omitted `capture` is not “capture nothing”.
    FlowFirstMessageBlock:
      type: object
      description: >-
        What the agent says first. Supports `{{contact.*}}` variables, injected
        per call.
      required:
        - type
        - text
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - first_message
        label:
          type: string
          description: Optional name for this block, shown in the visual builder.
          example: Qualify budget
        text:
          type: string
          description: The opening line.
          example: Hi {{contact.firstName}}, do you have two minutes?
    FlowBlock:
      description: One section of the conversation. `type` selects the shape.
      oneOf:
        - $ref: '#/components/schemas/FlowSequentialBlock'
        - $ref: '#/components/schemas/FlowLoopingBlock'
        - $ref: '#/components/schemas/FlowConditionalBlock'
        - $ref: '#/components/schemas/FlowHumanHandoffBlock'
      discriminator:
        propertyName: type
        mapping:
          sequential:
            $ref: '#/components/schemas/FlowSequentialBlock'
          looping:
            $ref: '#/components/schemas/FlowLoopingBlock'
          conditional:
            $ref: '#/components/schemas/FlowConditionalBlock'
          human_handoff:
            $ref: '#/components/schemas/FlowHumanHandoffBlock'
    FlowLastMessageBlock:
      type: object
      description: What the agent says before hanging up.
      required:
        - type
        - text
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - last_message
        label:
          type: string
          description: Optional name for this block, shown in the visual builder.
          example: Qualify budget
        text:
          type: string
          description: The closing line.
          example: Thanks for your time — have a great day!
    AnalyticsExtractionTarget:
      type: object
      description: One value to pull out of every call.
      required:
        - label
        - type
      properties:
        key:
          type: string
          description: >-
            Stable name this item's results come back under. Optional — omit it
            and InstaView derives a slug of `label` (`Budget confirmed` →
            `budget_confirmed`). Set it when your integration reads the results
            by name: a key you choose survives a later `label` change, a derived
            one does not. If two derived keys collide, the later one gets a
            numeric suffix (`budget`, `budget_2`). Must be unique within its
            list — a duplicate you sent is rejected with `400`, never renamed.
            Reserved names (`__proto__`, `constructor`, `prototype`) are
            rejected. Editing the agent in the visual builder re-derives the key
            from the label.
          maxLength: 64
          pattern: ^[A-Za-z0-9_]{1,64}$
          example: budget_confirmed
        label:
          type: string
          description: >-
            What to extract, in your own words. Also the default source of
            `key`.
          maxLength: 200
          example: Budget confirmed
        type:
          type: string
          description: >-
            The value's type. The extracted value is coerced to it, or returned
            as `null`.
          enum:
            - string
            - number
            - bool
            - enum
          example: bool
        enumValues:
          type: array
          description: The allowed values. Required when `type` is `enum`.
          maxItems: 50
          items:
            type: string
          example:
            - hot
            - warm
            - cold
        ideal:
          type: string
          description: >-
            What a good value looks like. Its PRESENCE is what makes this field
            count toward the match score — a field with no `ideal` is extracted
            but not scored.
          maxLength: 500
          example: A confirmed budget of at least 10k
        importance:
          type: string
          description: >-
            How much a miss matters. `REQUIRED` gates the score; `PREFERRED`
            contributes by weight.
          enum:
            - REQUIRED
            - PREFERRED
          example: PREFERRED
        weight:
          type: number
          description: >-
            Relative contribution to the match score. Weights are relative to
            each other, not a budget that must total 100.
          minimum: 0
          maximum: 100
          example: 40
    AnalyticsScoringCriterion:
      type: object
      description: A holistic judgement over the whole call.
      required:
        - label
        - description
      properties:
        key:
          type: string
          description: >-
            Stable name this item's results come back under. Optional — omit it
            and InstaView derives a slug of `label` (`Budget confirmed` →
            `budget_confirmed`). Set it when your integration reads the results
            by name: a key you choose survives a later `label` change, a derived
            one does not. If two derived keys collide, the later one gets a
            numeric suffix (`budget`, `budget_2`). Must be unique within its
            list — a duplicate you sent is rejected with `400`, never renamed.
            Reserved names (`__proto__`, `constructor`, `prototype`) are
            rejected. Editing the agent in the visual builder re-derives the key
            from the label.
          maxLength: 64
          pattern: ^[A-Za-z0-9_]{1,64}$
          example: handled_objections
        label:
          type: string
          description: Name of the judgement.
          maxLength: 200
          example: Handled objections
        description:
          type: string
          description: >-
            What a good call looks like for this criterion — what the score is
            judged against.
          maxLength: 1000
          example: Acknowledged the objection and answered it with a concrete example
        importance:
          type: string
          description: >-
            How much a miss matters. `REQUIRED` gates the score; `PREFERRED`
            contributes by weight.
          enum:
            - REQUIRED
            - PREFERRED
          example: PREFERRED
        weight:
          type: number
          description: >-
            Relative contribution to the match score. Weights are relative to
            each other, not a budget that must total 100.
          minimum: 0
          maximum: 100
          example: 40
    AnalyticsOutcome:
      type: object
      description: Something the call was for, decided met / not met afterwards.
      required:
        - label
        - description
      properties:
        key:
          type: string
          description: >-
            Stable name this item's results come back under. Optional — omit it
            and InstaView derives a slug of `label` (`Budget confirmed` →
            `budget_confirmed`). Set it when your integration reads the results
            by name: a key you choose survives a later `label` change, a derived
            one does not. If two derived keys collide, the later one gets a
            numeric suffix (`budget`, `budget_2`). Must be unique within its
            list — a duplicate you sent is rejected with `400`, never renamed.
            Reserved names (`__proto__`, `constructor`, `prototype`) are
            rejected. Editing the agent in the visual builder re-derives the key
            from the label.
          maxLength: 64
          pattern: ^[A-Za-z0-9_]{1,64}$
          example: meeting_booked
        label:
          type: string
          description: Name of the outcome.
          maxLength: 200
          example: Meeting booked
        description:
          type: string
          description: When this counts as achieved, in plain language.
          maxLength: 1000
          example: The contact agreed to a specific date and time
    AnalyticsOutputTag:
      type: object
      description: A label the AI may assign to a call.
      required:
        - label
      properties:
        key:
          type: string
          description: >-
            Stable name this item's results come back under. Optional — omit it
            and InstaView derives a slug of `label` (`Budget confirmed` →
            `budget_confirmed`). Set it when your integration reads the results
            by name: a key you choose survives a later `label` change, a derived
            one does not. If two derived keys collide, the later one gets a
            numeric suffix (`budget`, `budget_2`). Must be unique within its
            list — a duplicate you sent is rejected with `400`, never renamed.
            Reserved names (`__proto__`, `constructor`, `prototype`) are
            rejected. Editing the agent in the visual builder re-derives the key
            from the label.
          maxLength: 64
          pattern: ^[A-Za-z0-9_]{1,64}$
          example: needs_followup
        label:
          type: string
          description: The tag itself.
          maxLength: 200
          example: Needs follow-up
        description:
          type: string
          description: When to apply it.
          maxLength: 1000
          example: The contact asked to be called back
    AnalyticsCapture:
      type: object
      description: >-
        Which qualitative artefacts the after-call pipeline should produce.
        Every flag defaults to `true`.
      properties:
        recording:
          type: boolean
          default: true
          description: Keep the call recording.
        transcript:
          type: boolean
          default: true
          description: Keep the transcript.
        summary:
          type: boolean
          default: true
          description: Produce a call summary.
        qa:
          type: boolean
          default: true
          description: Answer the flow's looping questions from the transcript.
        evaluation:
          type: boolean
          default: true
          description: Produce the strengths / concerns evaluation.
        sentiment:
          type: boolean
          default: true
          description: Produce a sentiment read.
    FlowSequentialBlock:
      type: object
      description: A topic to cover, written as free text.
      required:
        - type
        - prompt
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - sequential
        label:
          type: string
          description: Optional name for this block, shown in the visual builder.
          example: Qualify budget
        prompt:
          type: string
          description: >-
            What the agent should cover here. Supports `{{contact.*}}`
            variables.
          example: Introduce yourself and explain why you are calling.
    FlowLoopingBlock:
      type: object
      description: >-
        A list of questions the agent works through, each with the answer it is
        measured against.
      required:
        - type
        - questions
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - looping
        label:
          type: string
          description: Optional name for this block, shown in the visual builder.
          example: Qualify budget
        questions:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/FlowLoopingQuestion'
    FlowConditionalBlock:
      type: object
      description: >-
        Splits the conversation into routes. The agent takes the branch whose
        condition matches.
      required:
        - type
        - branches
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - conditional
        label:
          type: string
          description: Optional name for this block, shown in the visual builder.
          example: Qualify budget
        branches:
          type: array
          description: Between 2 and 5 routes, evaluated in order.
          minItems: 2
          maxItems: 5
          items:
            $ref: '#/components/schemas/FlowConditionalBranch'
        defaultBranch:
          type: string
          description: >-
            Label of the branch to fall through to when no condition matches.
            Branch ids are assigned by InstaView, so the fallthrough route is
            addressed by label; a label that matches no branch is rejected with
            `400`. Returned on reads as well, so a flow read back can be sent
            again unchanged apart from the assigned ids.
          example: Not now
        defaultBranchId:
          type: string
          format: uuid
          readOnly: true
          description: >-
            The resolved id of the fallthrough branch, assigned by InstaView.
            Read-only: strip it along with the other ids before re-sending a
            flow, and set `defaultBranch` to change the route.
    FlowHumanHandoffBlock:
      type: object
      description: >-
        Transfer the call to a person and end the agent's part of it.


        **PHONE agents only** — an `ONLINE` agent has no call to transfer, and a
        flow carrying this block is rejected with `422`
        (`HUMAN_HANDOFF_NOT_PHONE`).


        **The transfer is final.** InstaView hands the call to the carrier and
        leaves it (a cold transfer), so nothing after this block ever runs — the
        flow's `lastMessage` included. The interview settles as completed, and
        the transcript and analysis cover the agent's part of the call only. A
        step that could never run is rejected rather than stored, so a handoff
        must be the **last block in its own list** (`HUMAN_HANDOFF_NOT_LAST`);
        ending one branch of a `conditional` is fine, since the other branches
        still reach whatever follows it.


        When a flow can reach **more than one** number, every handoff needs a
        `label` and the labels must differ (`HUMAN_HANDOFF_LABEL_REQUIRED`,
        `HUMAN_HANDOFF_LABEL_DUPLICATE`): the agent is offered all destinations
        at once and picks by label, since the number is never in its prompt.
        Handoffs are deduplicated by `phoneNumber`, so two blocks pointing at
        one line are a single destination and may share a label or omit it.


        There is deliberately no fallback number: once the carrier holds the
        call InstaView cannot observe a busy destination, so routing between
        destinations belongs in a `conditional` the agent takes while it is
        still on the call.
      required:
        - type
        - phoneNumber
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        type:
          type: string
          enum:
            - human_handoff
        label:
          type: string
          description: >-
            Optional name for this block, shown in the visual builder. Also
            names the destination for the agent, so a label saying who answers
            the number is worth setting.
          example: Sales
        phoneNumber:
          type: string
          description: >-
            Where to transfer the call, in E.164 format — a leading `+`, country
            code, no spaces or dashes. A number that is not E.164 is rejected
            with `422` (`HUMAN_HANDOFF_INVALID_NUMBER`) when the agent is
            created, not when the transfer is attempted.
          example: '+421900123456'
        message:
          type: string
          description: >-
            What the agent says immediately before transferring. Optional —
            omitted, InstaView supplies a neutral line in the call's language,
            which promises nothing about who answers because the block does not
            know. Supports `{{contact.*}}` variables. An empty string is
            rejected (`HUMAN_HANDOFF_MESSAGE_EMPTY`) rather than read as absent,
            so clearing the field cannot silently restore the default.
          example: I'll put you through to Martin on our sales team now, one moment.
    FlowLoopingQuestion:
      type: object
      required:
        - question
        - idealAnswer
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        question:
          type: string
          description: The question to ask. Supports `{{contact.*}}` variables.
          example: What are you using today?
        idealAnswer:
          type: string
          description: What a good answer looks like. Used when scoring the call.
          example: Names a competing tool
        weight:
          type: number
          description: >-
            Relative importance of this question when scoring. Setting it also
            opts the question into the match score (as `PREFERRED` unless
            `importance` says otherwise).
          minimum: 0
          example: 40
          maximum: 100
        importance:
          type: string
          description: >-
            Opts this question into the agent's match score. Set `importance`
            (or `weight`) to score it; a question with neither is asked but not
            scored. `REQUIRED` gates the score, `PREFERRED` contributes by
            weight.
          enum:
            - REQUIRED
            - PREFERRED
          example: PREFERRED
      description: >-
        A question the agent asks, with the answer it is measured against.
        `importance` and `weight` are how a question joins the match score —
        they live here rather than in `analyticsConfig` because a question is
        addressed by an id InstaView assigns, which a caller cannot know when
        writing the flow.
    FlowConditionalBranch:
      type: object
      required:
        - label
        - condition
        - blocks
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: >-
            Assigned by InstaView. Returned on reads; sending one anywhere in
            the flow is rejected with `400`.
        label:
          type: string
          description: >-
            What this route is — unique within the block, and how
            `defaultBranch` addresses it.
          example: Interested
        condition:
          $ref: '#/components/schemas/FlowBranchCondition'
        blocks:
          type: array
          description: >-
            The sections executed on this route. Conditionals may nest up to 3
            levels deep.
          maxItems: 50
          items:
            $ref: '#/components/schemas/FlowBlock'
    FlowBranchCondition:
      description: How the route is chosen.
      oneOf:
        - $ref: '#/components/schemas/FlowIntentCondition'
        - $ref: '#/components/schemas/FlowExpressionCondition'
      discriminator:
        propertyName: type
        mapping:
          intent:
            $ref: '#/components/schemas/FlowIntentCondition'
          expression:
            $ref: '#/components/schemas/FlowExpressionCondition'
    FlowIntentCondition:
      type: object
      description: Natural-language intent the agent evaluates from the conversation.
      required:
        - type
        - description
      properties:
        type:
          type: string
          enum:
            - intent
        description:
          type: string
          description: The intent to match.
          example: wants to see a demo
    FlowExpressionCondition:
      type: object
      description: Deterministic route on an extracted field.
      required:
        - type
        - field
        - operator
      properties:
        type:
          type: string
          enum:
            - expression
        field:
          type: string
          description: Name of the extracted field to test.
          example: budget
        operator:
          type: string
          enum:
            - EQUALS
            - NOT_EQUALS
            - GREATER_THAN
            - LESS_THAN
            - CONTAINS
            - EXISTS
        value:
          description: >-
            Value to compare against. Required for every operator except EXISTS,
            which ignores it. Use a number for GREATER_THAN and LESS_THAN, and a
            string for CONTAINS.
          example: 1000
        logicalOperator:
          type: string
          description: >-
            Reserved. Nothing composes conditions yet — a branch takes exactly
            one — so this is accepted and ignored.
          enum:
            - AND
            - OR
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      description: API key for authentication using Bearer scheme

````