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

# API로 플로우 작성 및 검증

> v3 API에서 flow 필드로 플로우 에이전트 그래프를 작성·검증하고 기존 flow_data에서 마이그레이션하는 방법을 안내합니다.

# API로 플로우 작성 및 검증

v3 API에서 플로우 에이전트를 만들거나 수정할 때는 `flow` 필드를 사용합니다.
`flow`는 외부 통합을 위한 공개 그래프 계약입니다.

<Warning>
  `flow_data`는 기존 빌더 호환용 필드입니다. 새 통합은 `flow`를 사용하세요.
  `flow`와 `flow_data`를 함께 보내면 `flow`가 우선합니다.
</Warning>

## 필드 선택

| 필드          | 상태       | 사용 시점                                  |
| ----------- | -------- | -------------------------------------- |
| `flow`      | 권장       | 새 플로우 생성, 전체 그래프 교체, API·SDK·MCP 기반 작성 |
| `flow_data` | 지원 종료 예정 | 기존 빌더 형태를 이미 저장하거나 읽는 통합의 호환 유지        |
| 둘 다 생략      | 허용       | `type="flow"` 생성 시 기본 그래프를 서버가 만듭니다    |
| 둘 다 전송      | 허용       | `flow`만 처리하고 `flow_data`는 검증 전에 무시합니다  |

`flow`와 `flow_data`는 모두 전체 그래프 교체입니다. 일부 필드만 보내서 그래프를
부분 수정하지 않습니다.

<Note>
  `PATCH /v3/agents/{agent_id}`에서 현재 그래프를 유지하려면 `flow` 필드를
  생략하세요. `flow: null`은 명시적으로 거부됩니다.
</Note>

## 그래프 구조

`flow`는 `nodes`와 `edges`로 구성됩니다.

| 항목                           | 필수 값 | 설명                                                                 |
| ---------------------------- | ---- | ------------------------------------------------------------------ |
| `nodes[].id`                 | 필수   | 그래프 안에서 유일한 노드 ID입니다                                               |
| `nodes[].type`               | 필수   | `begin`, `conversation`, `condition`, `api`, `endCall` 같은 노드 타입입니다 |
| `nodes[].position`           | 필수   | 캔버스 좌표입니다. 서버가 좌표를 만들지 않습니다                                        |
| `nodes[].data`               | 선택   | 노드별 설정입니다. 라우팅 키는 `edges`로 표현합니다                                   |
| `edges[].source`             | 필수   | 출발 노드 ID입니다                                                        |
| `edges[].target`             | 필수   | 도착 노드 ID입니다                                                        |
| `edges[].condition`          | 필수   | `ai`, `logic`, `fallback` 중 하나입니다                                  |
| `edges[].skip_user_response` | 선택   | 사용자 응답을 기다리지 않고 다음 노드로 이동할지 정합니다                                   |

<Note>
  `nodes[].data`에 `transitions`, `logicalTransitions`, `logical_transitions`,
  `globalNodeSettings`, `global_node_settings`를 넣지 마세요. 이 값은 기존 빌더
  내부 키입니다. `flow`에서는 `edges`와 `global_node_setting`으로 표현합니다.
</Note>

## 노드별 data 스키마

`flow.nodes`는 `BeginFlowNode`, `ConversationFlowNode` 같은 노드 타입별
스키마로 나뉩니다. `nodes[].data`도 노드 타입별 엄격한 스키마를 따릅니다.
문서에 없는 키를 보내면 `FLOW_V2_INVALID`로 거부됩니다.

작성할 때는 `nodes[].type`에 맞는 v2 data 스키마를 참고하세요. 예를 들어
`begin`은 `BeginFlowNodeData`, `conversation`은 `ConversationFlowNodeData`,
`api`는 `ApiFlowNodeData`, `transferCall`은 `TransferCallFlowNodeData`를
사용합니다.

[스키마 레지스트리](/api-reference/v3/introduction)에서도 같은 계약을 확인할 수 있습니다.

| 스키마                                   | 용도                          |
| ------------------------------------- | --------------------------- |
| `/v3/schemas/flow-schema/flow-data`   | 전체 `flow` 그래프 스키마입니다        |
| `/v3/schemas/flow-schema/node-{type}` | 해당 노드의 v2 래퍼와 `data` 스키마입니다 |

| 위치                                           | 이름 규칙                                                                                                               |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `flow.nodes`, `flow.edges`, `edge.condition` | v3 API 기본 규칙인 snake\_case를 사용합니다                                                                                    |
| `edges[].skip_user_response`                 | 기존 `isSkipUserResponse` 전환을 `edges` 필드로 올린 값입니다                                                                     |
| `nodes[].data.global_node_setting`           | 기존 `globalNodeSettings`를 대체하는 v2 글로벌 노드 표시입니다. `conversation`, `sendSms`, `endCall`에서만 허용됩니다                        |
| `nodes[].data`의 일반 노드 설정                     | snake\_case 키를 사용합니다. 예: `first_line_type`, `prompt_type`, `tool_id`, `api_configuration`, `transfer_configuration` |

v2 `flow`는 라우팅과 글로벌 설정을 노드 밖으로 옮겼습니다. 기존 빌더(`flow_data`)에서
`nodes[].data` 안에 두던 키는 v2에서 위치가 아래처럼 바뀝니다.

| 기존 빌더(`flow_data`)의 `node.data`                    | v2 `flow`에서의 표현                             |
| -------------------------------------------------- | ------------------------------------------- |
| `transitions`                                      | `edges` (분기 조건은 `edge.condition`)           |
| `logicalTransitions` / `logical_transitions`       | condition 노드 분기 → `edge.condition`의 `logic` |
| `globalNodeSettings` / `global_node_settings` (복수) | `nodes[].data.global_node_setting` (단수 표시)  |

따라서 `nodes[].data`에는 위 빌더 내부 키를 넣지 않습니다. 넣으면 저장이 거부됩니다.

각 노드 타입의 정확한 `data` 필드 목록은 `/v3/schemas/flow-schema/node-{type}`에서
확인하세요. 노드별로 알아둘 점:

* `prompt_type`/`prompt`/`static_sentence` (사전 멘트 모드 none/static/dynamic)는
  `conversation`, `api`, `tool`, `sendSms`, `transferCall`, `endCall`에서 받습니다.
  `transferCall`의 warm 위스퍼 멘트는 별도 필드
  (`warm_transfer_prompt`, `warm_transfer_static_sentence`)입니다.
* `transferAgent`는 `agent`, `preserve_chat_context`만 받습니다. `prompt`는 없습니다.
* `conversation`의 지식 베이스는 `knowledge.rag_enabled`와 `knowledge.knowledge_ids`로
  설정합니다. 최상위 `knowledge_ids`는 받지 않습니다.
* `extraction`의 추출 프롬프트는 `extraction_configuration.extraction_prompt`에
  적습니다. 최상위 `prompt`는 받지 않습니다.
* `note`는 에디터 주석 노드입니다. `content`, `width`, `height`만 받고
  `name`은 받지 않으며, note로 향하거나 note에서 나가는 전환은 저장이 거부됩니다.

## 생성 예시

```bash theme={null}
curl https://client-api.tryvox.co/v3/agents \
  -H "Authorization: Bearer $VOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "예약 확인 플로우",
    "type": "flow",
    "flow": {
      "nodes": [
        {
          "id": "begin",
          "type": "begin",
          "position": { "x": 0, "y": 0 },
          "data": {
            "name": "시작",
            "first_line_type": "aiFirst"
          }
        },
        {
          "id": "confirm",
          "type": "conversation",
          "position": { "x": 360, "y": 0 },
          "data": {
            "name": "예약 확인",
            "prompt_type": "dynamic",
            "prompt": "예약 정보를 확인하고 변경이 필요한지 물어보세요."
          }
        },
        {
          "id": "end",
          "type": "endCall",
          "position": { "x": 720, "y": 0 },
          "data": {
            "name": "종료",
            "prompt_type": "static",
            "static_sentence": "확인했습니다. 감사합니다."
          }
        }
      ],
      "edges": [
        {
          "source": "begin",
          "target": "confirm",
          "condition": { "type": "fallback" }
        },
        {
          "source": "confirm",
          "target": "end",
          "condition": {
            "type": "ai",
            "prompt": "사용자가 예약 확인을 마쳤을 때"
          }
        }
      ]
    }
  }'
```

## 전환 조건

`edge.condition`은 `type`으로 구분합니다.

<Tabs>
  <Tab title="ai">
    자연어 조건입니다. 사용자의 발화와 현재 대화 맥락을 보고 다음 노드로 이동할지
    판단합니다.

    ```json theme={null}
    {
      "type": "ai",
      "prompt": "사용자가 상담원 연결을 요청했을 때"
    }
    ```
  </Tab>

  <Tab title="logic">
    조건 노드에서 변수 값을 비교할 때 사용합니다. `equations`는 하나 이상이어야
    합니다.

    ```json theme={null}
    {
      "type": "logic",
      "operator": "&&",
      "equations": [
        {
          "left": "reservation_status",
          "operator": "equals",
          "right": "confirmed"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="fallback">
    같은 source 노드의 다른 조건이 맞지 않을 때 이동하는 기본 경로입니다.

    ```json theme={null}
    {
      "type": "fallback"
    }
    ```
  </Tab>
</Tabs>

## 글로벌 노드

글로벌 노드는 전환이 아니라 노드에 표시합니다.
`node.data.global_node_setting`이 있으면 글로벌 노드로 처리합니다.

`global_node_setting`은 `conversation`, `sendSms`, `endCall` 노드에서만
허용됩니다. 다른 노드 타입에 넣으면 저장이 거부됩니다(웹 빌더도 이 세 타입에서만
글로벌 노드를 지원합니다).

```json theme={null}
{
  "id": "human-help",
  "type": "conversation",
  "position": { "x": 360, "y": 240 },
  "data": {
    "name": "상담원 연결 안내",
    "prompt_type": "dynamic",
    "prompt": "상담원 연결 절차를 안내하세요.",
    "global_node_setting": {
      "condition": {
        "type": "ai",
        "prompt": "사용자가 사람 상담원을 원할 때"
      }
    }
  }
}
```

글로벌 노드의 조건은 `ai`만 지원합니다. `logic`이나 `fallback`은 저장할 수
없습니다.

## 저장 검증

`flow` 저장은 자동 수정 없이 검증합니다. 불완전한 그래프를 서버가 추측해서
고치지 않습니다.

저장 전에 같은 `flow` 그래프를 서버에 저장하지 않고 검증하려면
`POST /v3/agents/validate-flow`를 호출합니다.

```bash theme={null}
curl "https://client-api.tryvox.co/v3/agents/validate-flow?level=all" \
  -H "Authorization: Bearer $VOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": {
      "nodes": [
        {
          "id": "begin",
          "type": "begin",
          "position": { "x": 0, "y": 0 },
          "data": {
            "name": "시작",
            "first_line_type": "aiFirst"
          }
        },
        {
          "id": "end",
          "type": "endCall",
          "position": { "x": 320, "y": 0 },
          "data": {
            "name": "종료"
          }
        }
      ],
      "edges": [
        {
          "source": "begin",
          "target": "end",
          "condition": { "type": "fallback" }
        }
      ]
    }
  }'
```

| 응답 필드        | 의미                                              |
| ------------ | ----------------------------------------------- |
| `valid`      | 저장을 막는 치명적 오류가 없으면 `true`입니다                    |
| `errors`     | 저장을 막는 치명적 오류 목록입니다                             |
| `advisories` | 저장은 가능하지만 실행 중 동작이 예상과 달라질 수 있는 런타임 주의 항목 목록입니다 |

`validate-flow`는 저장하지 않고 검증하므로 형식이 잘못된 `flow`도 일반 에러 응답 대신
`valid=false` 엔벨로프로 돌려줍니다. 예를 들어 `position` 누락, 잘못된
`condition.type`, 지원하지 않는 `logic` 연산자는 `errors[].code =
"flow_v2_schema_invalid"`로 반환됩니다.

`level` 쿼리로 응답에 포함할 항목 범위를 정할 수 있습니다.

| `level`    | 반환 내용                                             |
| ---------- | ------------------------------------------------- |
| `critical` | 기본값입니다. 저장을 막는 `errors`만 반환합니다                    |
| `runtime`  | `advisories`만 반환합니다. `valid`는 치명적 오류 여부를 계속 반영합니다 |
| `all`      | `errors`와 `advisories`를 모두 반환합니다                  |

치명적 오류가 있는 그래프라도 변환 가능한 경우에는 런타임 주의 항목을 함께
반환합니다. 그래서 `level=runtime`에서는 `errors`가 비어 있어도 `valid=false`일
수 있습니다. `level=all`에서는 저장을 막는 오류와 런타임 주의 항목을 함께
확인할 수 있습니다.

런타임 주의 항목에는 연결되지 않은 skip/fallback 전환이나 begin에서 도달 가능한
종료 노드가 없는 경우가 포함됩니다. 이런 항목은 저장을 막지 않지만, 실제 통화
실행 전에 수정하는 것이 좋습니다.

도구, 지식 베이스, 파일, 다른 에이전트 이관처럼 외부 리소스를 참조하는 필드는
값을 보냈다면 실제 존재 여부와 조직 접근 권한을 검증합니다. 비어 있는 참조는 작성
중인 초안으로 저장할 수 있지만, 잘못된 ID나 다른 조직의 ID는 저장이 거부됩니다.
런타임 주의 항목이 있는 그래프라도 이 참조 검증은 생략되지 않습니다.

| 상황                                                                                                          | 결과           |
| ----------------------------------------------------------------------------------------------------------- | ------------ |
| `nodes` 또는 `edges` 누락                                                                                       | 저장 거부        |
| `position` 누락                                                                                               | 저장 거부        |
| `begin.data.first_line_type` 누락                                                                             | 저장 거부        |
| `begin` 노드가 0개 또는 2개 이상                                                                                     | 저장 거부        |
| `begin` 노드에 `fallback` 전환 없음                                                                                | 저장 거부        |
| `begin`에서 `fallback`이 아닌 조건 사용                                                                              | 저장 거부        |
| `begin`에서 `skip_user_response=true` 사용                                                                      | 저장 거부        |
| 전환의 `source` 또는 `target`이 존재하지 않는 노드 ID                                                                     | 저장 거부        |
| 노드 ID 또는 전환 ID 중복                                                                                           | 저장 거부        |
| `logic` 전환이 조건 노드가 아닌 곳에서 출발                                                                                | 저장 거부        |
| `logic` 조건의 `equations`가 비어 있음                                                                              | 저장 거부        |
| 지원하지 않는 `logic` 연산자 사용                                                                                      | 저장 거부        |
| `source`별 `fallback` 전환이 2개 이상                                                                              | 저장 거부        |
| 같은 `source`에 같은 조건 전환이 2개 이상                                                                                | 저장 거부        |
| `global_node_setting`이 객체가 아니거나 비어 있거나 `ai` 조건이 아님                                                          | 저장 거부        |
| `conversation`/`sendSms`/`endCall`이 아닌 노드에 `global_node_setting` 포함                                         | 저장 거부        |
| `note` 노드에 연결된 전환(`source` 또는 `target`이 note)                                                               | 저장 거부        |
| `transitions`, `logicalTransitions`, `logical_transitions`, `globalNodeSettings`, `global_node_settings` 포함 | 저장 거부        |
| 문서화되지 않은 `nodes[].data` 최상위 키 포함                                                                            | 저장 거부        |
| 제공한 도구, 지식 베이스, 파일, 이관 대상 에이전트 참조가 없거나 접근 불가                                                                | 저장 거부        |
| `function` 또는 기존 `knowledge` 노드 포함                                                                          | `flow` 쓰기 거부 |

오래된 그래프를 조회하면 `flow`에 표시되지 않는 빌더 내부 분기가 있을 수
있습니다. 이런 그래프는 `flow`로 다시 저장할 때 거부될 수 있습니다. 기존 빌더
형태를 정리한 뒤 다시 저장하세요.

## 지원 종료 예정 경로

새 통합에서는 아래 경로를 사용하지 마세요.

* `flow_data`: 기존 빌더 그래프입니다. 읽기와 쓰기는 호환용으로만 유지됩니다.
* `POST /v3/agents/validate-flow-data`: `flow_data` 전용 검증입니다. `flow` 검증은 `POST /v3/agents/validate-flow`를 사용합니다.
* `POST /v3/agents/autofix-flow-data`: `flow_data` 보정 도구입니다.
* `POST /v3/flow-data/autofix`: `flow_data` 보정 도구입니다.
* `POST /v3/agents/{agent_id}/operations`: 기존 빌더 그래프를 diff 방식으로 수정합니다.
* `sourceHandle`, `targetHandle`, `transitions`, `logicalTransitions`, `globalNodeSettings`: `flow` 작성에는 사용하지 않습니다.

## 마이그레이션

<Steps>
  <Step title="조회 코드를 flow로 전환">
    `GET /v3/agents/{agent_id}` 응답에서 `flow_data` 대신 `flow`를 읽습니다.
  </Step>

  <Step title="작성 코드를 flow로 전환">
    `POST /v3/agents`와 `PATCH /v3/agents/{agent_id}`에서 `flow`를 보냅니다.
    `flow_data`는 보내지 않습니다.
  </Step>

  <Step title="검증 루틴 정리">
    `validate-flow-data`와 `autofix-flow-data` 호출을 제거합니다. 저장 전 검증이
    필요하면 `POST /v3/agents/validate-flow`를 사용합니다.
  </Step>

  <Step title="빌더 내부 키 제거">
    `sourceHandle`, `transitions`, `globalNodeSettings` 같은 내부 키를 제거하고
    `edges[].condition`과 `global_node_setting`으로 바꿉니다.
  </Step>

  <Step title="노드 data 키를 snake_case로 전환">
    `firstLineType`, `promptType`, `apiConfiguration` 같은 키를
    `first_line_type`, `prompt_type`, `api_configuration`으로 바꿉니다.
  </Step>
</Steps>

## LLM 작성 체크리스트

LLM이나 MCP 클라이언트가 `flow`를 직접 작성할 때는 아래 순서로 payload를
조립하세요.

1. `flow_data` 대신 `flow`만 사용합니다.
2. 모든 노드에 `id`, `type`, `position`을 넣습니다.
3. begin 노드에는 `data.first_line_type`을 넣고, begin에서 나가는 전환은 `fallback` 하나로 둡니다.
4. 전환은 `edges[].condition.type`을 `ai`, `logic`, `fallback` 중 하나로 표현합니다.
5. `logic` 조건은 조건 노드에서만 시작하게 하고, `equations`를 하나 이상 넣습니다.
6. `node.data`에는 snake\_case 키만 사용합니다.
7. `node.data`에는 `transitions`, `logicalTransitions`, `globalNodeSettings`를 넣지 않습니다.
8. 저장 전에 `POST /v3/agents/validate-flow?level=all`을 호출해 `errors`를 모두 해결합니다.

## 다음 단계

<CardGroup cols={2}>
  <Card title="전환 조건" icon="arrow-right-arrow-left" href="/docs/build/flow/transitions">
    대시보드에서 전환 조건을 설계하는 방법을 확인하세요.
  </Card>

  <Card title="v3 API 참조" icon="brackets-curly" href="/api-reference/v3/introduction">
    에이전트 생성과 수정 엔드포인트의 전체 스키마를 확인하세요.
  </Card>
</CardGroup>

***

<Accordion title="연관 검색어">
  flow, flow\_data deprecated, 플로우 API, 플로우 작성, 플로우 검증, validate-flow, edge condition, global\_node\_setting, sourceHandle, transitions, validate-flow-data, autofix-flow-data
</Accordion>
