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

# Create Conversation

> Creates a conversation with a contact, using an existing or inline contact/agent/job, scoped to the API key's company. Respects company billing limits and subscription plans. Set isTest=true to create a test conversation that completes immediately without consuming billing minutes.

<Info>
  `POST /interviews` is a permanent alias of this endpoint and keeps working unchanged, with the same scopes and the same response. See [Resource names](/api-reference/introduction#resource-names).
</Info>

Creates a conversation — one AI-run call to one contact, by phone or on the web. You can point at contacts and agents you already have, or define them inline in the same request.

## Overview

A conversation is created either to run immediately or at a `scheduleTime` you name. Everything it needs can come from ids you already hold (`contactId`, `agentId`) or from inline objects the request creates as it goes, so an integration that has just learned about a person does not need two round trips before it can call them.

## Use Cases

* **Scheduled calls**: Run the conversation at a specific date and time
* **Immediate calls**: Start as soon as the agent's calling hours allow
* **Inline resources**: Create a conversation without pre-creating the contact or the agent
* **Bulk outreach**: Automate one conversation per contact across a list

## Inline Contact, Agent, and Job Support

Contacts, agents and jobs that do not exist yet can be defined inline as objects on the request. Nothing has to be created first.

<Info>
  The inline contact body is `contact`. `candidate` is its permanent legacy alias and takes the
  same fields; send one or the other, not both. The same holds for `contactId` and `candidateId`.
</Info>

### Inline Contact with Job Association

```javascript theme={null}
{
  "contact": {
    "jobId": "job-uuid",
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@example.com",
    "phoneNumber": "+1234567890",
    "gdprExpiryDate": "2026-11-16",
    "workHistory": [
      {
        "companyName": "Tech Corp",
        "candidatePosition": "Software Engineer",
        "referenceName": "Jane Smith",
        "referencePhone": "+1234567890",
        "startDate": "2020-01-01",
        "endDate": "2022-12-31"
      }
    ]
  },
  "agentId": "existing-agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}

```

### Inline Contact with Inline Job

Create both the contact and the job inline for a completely self-contained request:

```javascript theme={null}
{
  "contact": {
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@example.com",
    "phoneNumber": "+1234567890",
    "gdprExpiryDate": "2026-11-16"
  },
  "job": {
    "jobTitle": "Senior Backend Engineer",
    "jobDescription": "We are looking for a senior engineer...",
    "requiredSkills": ["TypeScript", "NestJS", "PostgreSQL"],
    "niceToHaveSkills": ["Redis", "AWS"],
    "languages": ["EN"],
    "experience": "SENIOR",
    "contractType": "FULL_TIME"
  },
  "agentId": "existing-agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Info>
  **Complete inline workflow**: when you send `contact` together with `job`: - a new **Job** is
  created for your company - a new **Contact** is created and automatically associated with that
  job - the **Conversation** is linked to both - all three become permanent resources you can
  fetch and manage afterwards
</Info>

### Inline Contact without Job Association

```javascript theme={null}
{
  "contact": {
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@example.com",
    "phoneNumber": "+1234567890",
    "gdprExpiryDate": "2026-11-16"
  },
  "agentId": "existing-agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Info>
  **Job specification options**: you can name the job in several ways: - **Create inline**: use
  `job` at the top level to create a new job - **Reference existing**: use `jobId` at the top
  level to link an existing job - **Within the inline contact**: use `jobId` inside `contact`
  for a more compact request when creating the contact and the association together - **No
  job**: omit every job field. A job is optional on a conversation, and most callers outside
  hiring never send one; the contact can still be assigned to jobs later via the [update
  contact endpoint](/api-reference/contacts/update-contact). **Priority**: when several job
  sources are provided, `job` wins over top-level `jobId`, which wins over `contact.jobId`.
  **XOR validation**: `contact.jobId` cannot be combined with a top-level `jobId` or `job`,
  since that would be ambiguous. Pick one per request.
</Info>

### Inline Custom Agent

The inline `agent` accepts the same custom-agent fields as [Create Agent](/api-reference/agents/create-agent#custom-agents): send a `flow` (optionally with `guardrails`, `contextConfig` and `analyticsConfig`) and the agent is created as a custom agent, running the conversation you designed instead of a template. As there, `focus` must be omitted when a `flow` is present, ids and `schemaVersion` are assigned by InstaView, and an invalid flow is rejected with a `422` carrying [the same `errors` array](/api-reference/agents/create-agent#flow-validation-errors) — every problem found, each with the `path` of the block it is on. No conversation is created when that happens.

Sending `analyticsConfig` here is how a one-off call returns structured data rather than just a transcript — the results come back as [`analytics`](/api-reference/conversations/get-conversation#custom-agent-analytics) on this conversation and on its `analysis.completed` webhook.

```javascript theme={null}
{
  "contact": {
    "firstName": "Jana",
    "lastName": "Novák",
    "phoneNumber": "+421900123456",
    "gdprExpiryDate": "2027-08-02"
  },
  "agent": {
    "name": "Renewal check-in",
    "type": "PHONE",
    "language": "EN",
    "duration": 10,
    "companyPhoneNumberId": "123e4567-e89b-12d3-a456-426614174000",
    "flow": {
      "firstMessage": { "type": "first_message", "text": "Hi {{contact.first_name}}, quick question about your renewal." },
      "sections": [{ "type": "sequential", "prompt": "Ask whether they intend to renew, and note any blockers." }],
      "lastMessage": { "type": "last_message", "text": "Thanks — we'll follow up by email." }
    },
    "analyticsConfig": {
      "extractionTargets": [
        { "key": "intends_to_renew", "label": "Intends to renew", "type": "bool" }
      ],
      "outcomes": [
        { "label": "Renewal confirmed", "description": "The contact committed to renewing" }
      ]
    }
  }
}
```

<Warning>
  **Inline creation mints a permanent agent per conversation.** That is fine for a one-off,
  but calling a list of 50 people this way leaves 50 near-identical custom agents behind.
  When the same flow will call more than one person, create the agent once via
  [POST /agents](/api-reference/agents/create-agent) and pass `agentId`.
</Warning>

## Job-Optional Focuses

Two kinds of agent are **job-optional** — they can call someone without associating the
conversation with a job: `GENERIC`, and any custom agent (one created with a `flow`). Every other
focus requires a job. The two are not identical, and the difference is worth knowing:

|                                        | `GENERIC`       | Custom (flow-based) |
| -------------------------------------- | --------------- | ------------------- |
| Conversation without a job             | Yes, always     | Yes, always         |
| Contact may hold job assignments       | Yes, any number | Yes, any number     |
| `jobId` needed for a multi-job contact | No              | No                  |
| May you attach a job anyway            | Yes, optional   | **No — `400`**      |

<Info>
  **A job-optional focus ignores the contact's assignments entirely.** You do not need to
  pick one with `jobId`, and a contact assigned to five jobs is called exactly like one
  assigned to none. The "specify a `jobId`" error described under [Contact with Multiple
  Jobs](#contact-with-multiple-jobs) applies only to job-requiring focuses.
</Info>

<Info>
  **Custom agents are always jobless.** An agent created with a `flow` is job-agnostic by
  design: its prompt comes entirely from the flow, so a job would never be read. This is
  stricter than `GENERIC`, which merely makes a job optional — sending `job`, `jobId` or
  `contact.jobId` for a custom agent is a `400` rather than a silently ignored field.
</Info>

`GENERIC` agents exist for conversations that are not about a job at all. That covers most
non-hiring work, and in hiring it covers:

* **Talent pool building**: calling people about future openings rather than a specific role
* **General outreach**: reaching contacts in your database who hold no job assignment
* **Pre-screening**: an initial conversation before anyone is matched to a position

### Creating a Jobless Conversation with GENERIC Focus

```javascript theme={null}
{
  "contact": {
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@example.com",
    "phoneNumber": "+1234567890",
    "gdprExpiryDate": "2026-11-16"
    // No jobId - a GENERIC conversation does not need one
  },
  "agentId": "generic-agent-uuid", // Agent with focus: "GENERIC"
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Info>
  **Validation**: The API will return a `400 Bad Request` error if:

  * You try to create a jobless conversation (no job specified) with an agent whose focus
    requires a job — that is, anything other than `GENERIC` or a custom agent
  * You attach a job to a custom agent's conversation

  Note that a `GENERIC` conversation is **not** rejected for a contact who has job assignments:
  the assignments are simply not used.

  For more detail on how a `GENERIC` conversation is analysed, see the [Conversations Resource Guide](/guides/resources/conversations#conversation-types--analysis).
</Info>

### Existing Contact with Inline Job

You can also define the job inline using `job` when you already have the contact but don't want to create a separate job resource first.

```javascript theme={null}
{
  "contactId": "contact-uuid",
  "job": {
    "jobTitle": "Senior Backend Engineer",
    "jobDescription": "We are looking for a senior engineer...",
    "jobUrl": "https://company.com/jobs/123",
    "benefits": "Health insurance, remote work",
    "requiredSkills": ["TypeScript", "NestJS", "PostgreSQL"],
    "niceToHaveSkills": ["Redis", "AWS"],
    "languages": ["EN"],
    "languageRequirements": [
      { "language": "EN", "proficiency": "C1" }
    ],
    "education": ["BACHELORS"],
    "experience": "SENIOR",
    "otherRequirements": "Comfortable with async communication",
    "contractType": "FULL_TIME",
    "location": {
      "workMode": "REMOTE",
      "street": "Main Street 1",
      "city": "Bratislava",
      "postalCode": "81101",
      "countryCode": "SK"
    },
    "salary": {
      "min": 4000,
      "max": 5500,
      "currency": "EUR",
      "period": "MONTHLY"
    },
    "humanRecruiter": "John Doe",
    "metadata": {
      "externalJobId": "JOB-12345",
      "department": "Engineering"
    }
  },
  "agentId": "existing-agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Info>
  **Inline job behaviour**: when you provide `job` together with an existing `contactId`, the
  API: - creates a full **Job** for your company from the inline fields - associates the contact
  with that job if they were not already linked - links the conversation to the new job.
  `jobId` and `job` are **mutually exclusive (XOR)**: use either `jobId` (an existing job) or
  `job` (an inline definition), never both.
</Info>

## Billing and Limits

<Warning>
  Creating a conversation consumes call minutes from your company's billing plan. Make sure
  enough are available before scheduling in bulk — the API returns `402 Payment Required` once
  billing limits are exceeded, with a `billing` object naming what the request needed and what
  was left. See [Billing errors](/guides/error-handling#billing-errors).

  **Exception:** test conversations (created with `isTest: true`) consume no minutes and bypass all billing checks.
</Warning>

## Scheduling Options

### Immediate Conversation

Omit `scheduleTime` to start the conversation as soon as possible:

```javascript theme={null}
{
  "contactId": "contact-uuid",
  "agentId": "agent-uuid"
}
```

With no `scheduleTime`, the time is ours to pick, so the agent's [calling window](/api-reference/agents/create-agent#retries-and-calling-hours) applies: a conversation created outside those hours goes out when they next open, in the contact's own timezone.

### Scheduled Conversation

Provide a future `scheduleTime` timestamp (max 30 days in the future):

```javascript theme={null}
{
  "contactId": "contact-uuid",
  "agentId": "agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Info>
  **A `scheduleTime` you send is used exactly as given**, including when it falls outside the
  agent's calling window. You know things the window does not, such as an appointment the contact
  agreed to, so naming a time overrides the hours rather than being moved into them. The window
  still governs everything we schedule ourselves: conversations created without a `scheduleTime`,
  and every retry after a call that did not connect.

  The future and 30-day limits above still apply, and are the only bounds on the value. Within
  them the offset only pins the moment: `14:00:00Z` and `16:00:00+02:00` are the same instant and
  are treated identically.
</Info>

<Info>
  **Note**: a `contactId` must already exist. For an inline `contact`, every required field
  (`firstName`, `lastName`, `email` or `phoneNumber`, `gdprExpiryDate`) must be present.
</Info>

## Job Selection for Existing Contacts

When using an existing `contactId`, you can optionally name a `jobId` to record which job the conversation is for. This matters when the contact is associated with several jobs.

### Contact with Single Job

If the contact has one job or none, `jobId` is optional — the contact's first job is used if there is one:

```javascript theme={null}
{
  "contactId": "contact-uuid",
  "agentId": "agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
  // jobId is optional - the contact's first job is used
}
```

### Contact with Multiple Jobs

When the contact is associated with several jobs, you **must** name the `jobId`:

```javascript theme={null}
{
  "contactId": "contact-uuid",
  "jobId": "job-uuid",  // Required when the contact has multiple job assignments
  "agentId": "agent-uuid",
  "scheduleTime": new Date(Date.now() + 60 * 60 * 1000).toISOString()
}
```

<Warning>
  **Multiple job assignments**: if the contact holds several and you send no `jobId`, the API
  returns `400 Bad Request` with the message *"This candidate is assigned to multiple jobs.
  Please specify a 'jobId' to associate with this interview."* — quoted as it is actually sent.
  Error strings are matched on by integrations, so they keep their original wording; only the
  resource names in this documentation changed.

  This applies only to job-requiring focuses. `GENERIC` and custom (flow-based) agents are
  job-optional, so they call a multi-job contact without a `jobId` — and a custom agent
  rejects one outright.
</Warning>

<Info>
  **Job association**: the `jobId` you provide must belong to the same company as your API key. If the contact is not already assigned to that job, they are assigned to it when the conversation is created.
</Info>

<Info>
  **Job selection rules**: `jobId` and `job` are XOR (mutually exclusive) — provide `jobId` and
  omit `job`, or provide `job` and omit `jobId`. When `job` is used: a new job is created for
  your company, that job becomes the conversation's `jobId`, and the contact is associated with
  it before the conversation is created.
</Info>

## Company Isolation

Every resource involved (contact, agent, job) must belong to the same company as your API key. The API validates ownership and returns `403 Forbidden` when they do not.

## Complete Workflow Examples

### Scenario 1: Conversation with Job Association

```javascript theme={null}
// 1. Create a job (if not exists)
const job = await createJob({
  title: "Senior Software Engineer",
  status: "OPEN",
});

// 2. Create the conversation with an inline contact linked to that job
const conversation = await createConversation({
  contact: {
    jobId: job.id,
    firstName: "Jane",
    lastName: "Doe",
    email: "jane@example.com",
    phoneNumber: "+1234567890",
    gdprExpiryDate: "2026-11-16",
  },
  agentId: "agent-uuid",
  scheduleTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // Tomorrow
});

// 3. Monitor its status
const status = await getConversation(conversation.id);
console.log(`Conversation status: ${status.status}`);
```

### Scenario 2: Conversation without Job Association

```javascript theme={null}
// 1. Create a conversation for a contact who holds no job assignment
const conversation = await createConversation({
  contact: {
    firstName: "John",
    lastName: "Smith",
    email: "john@example.com",
    phoneNumber: "+1234567890",
    gdprExpiryDate: "2026-11-16",
  },
  agentId: "agent-uuid",
  scheduleTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
});

// 2. Later, assign the contact to jobs if you need to
const contactId = conversation.contactId;
await updateContact(contactId, {
  jobIds: ["job-uuid-1", "job-uuid-2"],
});
```

### Scenario 3: Conversation for a Contact with Multiple Jobs

```javascript theme={null}
// 1. The contact is associated with several jobs
const contact = await getContact("contact-uuid");
// contact.jobIds = ["job-1-uuid", "job-2-uuid", "job-3-uuid"]

// 2. Create the conversation for one of them (jobId is required here)
const conversation = await createConversation({
  contactId: "contact-uuid",
  jobId: "job-2-uuid", // Must specify which job
  agentId: "agent-uuid",
  scheduleTime: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
});

// 3. The conversation is now associated with job-2-uuid
console.log(`Conversation created for job: ${conversation.jobId}`);
```

### Scenario 4: Existing Contact with Inline Job

```javascript theme={null}
// 1. The contact already exists in your system
const contactId = "contact-uuid";

// 2. Create the conversation and define the job inline
const conversation = await createConversation({
  contactId,
  job: {
    jobTitle: "Senior Backend Engineer",
    jobDescription: "We are looking for a senior engineer...",
    requiredSkills: ["TypeScript", "NestJS", "PostgreSQL"],
    niceToHaveSkills: ["Redis", "AWS"],
    languages: ["EN"],
    experience: "SENIOR",
    contractType: "FULL_TIME",
    location: { workMode: "REMOTE", countryCode: "SK" },
    metadata: { externalJobId: "JOB-12345" },
  },
  agentId: "agent-uuid",
  scheduleTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
});

console.log(`Conversation jobId: ${conversation.jobId}`);
```

<Info>
  The inline job is saved as a regular **Job**. You can fetch and manage it afterwards through
  the Jobs API using the `jobId` returned on the conversation.
</Info>

## Test Mode

Test mode creates a conversation that completes immediately and consumes no billing minutes. It exists so you can exercise webhook handlers and integration flows without placing a call.

### When to Use Test Mode

* **Testing webhook handlers**: check that your endpoint handles `conversation.completed` and `analysis.completed` correctly
* **Integration testing**: exercise your processing logic without waiting for a real call
* **Development**: build against completed conversations on demand
* **Demos**: create sample conversations to show

### How Test Mode Works

When you set `isTest: true`:

1. **Billing bypass**: all billing checks are skipped — no minutes are consumed
2. **Immediate completion**: the conversation is created with `COMPLETED` status straight away
3. **Mock analysis**: realistic analysis data is generated and stored
4. **Webhook events**: both `conversation.completed` and `analysis.completed` fire
5. **Default duration**: 5 minutes

<Info>
  The event names above are the default `NEUTRAL` vocabulary. A webhook registered before that
  option existed is on `LEGACY` and receives `interview.completed` instead — same payload, same
  trigger. See [Event name vocabulary](/guides/webhooks#event-name-vocabulary).
</Info>

### Example: Creating a Test Conversation

```javascript theme={null}
const testConversation = await createConversation({
  contact: {
    firstName: "Jane",
    lastName: "Doe",
    email: "jane@example.com",
    phoneNumber: "+1234567890",
    gdprExpiryDate: "2026-11-16",
  },
  agentId: "agent-uuid",
  isTest: true, // Enable test mode
});

console.log(`Test conversation created: ${testConversation.id}`);
console.log(`Status: ${testConversation.status}`); // "COMPLETED"
console.log(`Duration: ${testConversation.durationMinutes} minutes`); // 5
```

### Test Conversation Characteristics

* **Status**: always `COMPLETED` immediately
* **Duration**: 5 minutes (300 seconds)
* **Analysis**: mock analysis, including:
  * conversation check results (`candidateInterest: true`, `conversationCompleted: true`)
  * analysis scores and recommendations
  * mock transcript segments
* **Webhooks**: fires `conversation.completed` and `analysis.completed`
* **Billing**: no minutes consumed, no billing checks performed

<Info>
  Test conversations carry `isTest: true`. They appear in listings and can be queried like any other, but they do not affect billing or production metrics.
</Info>

## Related Resources

<CardGroup cols={2}>
  <Card title="Conversations Resource Guide" icon="microphone" href="/guides/resources/conversations">
    Learn how conversations are run and analysed
  </Card>

  <Card title="Agents Resource Guide" icon="robot" href="/guides/resources/agents">
    Configure the agents that run them
  </Card>

  <Card title="Contacts Resource Guide" icon="user" href="/guides/resources/contacts">
    Manage the people you call
  </Card>

  <Card title="Billing Guide" icon="credit-card" href="/guides/resources/billing">
    Understand call costs and limits
  </Card>

  <Card title="Webhooks Guide" icon="webhook" href="/guides/webhooks">
    Learn about webhook events and testing
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /conversations
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:
  /conversations:
    post:
      tags:
        - Conversations
      summary: Create conversation
      description: >-
        Creates a conversation with a contact, using an existing or inline
        contact/agent/job, scoped to the API key's company. Respects company
        billing limits and subscription plans. Set isTest=true to create a test
        conversation that completes immediately without consuming billing
        minutes.
      operationId: PublicInterviewsController_createInterview_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/PublicCreateInterviewDto'
      responses:
        '200':
          description: OK (legacy; prefer 201)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicInterviewDto'
        '201':
          description: Conversation created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicInterviewDto'
        '400':
          description: Bad Request - Validation error or invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingJobId:
                  summary: Missing jobId for a contact with several jobs
                  value:
                    statusCode: 400
                    message: >-
                      Job ID must be provided when candidate has multiple job
                      assignments
                    error: Bad Request
                xorViolation:
                  summary: XOR constraint violation (contactId vs contact)
                  value:
                    statusCode: 400
                    message: >-
                      Either contactId or contact must be provided, but not
                      both.
                    error: Bad Request
                jobXorViolation:
                  summary: XOR constraint violation (jobId vs job)
                  value:
                    statusCode: 400
                    message: >-
                      Either jobId or job must be provided, but not both. You
                      may also provide neither.
                    error: Bad Request
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingBearer:
                  summary: Missing Bearer token
                  value:
                    statusCode: 401
                    message: Unauthorized
                    error: Unauthorized
        '402':
          description: >-
            Payment Required - no active subscription, or the batch costs more
            than the balance allows. May carry a `billing` object with the
            required and available amounts; it is absent when no figures were
            reported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BillingRefusalResponse'
        '403':
          description: Forbidden - access denied. Billing refusals are 402, not 403.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not Found - Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            An inline `agent` carried a structurally invalid conversation flow.
            `errors` lists every problem found, each with the path of the block
            it is on. Other 422s on this route carry a message but no `errors`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlowValidationErrorResponse'
              examples:
                invalidFlow:
                  summary: The inline agent's flow is invalid
                  value:
                    statusCode: 422
                    message: Invalid conversation flow.
                    errors:
                      - code: LOOPING_QUESTION_INCOMPLETE
                        message: Each looping question needs a non-empty question.
                        path: sections[1].questions[0]
                    traceId: 6a707aee000000000c1e285eefed9980
        '429':
          description: Too Many Requests - Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearer: []
components:
  schemas:
    PublicCreateInterviewDto:
      type: object
      additionalProperties: false
      oneOf:
        - anyOf:
            - required:
                - contactId
            - required:
                - candidateId
          not:
            anyOf:
              - required:
                  - contact
              - required:
                  - candidate
        - anyOf:
            - required:
                - contact
            - required:
                - candidate
          not:
            anyOf:
              - required:
                  - contactId
              - required:
                  - candidateId
      allOf:
        - oneOf:
            - required:
                - agentId
              not:
                required:
                  - agent
            - required:
                - agent
              not:
                required:
                  - agentId
        - oneOf:
            - required:
                - jobId
              not:
                required:
                  - job
            - required:
                - job
              not:
                required:
                  - jobId
            - not:
                anyOf:
                  - required:
                      - jobId
                  - required:
                      - job
      properties:
        contactId:
          type: string
          description: >-
            Existing contact ID (XOR with contact). Must exist and belong to the
            same company as the API key; both are validated server-side.
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        candidateId:
          type: string
          description: >-
            Deprecated alias of `contactId`, kept for the life of v1. Sending
            both is allowed only when they hold the same value.
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
          deprecated: true
        jobId:
          type: string
          description: >-
            Job ID to associate the conversation with (XOR with job). Required
            when the contact has multiple job assignments and no job is
            provided, unless the agent's focus is job-optional (GENERIC,
            CUSTOM). Must belong to the API key's company. If the contact is not
            already assigned to this job, they will be automatically assigned.
            Rejected with 400 for custom agents (agents defined by a
            conversation `flow`), which are always jobless.
          example: 123e4567-e89b-12d3-a456-426614174000
          format: uuid
        job:
          description: >-
            Job for inline creation (XOR with jobId). When provided, a new job
            will be created and used for the conversation. Cannot be combined
            with jobId. Rejected with 400 for custom agents (agents defined by a
            conversation `flow`), which are always jobless.
          allOf:
            - $ref: '#/components/schemas/JobDto'
        contact:
          description: Contact for inline creation (XOR with contactId)
          allOf:
            - $ref: '#/components/schemas/ContactDto'
        candidate:
          description: >-
            Deprecated alias of `contact`, kept for the life of v1. Send one
            inline body, not both.
          allOf:
            - $ref: '#/components/schemas/ContactDto'
          deprecated: true
        agentId:
          type: string
          description: >-
            Existing agent ID (XOR with agent). Must exist and belong to the
            same company as the API key. Existence/company relationship is
            validated server-side.
          format: uuid
          example: 987e6543-e21b-12d3-a456-426614174000
        agent:
          description: Agent for inline creation (XOR with agentId)
          allOf:
            - $ref: '#/components/schemas/AgentDto'
        scheduleTime:
          type: string
          description: >-
            Scheduled time in ISO 8601 format (max 30 days in future). Used
            exactly as sent, including when it falls outside the agent's calling
            window: naming a time overrides those hours. Omit it and the call is
            placed inside the window instead. The offset only pins the instant,
            so 14:00:00Z and 16:00:00+02:00 behave identically.
          example: '2025-11-20T10:00:00Z'
          format: date-time
        metadata:
          type: object
          description: >-
            Custom metadata for the conversation (max 10KB, 5 levels deep, 50
            keys)
          example:
            externalInterviewId: INT-789
            interviewerNotes: Focus on technical skills
        isTest:
          type: boolean
          description: >-
            If true, creates a test conversation that completes immediately
            without consuming billing minutes. Triggers webhook events
            (conversation.completed, analysis.completed — interview.completed on
            a LEGACY-vocabulary webhook) for testing webhook handlers.
          example: false
          default: false
    PublicInterviewDto:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Conversation ID
          example: 123e4567-e89b-12d3-a456-426614174000
        contactId:
          type: string
          format: uuid
          description: Contact ID
          example: 987e6543-e21b-12d3-a456-426614174000
        candidateId:
          type: string
          format: uuid
          description: >-
            Deprecated alias of `contactId`, always identical to it. Kept for
            the life of v1 so existing integrations keep working; new callers
            should read `contactId`.
          example: 987e6543-e21b-12d3-a456-426614174000
          deprecated: true
        agentId:
          type: string
          description: Agent ID
          format: uuid
          example: 456e7890-e12b-34d5-a678-901234567890
        jobId:
          type: string
          description: Job ID associated with this conversation, if any
          format: uuid
          nullable: true
          example: 123e4567-e89b-12d3-a456-426614174000
        runId:
          type: string
          description: >-
            The run that produced this conversation, or `null` for one created
            on its own. A run mints one conversation per contact, so this is how
            a conversation is traced back to the batch it came from without
            listing the run.
          format: uuid
          nullable: true
          example: 789e0123-e45b-67d8-a901-234567890123
        status:
          type: string
          description: Conversation status
          enum:
            - UNDEFINED
            - SCHEDULED
            - CANCELLED
            - FAILED
            - COMPLETED
            - IN_PROGRESS
            - UNREACHABLE
          example: SCHEDULED
        scheduledAt:
          type: string
          format: date-time
          description: Scheduled time
          example: '2025-11-20T10:00:00Z'
        durationMinutes:
          type: number
          description: Conversation duration in minutes
          example: 15
        finishedDate:
          type: string
          format: date-time
          description: Finished date
          example: '2025-11-20T10:15:00Z'
        metadata:
          type: object
          description: Custom metadata
          example:
            externalInterviewId: INT-789
        callAttempts:
          description: >-
            Call attempt logs for this conversation. Present for PHONE
            conversations, typically empty or undefined for ONLINE ones.
          type: array
          items:
            $ref: '#/components/schemas/PublicCallAttemptDto'
        analysis:
          description: >-
            Conversation analysis data. Present only if analysis has been
            generated.
          allOf:
            - $ref: '#/components/schemas/PublicConversationAnalysisDto'
        analytics:
          allOf:
            - $ref: '#/components/schemas/ConversationAnalytics'
          description: >-
            What the agent's own analytics configuration produced on this call:
            extracted fields, match score, outcomes and tags. Present only for a
            custom agent whose after-call analysis has run.
        createdAt:
          type: string
          description: Created timestamp
          example: '2024-11-16T10:30:00Z'
        updatedAt:
          type: string
          description: Updated timestamp
          example: '2024-11-16T10:30:00Z'
        isTest:
          type: boolean
          description: >-
            Whether this is a test conversation. A test conversation is created
            with isTest=true, completes immediately without consuming billing
            minutes, and is excluded from billing and usage summaries. Use them
            to exercise webhook handlers and integration flows.
          example: false
      required:
        - id
        - contactId
        - candidateId
        - agentId
        - status
        - scheduledAt
        - createdAt
        - updatedAt
    ErrorResponse:
      type: object
      properties:
        statusCode:
          type: integer
          example: 400
        message:
          type: string
          example: Validation failed
        error:
          type: string
          example: Bad Request
      required:
        - statusCode
        - message
    BillingRefusalResponse:
      type: object
      description: >-
        A 402 refusal. `billing` is present when the billing system reported
        figures, and absent when it had none to report - treat an absent object
        as unknown rather than as zero.
      properties:
        statusCode:
          type: integer
          example: 402
        message:
          type: string
          example: >-
            Insufficient minutes available to schedule 10 interviews (50 minutes
            total). Please purchase additional minutes.
        error:
          type: string
          example: PAYMENT_REQUIRED
        billing:
          type: object
          description: >-
            The shortfall, in the company's own billing unit. Read
            `billingSystem` first: a minutes-based company carries
            `requiredMinutes` and `availableMinutes`, a credits-based one
            `requiredCredits` and `availableCredits`, matching the split `GET
            /billing/usage` returns.
          properties:
            billingSystem:
              type: string
              enum:
                - minutes
                - credits
              example: minutes
            requiredMinutes:
              type: number
              description: >-
                Minutes the request would cost. Present only for a minutes-based
                company.
              example: 50
            availableMinutes:
              type: number
              description: >-
                Minutes the company has left. Present only for a minutes-based
                company.
              example: 12
            requiredCredits:
              type: number
              description: >-
                Credits the request would cost. Present only for a credits-based
                company.
              example: 50
            availableCredits:
              type: number
              description: >-
                Credits the company has left. Present only for a credits-based
                company.
              example: 12
            suggestedAction:
              type: string
              enum:
                - purchase_minutes
                - upgrade_plan
                - increase_spending_cap
                - contact_support
              example: purchase_minutes
          required:
            - billingSystem
        traceId:
          type: string
          example: 4b1f0c9d2e6a47f8b3c5d7e9a1b2c3d4
      required:
        - statusCode
        - message
    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
    JobDto:
      type: object
      additionalProperties: false
      allOf:
        - $ref: '#/components/schemas/PublicCreateJobDto'
      description: Job for inline creation, used when creating a conversation
    ContactDto:
      type: object
      additionalProperties: false
      properties:
        firstName:
          type: string
          description: The contact's first name
          example: John
          minLength: 1
          maxLength: 100
        lastName:
          type: string
          description: The contact's last name
          example: Doe
          minLength: 1
          maxLength: 100
        email:
          type: string
          description: The contact's email address
          example: john.doe@example.com
        phoneNumber:
          type: string
          description: The contact's phone number in E.164 format (must start with +)
          example: '+421915123456'
          pattern: ^\+[1-9]\d{1,14}$
        jobId:
          type: string
          description: >-
            Optional Job ID to associate the contact with. Must belong to the
            same company as the API key. Note: When ContactDto is used inline
            within conversation creation, this field is ignored; use the
            top-level jobId/job instead.
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        gdprExpiryDate:
          type: string
          description: GDPR expiry date in ISO 8601 format (must be in the future)
          example: '2026-11-16'
          format: date
        gender:
          type: string
          description: >-
            The contact's gender. Used for gender-aware addressing on the call.
            If not provided, it is auto-detected from the name.
          enum:
            - male
            - female
          example: female
        workHistory:
          type: array
          description: Optional work history items for the contact.
          maxItems: 20
          items:
            $ref: '#/components/schemas/PublicCandidateWorkHistoryItemDto'
      required:
        - firstName
        - lastName
        - gdprExpiryDate
    AgentDto:
      type: object
      properties:
        name:
          type: string
          description: Agent name
          example: Senior Developer Interview
          minLength: 2
          maxLength: 100
        type:
          type: string
          description: Type of the agent
          enum:
            - UNDEFINED
            - ONLINE
            - PHONE
          example: ONLINE
        focus:
          type: string
          description: >-
            Focus of the agent. GENERIC: job-optional, for a conversation that
            is not about a particular job. SCREENING: a hiring screen against a
            job's requirements. OUTREACH: a first call to gauge interest.
            LANGUAGE_TEST: language proficiency. Required unless a conversation
            `flow` is provided: an agent with a flow is a custom agent, whose
            focus is derived rather than selected. Sending both is rejected.
          enum:
            - GENERIC
            - SCREENING
            - OUTREACH
            - LANGUAGE_TEST
          example: SCREENING
        language:
          type: string
          description: Language of the conversation
          enum:
            - UNDEFINED
            - EN
            - JA
            - ZH
            - DE
            - HI
            - FR
            - KO
            - PT
            - IT
            - ES
            - ID
            - NL
            - TR
            - FIL
            - PL
            - SV
            - BG
            - RO
            - AR
            - CS
            - EL
            - FI
            - HR
            - MS
            - SK
            - DA
            - TA
            - UK
            - RU
            - HU
            - 'NO'
            - VI
          example: EN
        duration:
          type: number
          description: Duration of the conversation in minutes
          example: 30
          minimum: 1
          maximum: 180
        questions:
          description: List of questions for the conversation
          example:
            - What is your experience with React?
            - Tell me about a challenging project you worked on
          type: array
          items:
            type: string
        instructions:
          type: string
          description: Additional instructions for the conversation
          example: Focus on technical skills and previous project experience
        voiceId:
          type: string
          description: Voice ID the agent speaks with
          enum:
            - ALEX
            - PETER
            - MIRIAM
            - SUE
            - VIERA
            - CASANDRA
            - SILVIA
            - MICHAEL
            - LUKE
            - EMMA
            - SARAH
            - EVA
        companyPhoneNumberId:
          type: string
          description: >-
            ID of the company phone number assignment to use for calls from this
            agent (CompanyPhoneNumber.id). Required when type=PHONE.
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        cefrLevel:
          type: string
          description: CEFR level for language test agents
          enum:
            - A1
            - A2
            - B1
            - B2
            - C1
            - C2
          example: B1
        flow:
          allOf:
            - $ref: '#/components/schemas/ConversationFlow'
          description: >-
            Conversation flow graph. Providing it makes this inline agent a
            custom agent, so `focus` must be omitted. Inline creation mints a
            permanent agent per conversation — when the same flow will call more
            than one person, create it once via POST /agents and pass `agentId`
            instead.
        guardrails:
          allOf:
            - $ref: '#/components/schemas/AgentGuardrailsConfig'
          description: >-
            Rules the agent must obey during the call. Accepted only alongside a
            `flow` — sending it for an agent that will not be custom is
            rejected.
        contextConfig:
          allOf:
            - $ref: '#/components/schemas/AgentContextConfig'
          description: >-
            Who the agent is and what the call is about. Accepted only alongside
            a `flow` — sending it for an agent that will not be custom is
            rejected.
        analyticsConfig:
          allOf:
            - $ref: '#/components/schemas/AgentAnalyticsConfig'
          description: >-
            What to extract, score, decide and label after every call. Accepted
            only alongside a `flow` — sending it for an agent that will not be
            custom is rejected. Per-question scoring is set inline on the flow's
            looping questions, not here.
        callConfig:
          allOf:
            - $ref: '#/components/schemas/AgentCallConfig'
          description: >-
            How persistently and when the agent calls (custom agents only).
            Omitted, the agent calls Mon–Fri 09:00–18:00 in each contact's own
            timezone.
        overrides:
          allOf:
            - $ref: '#/components/schemas/AgentOverrides'
          description: >-
            Who this agent says it is on the call. By default it introduces
            itself as the company your API key belongs to; send this to have it
            speak for one of your own customers instead.
      allOf:
        - oneOf:
            - properties:
                type:
                  enum:
                    - PHONE
              required:
                - companyPhoneNumberId
            - properties:
                type:
                  not:
                    enum:
                      - PHONE
      required:
        - name
        - type
        - language
        - duration
    PublicCallAttemptDto:
      type: object
      properties:
        id:
          type: string
          description: Call attempt ID
          example: c9c9a6e8-fb4a-45f3-80fc-bf94f071cd2a
        direction:
          type: string
          description: Call direction (inbound / outbound)
          enum:
            - UNDEFINED
            - INBOUND
            - OUTBOUND
          example: OUTBOUND
        status:
          type: string
          description: Current status of the call attempt
          enum:
            - UNDEFINED
            - SCHEDULED
            - IN_PROGRESS
            - COMPLETED
            - NO_ANSWER
            - BUSY
            - FAILED
            - CANCELLED
            - VOICEMAIL
            - REJECTED
            - NO_CONSENT
            - TIMEOUT
            - MAX_ATTEMPTS_REACHED
            - HUMAN_CONTACT_REQUESTED
            - OTHER_ROLE_INTEREST
            - POLITE_DECLINE
            - TIMING_ISSUE
          example: COMPLETED
        reasonCode:
          type: string
          description: Machine-readable reason code for failures or special outcomes
          example: NO_ANSWER
        notes:
          type: string
          description: Human-readable notes associated with this call attempt
          example: Candidate did not pick up, mailbox full
        retryNumber:
          type: number
          description: >-
            Retry number for this attempt (starting from 0 or 1 depending on
            implementation)
          example: 2
        recordingUrl:
          type: string
          description: URL to the call recording, if available
          example: https://audio.example.com/recording.mp3
        duration:
          type: number
          description: Duration of the call attempt in seconds
          example: 180
        scheduledAt:
          type: string
          format: date-time
          description: Scheduled time for this call attempt (ISO 8601, UTC)
          example: '2025-07-22T10:00:00Z'
        calledAt:
          type: string
          format: date-time
          description: Actual time when the call started (ISO 8601, UTC)
          example: '2025-07-22T10:01:00Z'
        transcript:
          description: >-
            Transcript segments for this call attempt, ordered chronologically.
            Each segment contains the speaker, text content, and timing. Omitted
            when the conversation has no transcript or the call attempt did not
            produce one.
          type: array
          items:
            $ref: '#/components/schemas/PublicTranscriptSegmentDto'
      required:
        - id
        - direction
    PublicConversationAnalysisDto:
      type: object
      properties:
        id:
          type: string
          description: Analysis ID
          example: 123e4567-e89b-12d3-a456-426614174000
        general:
          description: General analysis data
          allOf:
            - $ref: '#/components/schemas/PublicGeneralAnalysisDto'
        specific:
          type: object
          description: >-
            Specific analysis data, depending on the agent's focus (e.g.
            language test scores, outreach feedback)
          example:
            pronunciationScores:
              accuracy: 90
              fluency: 85
              prosody: 82
            grammar:
              level: C1
      required:
        - id
        - general
    ConversationAnalytics:
      type: object
      description: >-
        What the agent's own `analyticsConfig` produced on this call. Present
        only for a custom agent whose after-call analysis has run. Distinct from
        `analysis`, which is the recruiting pipeline's output.


        Each slice is written by its own after-call job and each job self-gates
        on the agent's config, so an absent field means “not configured, or not
        run yet”.
      required:
        - matchScore
        - updatedAt
      properties:
        matchScore:
          type: number
          description: The weighted overall match score. Null until scoring has run.
          nullable: true
          minimum: 0
          maximum: 100
          example: 72
        fields:
          type: array
          description: The agent's extraction targets, resolved against this call.
          items:
            $ref: '#/components/schemas/AnalyticsFieldResult'
        scoring:
          allOf:
            - $ref: '#/components/schemas/AnalyticsScoring'
          description: How the match score was made up.
        outcomes:
          type: array
          description: The agent's outcomes, decided for this call.
          items:
            $ref: '#/components/schemas/AnalyticsOutcomeResult'
        tags:
          type: array
          description: The agent's output tags, assigned or not.
          items:
            $ref: '#/components/schemas/AnalyticsTagResult'
        qa:
          type: array
          description: The flow's looping questions, answered from the transcript.
          items:
            $ref: '#/components/schemas/AnalyticsQaEntry'
        evaluation:
          allOf:
            - $ref: '#/components/schemas/AnalyticsEvaluation'
          description: Purpose-driven evaluation of the call.
        sentiment:
          allOf:
            - $ref: '#/components/schemas/AnalyticsSentiment'
          description: Overall tone of the call.
        summary:
          type: string
          description: Generic call summary.
          example: Qualified lead; demo booked for Thursday.
        updatedAt:
          type: string
          format: date-time
          description: When these analytics were last written (UTC).
          example: '2026-08-03T10:30:00Z'
    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
    PublicCreateJobDto:
      type: object
      properties:
        title:
          type: string
          description: Job title
          example: Senior TypeScript Developer
          minLength: 5
          maxLength: 200
        description:
          type: string
          description: Job description
          example: We are looking for an experienced TypeScript developer...
          minLength: 10
          maxLength: 5000
        jobUrl:
          type: string
          description: Job URL
          example: https://company.com/careers/senior-typescript-dev
        benefits:
          type: string
          description: Benefits
          example: Health insurance, remote work, flexible hours
        requiredSkills:
          description: Required skills
          example:
            - TypeScript
            - React
            - Node.js
          maxItems: 10
          type: array
          items:
            type: string
        niceToHaveSkills:
          description: Nice-to-have skills
          example:
            - Docker
            - AWS
            - GraphQL
          maxItems: 10
          type: array
          items:
            type: string
        languageRequirements:
          description: Language requirements with proficiency levels
          example:
            - language: EN
              proficiency: B2
            - language: SK
              proficiency: C1
          maxItems: 5
          type: array
          items:
            $ref: '#/components/schemas/PublicLanguageRequirementDto'
        education:
          type: array
          description: Education requirements
          example:
            - BACHELOR_LEVEL
            - MASTER_LEVEL
          items:
            type: string
            enum:
              - UNDEFINED
              - PRIMARY_EDUCATION
              - SECONDARY_SCHOOL_STUDENT
              - SECONDARY_WITHOUT_DIPLOMA
              - SECONDARY_WITH_DIPLOMA
              - POST_SECONDARY_VOCATIONAL
              - UNIVERSITY_STUDENT
              - BACHELOR_LEVEL
              - MASTER_LEVEL
              - POSTGRADUATE
        experience:
          type: string
          description: Experience level required
          enum:
            - UNDEFINED
            - NO_EXPERIENCE
            - LESS_THAN_1_YEAR
            - ONE_TO_3_YEARS
            - THREE_TO_5_YEARS
            - FIVE_TO_10_YEARS
            - TEN_TO_15_YEARS
            - MORE_THAN_15_YEARS
          example: FIVE_TO_10_YEARS
        contractType:
          type: string
          description: Contract type
          enum:
            - UNDEFINED
            - FULL_TIME
            - PART_TIME
            - FREELANCE_CONTRACT
            - TRADE_LICENSE
            - INTERNSHIP
          example: FULL_TIME
        status:
          type: string
          description: Job status
          enum:
            - UNDEFINED
            - OPEN
            - CLOSED
          example: OPEN
          default: OPEN
        location:
          description: Job location
          allOf:
            - $ref: '#/components/schemas/PublicJobLocationDto'
        salary:
          description: >-
            Salary range with comprehensive validation: min and max must be >=
            0, min < max, currency and period are required if min or max is
            provided, at least one of min or max must be provided if any salary
            field is present
          example:
            min: 50000
            max: 80000
            currency: USD
            period: YEARLY
          allOf:
            - $ref: '#/components/schemas/PublicSalaryRangeDto'
        metadata:
          type: object
          description: Custom metadata for extensibility (max 10KB, 5 levels deep, 50 keys)
          example:
            externalJobId: JOB-12345
            department: Engineering
            hiringManager: Jane Doe
      required:
        - title
    PublicCandidateWorkHistoryItemDto:
      type: object
      properties:
        id:
          type: string
          description: Work history item ID
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        companyName:
          type: string
          description: Name of the employer company
          example: Google
          minLength: 1
          maxLength: 255
        candidatePosition:
          type: string
          description: >-
            The role held at that company. Keeps its original field name, which
            is the name the API accepts.
          example: Software Engineer
          minLength: 1
          maxLength: 255
        referenceName:
          type: string
          description: Name of the reference person
          example: Jane Smith
          maxLength: 255
        referencePhone:
          type: string
          description: Phone number of reference contact in E.164 format
          example: '+1987654321'
          pattern: ^\+[1-9]\d{1,14}$
        startDate:
          type: string
          description: Job start date in calendar format (yyyy-MM-dd)
          example: '2020-01-01'
          format: date
          pattern: ^\d{4}-\d{2}-\d{2}$
        endDate:
          type: string
          description: Job end date in calendar format (yyyy-MM-dd)
          example: '2022-12-31'
          format: date
          pattern: ^\d{4}-\d{2}-\d{2}$
      required:
        - companyName
        - candidatePosition
        - referencePhone
    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
        useContactContext:
          type: boolean
          description: >-
            Give the agent what is known about the person it is calling. A short
            platform-decided set of fields goes into its prompt; everything else
            the contact has a value for, plus their documents, becomes a
            knowledge base it can query mid-call. The agent may refer to any of
            it, must not volunteer where it came from, and is told not to read a
            document out word for word. Off unless set. Fields marked `isPii`
            are excluded from both halves unless `allowPiiInContext` is set on
            that field, or the field is one the platform lists in
            `inEssentialPromptSet` — see `GET /contact-fields`.
          default: false
          example: true
    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”.
    AgentCallConfig:
      type: object
      description: >-
        How persistently and when a custom agent calls. Custom agents only —
        sending it without a `flow` is rejected.


        Omitted on create, a new agent calls Mon–Fri 09:00–18:00 in each
        contact's own timezone, three attempts each; sending only `retryPolicy`
        on create gets that same window, since there is no stored window to
        keep. On update the object is **merged**: `retryPolicy` and `callWindow`
        are each replaced when sent and left alone when omitted, so changing the
        attempt count does not mean restating the window. Unlike the other
        config fields it is **not nullable** — every agent dials on some
        schedule, so there is no "no policy" state to clear to.


        The legacy `business_hours` preset cannot be set. It exists only to hold
        agents created before calling windows were configurable at the behaviour
        they already had — Mon–Fri 08:00–20:00 in the contact's own timezone,
        which is what their phone number's settings enforced before the window
        moved onto the agent. Sending a `callWindow` is how such an agent is
        moved onto a real one.
      properties:
        retryPolicy:
          allOf:
            - $ref: '#/components/schemas/AgentRetryPolicy'
          description: >-
            How many times a contact who does not answer is called. Replaced
            when sent, kept when omitted.
        callWindow:
          allOf:
            - $ref: '#/components/schemas/AgentCallWindow'
          description: >-
            The days, local hours and timezone the agent may dial in. Replaced
            whole when sent, kept when omitted.
    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.
    PublicTranscriptSegmentDto:
      type: object
      description: >-
        A single segment of a call attempt transcript: one utterance, by either
        the agent or the contact.
      properties:
        speaker:
          type: string
          description: Who spoke this segment
          enum:
            - AI
            - HUMAN
          example: AI
        text:
          type: string
          description: Transcribed text of the utterance
          example: Hello, thank you for taking the time to speak with me today.
        startTime:
          type: number
          description: Start time of the segment in seconds from the start of the recording
          example: 0
          minimum: 0
        endTime:
          type: number
          description: End time of the segment in seconds from the start of the recording
          example: 3.5
          minimum: 0
        language:
          type: string
          description: BCP-47 language code detected for this segment (e.g. 'en', 'sk')
          example: en
          pattern: ^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$
      required:
        - speaker
        - text
        - startTime
        - endTime
    PublicGeneralAnalysisDto:
      type: object
      properties:
        overallRating:
          type: number
          nullable: true
          description: >-
            Overall rating (0-100). Null when scoring did not complete — see
            scoringIncompleteReason. A null rating is never a low one: it means
            no score was produced, not that the candidate scored badly.
          example: 85
        scoringIncompleteReason:
          type: string
          description: >-
            Why no overall rating was produced. Absent on a complete analysis.
            'model_unavailable' means the scoring model could not be reached and
            the analysis is queued to be re-run; 'model_error' means it answered
            unusably and the analysis will not be retried on its own.
          enum:
            - model_unavailable
            - model_error
          example: model_unavailable
        companyFitRating:
          type: string
          description: Company fit rating
          enum:
            - UNDEFINED
            - LOW
            - MEDIUM
            - HIGH
          example: HIGH
        education:
          type: string
          description: Summary of education analysis
          example: Master's degree in Computer Science
        experience:
          type: string
          description: Summary of experience analysis
          example: 5 years of experience in software development
        strongPoints:
          description: Strong points and advantages found in the call
          example:
            - Excellent communication skills
            - Strong technical expertise in React and Node.js
            - Proven track record of leading teams
          type: array
          items:
            type: string
        weakPoints:
          description: Weak points, or areas for improvement, found in the call
          example:
            - Limited experience with microservices
            - Needs improvement in system design
          type: array
          items:
            type: string
        evaluation:
          description: Overall evaluation summary and recommendations
          example:
            - Strong candidate with relevant experience
            - Good cultural fit for the team
            - Recommended for next round
          type: array
          items:
            type: string
        status:
          type: number
          description: Analysis processing status
          enum:
            - 1
            - 2
            - 3
            - 4
          example: 3
        createdAt:
          type: string
          description: Created timestamp (UTC)
          example: '2024-11-16T10:30:00Z'
          format: date-time
        updatedAt:
          type: string
          description: Updated timestamp (UTC)
          example: '2024-11-16T10:30:00Z'
          format: date-time
      required:
        - createdAt
        - updatedAt
    AnalyticsFieldResult:
      type: object
      description: One extraction target, resolved against the call.
      required:
        - key
        - label
        - type
        - value
      properties:
        key:
          type: string
          description: The key configured on the agent.
          example: budget_confirmed
        label:
          type: string
          example: Budget confirmed
        type:
          type: string
          enum:
            - string
            - number
            - bool
            - enum
        value:
          description: >-
            The extracted value, coerced to `type`. `null` when the call did not
            surface it — which is distinct from a `false` or empty value that
            WAS surfaced.
          anyOf:
            - type: string
              nullable: true
            - type: number
              nullable: true
            - type: boolean
              nullable: true
          example: true
        confidence:
          type: number
          minimum: 0
          maximum: 1
          description: Model confidence, 0..1.
        evidence:
          type: string
          description: Short transcript-grounded justification.
          example: Said they have signed off on 15k for this quarter
    AnalyticsScoring:
      type: object
      description: The overall match score and what made it up.
      required:
        - total
        - items
      properties:
        total:
          type: number
          minimum: 0
          maximum: 100
          description: The weighted overall match score.
          example: 72
        items:
          type: array
          description: One entry per scored item.
          items:
            $ref: '#/components/schemas/AnalyticsScoreItem'
    AnalyticsOutcomeResult:
      type: object
      description: One of the agent's outcomes, decided for this call.
      required:
        - key
        - label
        - met
      properties:
        key:
          type: string
          description: The key configured on the agent.
          example: meeting_booked
        label:
          type: string
          example: Meeting booked
        met:
          type: boolean
          description: Whether the call achieved it.
          example: true
        confidence:
          type: number
          minimum: 0
          maximum: 1
        evidence:
          type: string
          description: Short transcript-grounded justification.
    AnalyticsTagResult:
      type: object
      description: One of the agent's output tags, assigned or not.
      required:
        - key
        - label
        - applied
      properties:
        key:
          type: string
          description: The key configured on the agent.
          example: needs_followup
        label:
          type: string
          example: Needs follow-up
        applied:
          type: boolean
          description: Whether the AI applied it to this call.
          example: true
        reason:
          type: string
          description: Why.
    AnalyticsQaEntry:
      type: object
      description: One of the flow's looping questions, answered from the transcript.
      required:
        - id
        - question
        - answered
        - answer
      properties:
        id:
          type: string
          format: uuid
          description: >-
            Id of the flow's looping question — the same id the agent read
            returns.
        question:
          type: string
          description: The question, as designed.
        answered:
          type: boolean
          description: >-
            Whether the call actually covered it. Distinguishes “not discussed”
            from an empty answer.
          example: true
        answer:
          type: string
          description: The answer as given on the call; empty when not answered.
        evidence:
          type: string
          description: Short transcript-grounded justification.
    AnalyticsEvaluation:
      type: object
      description: >-
        Purpose-driven evaluation of the call, driven by the agent's design
        rather than a job spec.
      required:
        - strengths
        - concerns
        - objections
        - assessment
      properties:
        strengths:
          type: array
          description: What went well.
          items:
            type: string
        concerns:
          type: array
          description: What went poorly, and the risks.
          items:
            type: string
        objections:
          type: array
          description: Objections or hesitations the contact raised.
          items:
            type: string
        assessment:
          type: array
          description: Overall assessment, a few holistic lines.
          items:
            type: string
    AnalyticsSentiment:
      type: object
      description: Overall tone of the call and how it moved.
      required:
        - overall
      properties:
        overall:
          type: string
          enum:
            - positive
            - neutral
            - negative
          example: positive
        score:
          type: number
          minimum: 0
          maximum: 1
          description: 0 (very negative) .. 1 (very positive).
        trajectory:
          type: string
          enum:
            - improving
            - declining
            - steady
            - mixed
        notes:
          type: string
          description: One-line note explaining the read.
    PublicLanguageRequirementDto:
      type: object
      properties:
        language:
          type: string
          description: Language
          enum:
            - UNDEFINED
            - EN
            - JA
            - ZH
            - DE
            - HI
            - FR
            - KO
            - PT
            - IT
            - ES
            - ID
            - NL
            - TR
            - FIL
            - PL
            - SV
            - BG
            - RO
            - AR
            - CS
            - EL
            - FI
            - HR
            - MS
            - SK
            - DA
            - TA
            - UK
            - RU
            - HU
            - 'NO'
            - VI
          example: EN
        proficiency:
          type: string
          description: Proficiency level (CEFR)
          enum:
            - UNDEFINED
            - A1
            - A2
            - B1
            - B2
            - C1
            - C2
          example: B2
      required:
        - language
        - proficiency
    PublicJobLocationDto:
      type: object
      properties:
        workMode:
          type: string
          description: Work mode
          enum:
            - UNDEFINED
            - ONSITE
            - REMOTE
            - HYBRID
          example: REMOTE
        street:
          type: string
          description: Street address
          example: Hlavná 123
          minLength: 2
          maxLength: 200
        city:
          type: string
          description: City
          example: Bratislava
          minLength: 2
          maxLength: 100
        postalCode:
          type: string
          description: Postal code
          example: '81101'
          minLength: 2
          maxLength: 20
        countryCode:
          type: string
          description: Country code (ISO 3166-1 alpha-2)
          enum:
            - XX
            - AF
            - AL
            - DZ
            - AS
            - AD
            - AO
            - AI
            - AQ
            - AG
            - AR
            - AM
            - AW
            - AU
            - AT
            - AZ
            - BS
            - BH
            - BD
            - BB
            - BY
            - BE
            - BZ
            - BJ
            - BM
            - BT
            - BO
            - BA
            - BW
            - BR
            - IO
            - BN
            - BG
            - BF
            - BI
            - KH
            - CM
            - CA
            - CV
            - KY
            - CF
            - TD
            - CL
            - CN
            - CX
            - CC
            - CO
            - KM
            - CG
            - CD
            - CK
            - CR
            - CI
            - HR
            - CU
            - CY
            - CZ
            - DK
            - DJ
            - DM
            - DO
            - EC
            - EG
            - SV
            - GQ
            - ER
            - EE
            - SZ
            - ET
            - FK
            - FO
            - FJ
            - FI
            - FR
            - GF
            - PF
            - TF
            - GA
            - GM
            - GE
            - DE
            - GH
            - GI
            - GR
            - GL
            - GD
            - GP
            - GU
            - GT
            - GG
            - GN
            - GW
            - GY
            - HT
            - HM
            - VA
            - HN
            - HK
            - HU
            - IS
            - IN
            - ID
            - IR
            - IQ
            - IE
            - IM
            - IL
            - IT
            - JM
            - JP
            - JE
            - JO
            - KZ
            - KE
            - KI
            - KP
            - KR
            - KW
            - KG
            - LA
            - LV
            - LB
            - LS
            - LR
            - LY
            - LI
            - LT
            - LU
            - MO
            - MG
            - MW
            - MY
            - MV
            - ML
            - MT
            - MH
            - MQ
            - MR
            - MU
            - YT
            - MX
            - FM
            - MD
            - MC
            - MN
            - ME
            - MS
            - MA
            - MZ
            - MM
            - NA
            - NR
            - NP
            - NL
            - NC
            - NZ
            - NI
            - NE
            - NG
            - NU
            - NF
            - MK
            - MP
            - 'NO'
            - OM
            - PK
            - PW
            - PS
            - PA
            - PG
            - PY
            - PE
            - PH
            - PN
            - PL
            - PT
            - PR
            - QA
            - RE
            - RO
            - RU
            - RW
            - BL
            - SH
            - KN
            - LC
            - MF
            - PM
            - VC
            - WS
            - SM
            - ST
            - SA
            - SN
            - RS
            - SC
            - SL
            - SG
            - SX
            - SK
            - SI
            - SB
            - SO
            - ZA
            - GS
            - SS
            - ES
            - LK
            - SD
            - SR
            - SJ
            - SE
            - CH
            - SY
            - TW
            - TJ
            - TZ
            - TH
            - TL
            - TG
            - TK
            - TO
            - TT
            - TN
            - TR
            - TM
            - TC
            - TV
            - UG
            - UA
            - AE
            - GB
            - US
            - UM
            - UY
            - UZ
            - VU
            - VE
            - VN
            - VG
            - VI
            - WF
            - EH
            - YE
            - ZM
            - ZW
          example: SK
    PublicSalaryRangeDto:
      type: object
      properties:
        min:
          type: number
          description: Minimum salary (must be greater than or equal to 0)
          example: 50000
          minimum: 0
        max:
          type: number
          description: Maximum salary (must be greater than or equal to 0)
          example: 80000
          minimum: 0
        currency:
          type: string
          description: Currency (ISO 4217 alpha-3)
          pattern: ^[A-Z]{3}$
          example: USD
        period:
          type: string
          description: Salary period
          enum:
            - UNDEFINED
            - HOURLY
            - DAILY
            - MONTHLY
            - YEARLY
          example: YEARLY
    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.
    AgentRetryPolicy:
      type: object
      description: How persistently an unanswered contact is called.
      properties:
        maxAttempts:
          type: number
          description: >-
            Attempts per contact, counting the first. Reschedules the contact
            asks for come out of this budget; a transient network failure that
            never reached them does not.
          minimum: 1
          maximum: 5
          example: 3
      required:
        - maxAttempts
    AgentCallWindow:
      type: object
      description: >-
        When the agent may dial. Sent whole: `days`, `start`, `end` and
        `timezoneAnchor` together, since half a window is how an agent ends up
        calling at an hour nobody chose.
      properties:
        days:
          type: array
          description: Days the agent may call on.
          items:
            type: string
            enum:
              - mon
              - tue
              - wed
              - thu
              - fri
              - sat
              - sun
          example:
            - mon
            - tue
            - wed
            - thu
            - fri
        start:
          type: string
          description: >-
            Earliest local time the agent may call, `"HH:mm"` on a 24-hour
            clock.
          example: '09:00'
        end:
          type: string
          description: >-
            Local time the window closes, `"HH:mm"` on a 24-hour clock,
            exclusive. Must be later in the day than `start`; a window that
            wraps past midnight is rejected rather than reinterpreted.
          example: '18:00'
        timezoneAnchor:
          allOf:
            - $ref: '#/components/schemas/AgentCallWindowTimezoneAnchor'
          description: Whose clock `start` and `end` are on.
      required:
        - days
        - start
        - end
        - timezoneAnchor
    AnalyticsScoreItem:
      type: object
      description: One item contributing to the match score.
      required:
        - key
        - kind
        - label
        - score
      properties:
        key:
          type: string
          description: >-
            The extraction target's or criterion's key, or the flow question's
            id.
          example: budget_confirmed
        kind:
          type: string
          description: What kind of thing was scored.
          enum:
            - field
            - question
            - criterion
          example: field
        label:
          type: string
          description: The field/criterion label, or the question itself.
          example: Budget confirmed
        score:
          type: number
          minimum: 0
          maximum: 100
          description: How well this item scored.
          example: 80
        weightPercent:
          type: number
          description: >-
            Share of the overall match score this item accounts for, in percent.
            Computed from the configured weights (which are relative, not a
            budget), so the shares across all items sum to 100.
          minimum: 0
          maximum: 100
          example: 33.3
        importance:
          type: string
          enum:
            - REQUIRED
            - PREFERRED
        reasoning:
          type: string
          description: Why it scored that way.
    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 conversation 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.
    AgentCallWindowTimezoneAnchor:
      type: object
      description: >-
        Whose clock the calling window's `start` and `end` are on. This is what
        decides where those hours actually fall.
      properties:
        mode:
          type: string
          enum:
            - contact
            - fixed
          description: >-
            `contact` detects the contact's timezone from their phone number,
            down to the area code: on one list a `+1 212` number is called at
            09:00 in New York and a `+1 310` number at 09:00 in Los Angeles.
            Where a prefix covers more than one zone (an Australian `+61 8`
            number could be Adelaide or Perth) the window is narrowed to the
            part valid in every contact's zone, so nobody is called too early.
            `fixed` puts every contact on one named zone instead. Because every
            candidate zone has to allow the time, a narrow window over a wide
            prefix can have no valid moment at all (`09:00`–`10:00` is never
            true in both Adelaide and Perth at once); such a contact is called
            anyway rather than left uncalled, and the call is recorded as having
            gone out outside the window.
          example: contact
        timezone:
          type: string
          description: >-
            IANA timezone. Required when `mode` is `fixed`, ignored otherwise.
            Validated against the runtime's own timezone database, so a
            nonexistent zone is a `422` here rather than a schedule that never
            fires.
          example: Europe/Bratislava
        fallbackTimezone:
          type: string
          description: >-
            IANA timezone used when `mode` is `contact` and the contact's number
            cannot be resolved to one. Defaults to `Europe/Bratislava`.
          example: Europe/Bratislava
      required:
        - mode
    FlowLoopingQuestion:
      type: object
      required:
        - question
      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. Optional,
            and it is the scoring switch: a question without one is still asked
            but never scored, which is how you write a question set that has no
            right answers (an internal survey, a satisfaction check).
          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, optionally 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
      description: >-
        One route out of a `conditional`. Every entry in `branches` must be an
        object; anything else (a `null` left by a client that built the array by
        index, say) is rejected with `422` (`CONDITIONAL_BRANCH_NOT_AN_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.


        Required on every branch, and it must match one of the two shapes below
        — it is what the agent routes on. A missing or non-object condition is
        rejected with `422` (`CONDITIONAL_CONDITION_MISSING`), a `type` that is
        neither `intent` nor `expression` with
        `CONDITIONAL_CONDITION_TYPE_UNKNOWN`, and a condition of a known type
        missing the fields that type needs with `CONDITIONAL_CONDITION_INVALID`.
        Each carries the `path` of the offending branch, e.g.
        `sections[0].branches[1].condition`.


        The branch nominated by `defaultBranch` is checked like any other. Its
        condition is not read while it is the fallthrough, but moving
        `defaultBranch` to a different branch later would put it back in charge
        of a route.
      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. Must be non-empty: it is the whole instruction
            the agent reads this route off, so a blank one is rejected with
            `422` (`CONDITIONAL_CONDITION_INVALID`) rather than stored as a
            route nothing selects. Whitespace does not count as content, which
            is what the pattern says and what the validator checks.
          minLength: 1
          pattern: \S
          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. Must be non-empty, whitespace
            excluded (`CONDITIONAL_CONDITION_INVALID`).
          minLength: 1
          pattern: \S
          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 — omitting it for any other operator is rejected
            with `422` (`CONDITIONAL_CONDITION_INVALID`), since there is then
            nothing to compare against. 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

````