> ## 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.

# Design an Agent

> Designs a complete custom agent from a plain-language brief and returns it as a preview, together with a session you can keep refining. **No agent is created.** Nothing appears in your agent list until you post the returned `agent` (or `{ "composerSessionId": … }`) to `POST /agents` — the `201` here created a session, not an agent.

Describe the agent you want in plain language and get back the agent InstaView would build for you — the whole conversation, ready to create.

<Warning>
  **This does not create an agent.** Nothing appears in your agent list, nothing can be
  called, and nothing is billable until you separately post the result to
  [`POST /agents`](/api-reference/agents/create-agent). The `201` returned here created a
  **composer session**, not an agent.
</Warning>

That is the point of this endpoint: you can put "here is the agent I'd build for you" in front of a person, let them read it, change their mind twice, and only then create anything.

## The shortest useful call

```javascript theme={null}
POST /agents/composer
{
  "type": "PHONE",
  "message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit."
}
```

```javascript theme={null}
{
  "sessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90",
  "agent": {
    "name": "Senior Backend Phone Screen",
    "type": "PHONE",
    "language": "EN",
    "flow": { "firstMessage": { "…": "…" }, "sections": [], "lastMessage": { "…": "…" } },
    "contextConfig": { "…": "…" },
    "analyticsConfig": { "…": "…" }
  },
  "message": {
    "id": "…",
    "role": "assistant",
    "content": "Your agent opens by introducing itself and confirming it's speaking with the right person. It then works through Go and Postgres experience, asks about notice period and salary expectation, and transfers strong candidates to a recruiter. Anyone who isn't a fit gets a polite close.",
    "changes": ["Named it Senior Backend Phone Screen", "Set the language to EN", "Added Skills deep-dive"],
    "createdAt": "2026-08-18T10:15:00.000Z"
  },
  "expiresAt": "2026-09-17T10:15:00.000Z"
}
```

Two fields are required: your `message`, and the `type` of agent to design.

<Info>
  **`type` is required and cannot change later.** The channel decides which conversation
  steps are legal at all — a transfer to a person exists only on `PHONE` — so it has to be
  known before the agent is designed rather than guessed from your brief.
</Info>

Everything else is optional and, if you send it, **wins over what the composer chose** — on this turn and on every later one. That is what `name`, `language`, `duration`, `voiceId`, `backgroundSound`, `companyPhoneNumberId` and `metadata` are for: pin the things you already know, and let the composer decide the rest.

<Info>
  **You do not send a `flow`.** `flow`, `focus`, `guardrails`, `contextConfig` and
  `analyticsConfig` are what the composer writes for you, and sending any of them is
  rejected with a `422` naming which. If you already have a flow, you do not need the
  composer — post it straight to [`POST /agents`](/api-reference/agents/create-agent).
</Info>

## The accompanying message

`message.content` is a plain-language description of the agent, **in at most ten sentences**: what it opens with, what it asks about, what it decides, and how it ends. It is written for a person, not for a parser — it is the text to show next to a "create this agent" button.

`message.changes` is a short list of labels for what the composer did. On the first turn it describes what it built; on later turns, what changed.

## What to do with the preview

`agent` is a body [`POST /agents`](/api-reference/agents/create-agent) accepts **verbatim** — no ids, no `schemaVersion`, and a conditional's fallthrough already named by `defaultBranch`. Nothing to strip, nothing to fix up.

<CardGroup cols={2}>
  <Card title="Change something first" icon="pen" href="/api-reference/agents/refine-agent">
    Send another message to the session — "also ask about relocation", "make it Slovak".
  </Card>

  <Card title="Create it" icon="check" href="/api-reference/agents/create-agent">
    Post the `agent` object, or just `{ "composerSessionId": "…" }`.
  </Card>
</CardGroup>

The simplest way to create it is to hand back the session id and let us build exactly what you were shown:

```javascript theme={null}
POST /agents
{ "composerSessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90" }
```

Send fields alongside it to change your mind at the last moment — `{ "composerSessionId": "…", "name": "Backend Screen v2" }` creates the previewed agent under a different name.

<Info>
  **The conversation follows the agent.** However you create it, the messages that designed
  it are attached to the new agent, so opening it in InstaView shows how it was built — and
  someone can carry on editing it there by hand.
</Info>

## Sessions expire

A session lives **30 days**. After that, a preview nobody accepted is deleted along with its conversation; `expiresAt` on every response tells you when. Creating the agent ends the clock — a session that became an agent is kept.

## Rate limits

Every call here designs an agent with a language model, so these routes carry their own limit on top of your key's usual allowance: **6 per minute and 60 per hour** for this endpoint, **10 per minute and 120 per hour** for [refining](/api-reference/agents/refine-agent). Exceeding it is a `429`.

## Errors

| Status | When                                                                                                                                                                                                             |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `message` or `type` missing, malformed, or over 8,000 characters — or you sent `flow`, `focus`, `guardrails`, `contextConfig` or `analyticsConfig`, which the composer writes (`property flow should not exist`) |
| `404`  | The composer is not enabled for your account                                                                                                                                                                     |
| `422`  | The brief could not be turned into a valid agent, or you sent `flow` / `focus` / a config object                                                                                                                 |
| `429`  | Composer rate limit reached                                                                                                                                                                                      |
| `503`  | The model was unavailable or timed out — nothing was created                                                                                                                                                     |

A `422` here means **no session was created**. Rephrase or add detail and call again.

<Info>
  **Designing an agent takes a few seconds** — it is a language-model call, not a database
  write, and a long brief takes longer than a short one. Allow up to two minutes on your
  client before you give up on the request; a short brief plus a couple of
  [refinements](/api-reference/agents/refine-agent) is both quicker and easier to review than
  one page-long brief.
</Info>


## OpenAPI

````yaml POST /agents/composer
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:
    post:
      tags:
        - Agents
      summary: Design an agent from a description
      description: >-
        Designs a complete custom agent from a plain-language brief and returns
        it as a preview, together with a session you can keep refining. **No
        agent is created.** Nothing appears in your agent list until you post
        the returned `agent` (or `{ "composerSessionId": … }`) to `POST /agents`
        — the `201` here created a session, not an agent.
      operationId: PublicAgentComposerController_start_v1
      parameters:
        - 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/PublicComposerStartDto'
      responses:
        '201':
          description: >-
            The composer session, the agent it would create, and the composer's
            reply.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicComposerSessionDto'
        '422':
          description: >-
            The composer could not turn that brief into a valid agent, and no
            session was created. Rephrase or add detail. (Sending `flow`,
            `focus`, `guardrails`, `contextConfig` or `analyticsConfig` is a
            400, not this: they are not fields of this route.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowValidationErrorResponse'
        '429':
          description: Composer rate limit reached (6 per minute, 60 per hour, per key).
      security:
        - bearer: []
components:
  schemas:
    PublicComposerStartDto:
      type: object
      properties:
        message:
          type: string
          description: >-
            What the agent should do, in plain language. The composer designs
            the whole conversation from this.
          example: >-
            Screen senior backend devs. Ask about Go and Postgres, check notice
            period and salary expectation, then hand off to a recruiter if
            they're a strong fit.
          maxLength: 8000
        type:
          type: string
          description: >-
            The channel the agent will run on. Required, and fixed for the life
            of the session: it decides which conversation steps are available at
            all — a transfer to a person exists only on PHONE.
          enum:
            - ONLINE
            - PHONE
          example: PHONE
        name:
          type: string
          description: >-
            Name for the agent. Optional — the composer proposes one from your
            brief. Anything you send here wins, and keeps winning on later
            turns.
          example: Senior Developer Phone Screen
          minLength: 3
          maxLength: 100
        language:
          type: string
          description: >-
            Language of the conversation. When omitted, the composer follows the
            language of your brief.
          enum:
            - EN
            - SK
            - CS
        duration:
          type: number
          description: Target call length in minutes.
          minimum: 1
          maximum: 180
        voiceId:
          type: string
          description: Voice for the agent.
        backgroundSound:
          type: string
          description: Ambient background sound.
          enum:
            - OFFICE
            - 'OFF'
        companyPhoneNumberId:
          type: string
          description: The number this agent should call from. PHONE agents only.
        metadata:
          type: object
          description: >-
            Custom metadata carried through to the agent you create from this
            session.
      required:
        - message
        - type
    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

````