콘텐츠로 이동

시나리오·Graph Builder API

시나리오에는 서로 다른 세 저장·검증 경로가 있습니다.

  1. 일반 시나리오 /v1/scenarios: 워크스페이스 소유, content는 JSON 문자열, 저장 전에 Graph Builder 검증을 하지 않습니다.
  2. 전역 템플릿 /v1/scenario-templates: 워크스페이스 비종속, scenarioContent는 JSON 객체, 등록 전에 Graph Builder/DM 런타임 검증을 수행합니다.
  3. 공개 기본·더미 시나리오: 레거시/개발 지원 경로이며 더미 검증을 명시적으로 요청할 수 있습니다.

공통 시나리오 shape는 스키마, 테넌트 규칙은 워크스페이스와 테넌시, 빠른 경로 색인은 REST API 참조를 참고하십시오.

일반 시나리오

모든 일반 시나리오와 이력 경로는 access JWT와 X-Workspace-Id가 필요합니다. 워크스페이스 API key로 호출할 수 없습니다.

POST /v1/scenarios

  • 인증·테넌트: access JWT + 헤더, ACTIVE 멤버
  • 성공: 새 slug는 201 Created, 기존 slug에 이력 추가는 200 OK
  • 핵심: Graph Builder 검증을 호출하지 않습니다.
필드 필수 제약·의미
slug 예 공백 불가, 최대 128자; (workspaceId, slug) 활성 유일
title 아니요 값이 있으면 최대 128자
language 아니요 값이 있으면 최대 8자
content 예 공백 불가 JSON 문자열. 이 API는 JSON 구조/그래프 유효성을 검사하지 않음
isTemplate 아니요 전역 템플릿 기반 생성 여부 Boolean provenance
{
  "slug": "health-check",
  "title": "건강 체크",
  "language": "ko",
  "content": "{\"metadata\":{\"category\":\"health\"},\"graph\":{}}",
  "isTemplate": false
}

새 slug이면 scenario와 초기 scenario_history 스냅샷을 생성합니다. isTemplate이 누락되거나 null이면 false로 저장합니다.

기존 slug이면 scenario row는 유지하고 DRAFT 이력을 append합니다. 이때 isTemplate이 누락되거나 null이면 기존 provenance를 유지하고, 명시값이 기존값과 다를 때만 409입니다. isTemplate은 특정 템플릿 ID나 버전을 가리키지 않는 Boolean 선언이며 최초 생성 후 바꿀 수 없습니다.

{
  "status": 200,
  "message": "success",
  "data": {
    "id": "01900000-0000-7000-8000-000000000401",
    "slug": "health-check",
    "title": "건강 체크",
    "language": "ko",
    "status": "DRAFT",
    "isTemplate": false,
    "scenarioVersion": 0,
    "content": "{\"metadata\":{\"category\":\"health\"},\"graph\":{}}",
    "createdAt": "2026-09-23T10:20:00",
    "updatedAt": "2026-09-23T10:20:00"
  }
}
  • 201: 새 scenario와 첫 이력 생성
  • 200: 같은 slug의 새 DRAFT 이력 append
  • 400: 헤더·본문·필드 길이·필수값 오류
  • 401: 인증 오류
  • 403: ACTIVE 멤버가 아님
  • 409: slug 경합/중복 또는 명시한 isTemplate이 기존 provenance와 불일치

일반 POST는 검증 API가 아닙니다

content가 JSON처럼 보이더라도 이 경로는 Graph Builder/DM validate를 호출하지 않습니다. 호출자가 사전에 검증해야 하는 정책이라면 별도 검증 단계를 구성해야 합니다.

GET /v1/scenarios

  • query: page 기본 1, size 기본 20
  • 범위: 현재 워크스페이스의 soft-delete되지 않은 시나리오
  • 정렬: createdAt DESC, 동률이면 id DESC
  • 성공: 200 OK
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "page": {
    "page": 1,
    "size": 20,
    "totalItems": 1,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "first": true,
    "last": true
  },
  "count": 1,
  "list": [
    {
      "id": "01900000-0000-7000-8000-000000000401",
      "slug": "health-check",
      "title": "건강 체크",
      "language": "ko",
      "status": "DRAFT",
      "scenarioVersion": 0,
      "createdAt": "2026-09-23T10:20:00"
    }
  ]
}
  • 200: 페이지 반환
  • 400: 헤더 또는 페이지 query 오류
  • 401/403: 인증 또는 멤버십 오류

목록 항목에는 content와 isTemplate이 없습니다.

GET /v1/scenarios/{id}

  • path id: 시나리오 UUID
  • 성공: 200 OK
  • 부수 효과: 없음

응답 data는 POST 응답과 같은 ScenarioDetail입니다.

  • 200: 상세 반환
  • 400: 헤더나 UUID 형식 오류
  • 401/403: 인증 또는 멤버십 오류
  • 404: 없거나 다른 워크스페이스 소속

시나리오 이력

GET /v1/scenarios/{scenarioId}/histories

  • path scenarioId: 현재 워크스페이스의 시나리오 UUID
  • query: page, size, 선택 scenarioStatus
  • scenarioStatus: DRAFT, NEEDS_REVIEW, PUBLISHED, ERROR 중 하나
  • 정렬: createdAt DESC, 동률이면 id DESC
  • 성공: 200 OK
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "page": {
    "page": 1,
    "size": 20,
    "totalItems": 1,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "first": true,
    "last": true
  },
  "count": 1,
  "list": [
    {
      "id": "01900000-0000-7000-8000-000000000402",
      "scenarioId": "01900000-0000-7000-8000-000000000401",
      "historyType": "I",
      "scenarioStatus": "DRAFT",
      "title": "건강 체크",
      "language": "ko",
      "scenarioVersion": 0,
      "createdAt": "2026-09-23T10:20:00",
      "updatedAt": "2026-09-23T10:20:00"
    }
  ]
}
  • 200: 이력 페이지
  • 400: UUID, enum, 페이지 또는 헤더 오류
  • 401/403: 인증 또는 멤버십 오류
  • 404: 시나리오가 없거나 다른 워크스페이스 소속

목록은 content를 포함하지 않습니다. DB 이력에는 생성 당시 provenance가 보존되지만 현재 이력 응답 VO는 isTemplate을 노출하지 않습니다.

GET /v1/scenarios/{scenarioId}/histories/{historyId}

  • 두 path 변수 모두 UUID
  • 성공: 200 OK
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "data": {
    "id": "01900000-0000-7000-8000-000000000402",
    "scenarioId": "01900000-0000-7000-8000-000000000401",
    "historyType": "I",
    "scenarioStatus": "DRAFT",
    "title": "건강 체크",
    "language": "ko",
    "scenarioVersion": 0,
    "content": "{\"metadata\":{\"category\":\"health\"},\"graph\":{}}",
    "createdAt": "2026-09-23T10:20:00",
    "updatedAt": "2026-09-23T10:20:00"
  }
}
  • 200: 상세 반환
  • 400: UUID 또는 헤더 오류
  • 401/403: 인증 또는 멤버십 오류
  • 404: 시나리오 또는 이력이 없거나 이력이 지정한 시나리오에 속하지 않음

이력 삭제 API는 없습니다.

전역 시나리오 템플릿

전역 템플릿은 워크스페이스와 독립된 마스터입니다. 구 /v1/template-scenarios 경로와 본문 단건 GET은 기준 커밋에 없으며 404입니다.

POST /v1/scenario-templates

  • 인증: ROLE_ADMIN access JWT만 가능
  • 테넌트: 헤더 불필요
  • 성공: 201 Created
  • 부수 효과: 구조 검증 → Graph Builder/DM validate → 성공 시 전역 템플릿 INSERT
필드 필수 제약
slug 예 공백 불가, 최대 128자, 앞뒤 공백 금지
name 예 공백 불가, 최대 128자, 앞뒤 공백 금지
description 아니요 null 가능, 최대 255자, 값이 있으면 앞뒤 공백 금지
scenarioContent 예 JSON 객체, 직렬화 UTF-8 1 MiB 이하

scenarioContent.metadata, prompts, graph는 JSON 객체로 필수이고 slots는 생략, null 또는 객체만 가능합니다. metadata.slug와 metadata.name은 비어 있지 않은 문자열로 상위 필드와 정확히 같아야 합니다. metadata.description도 상위 description과 null 여부와 문자열 값까지 정확히 같아야 합니다.

{
  "slug": "senior-care",
  "name": "시니어 케어",
  "description": "시니어 케어 기본 시나리오",
  "scenarioContent": {
    "metadata": {
      "slug": "senior-care",
      "name": "시니어 케어",
      "description": "시니어 케어 기본 시나리오"
    },
    "prompts": {},
    "graph": {},
    "slots": {}
  }
}

App Backend는 scenarioContent 전체를 Graph Builder/DM 검증 요청의 scenario_graph로 감싸 설정된 POST /{apiVersion}/session/validate로 전달합니다.

{
  "scenario_graph": {
    "metadata": {},
    "prompts": {},
    "graph": {}
  }
}

검증 성공은 HTTP 2xx이면서 응답의 status가 success, ok, valid 중 하나인 경우입니다. Graph Builder의 400/422는 그래프 오류로, 그 밖의 비정상 HTTP·통신 실패·빈 응답은 서비스 불가로 분류합니다. 검증은 DB 트랜잭션 전에 수행되고, 실패하면 아무 행도 저장하지 않습니다.

{
  "status": 200,
  "message": "success",
  "data": {
    "id": "01900000-0000-7000-8000-000000000501",
    "slug": "senior-care",
    "name": "시니어 케어",
    "description": "시니어 케어 기본 시나리오",
    "scenarioContent": {
      "metadata": {
        "slug": "senior-care",
        "name": "시니어 케어",
        "description": "시니어 케어 기본 시나리오"
      },
      "prompts": {},
      "graph": {},
      "slots": {}
    },
    "createdAt": "2026-09-23T10:30:00",
    "updatedAt": "2026-09-23T10:30:00"
  }
}
  • 201: 검증 후 등록 성공
  • 400: 필드·구조·크기·metadata 불일치 또는 Graph Builder가 보고한 그래프 오류
  • 401: 미인증
  • 403: 관리자 아님 또는 API key 사용
  • 409: 활성 전역 slug 중복
  • 503: Graph Builder/DM 타임아웃·연결 오류·빈/비정상 응답·서비스 장애

검증은 하지만 deploy하지 않습니다

이 POST는 Graph Builder/DM의 validate 경로만 호출합니다. deploy, publish 또는 세션 생성 호출은 하지 않습니다. 검증 성공과 런타임 배포 완료를 같은 상태로 해석하면 안 됩니다.

GET /v1/scenario-templates

  • 인증: access JWT 또는 워크스페이스 API key
  • 테넌트: 전역 마스터이므로 헤더 불필요
  • query: page, size
  • 정렬: slug 오름차순
  • 성공: 200 OK
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "page": {
    "page": 1,
    "size": 20,
    "totalItems": 1,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "first": true,
    "last": true
  },
  "count": 1,
  "list": [
    {
      "slug": "senior-care",
      "name": "시니어 케어",
      "description": "시니어 케어 기본 시나리오",
      "updatedAt": "2026-09-23T10:30:00"
    }
  ]
}

목록은 id, scenarioContent, createdAt을 반환하지 않습니다. 본문은 등록 성공 응답에서만 받을 수 있으며 단건 본문 조회·수정·삭제 API는 없습니다.

  • 200: 페이지 반환
  • 401: credential 오류
  • 403: 허용되지 않은 credential 권한

공개 기본 시나리오

GET /v1/base-scenarios/{language}

  • 인증·테넌트: 공개
  • path language: 언어 조회 키 문자열
  • 성공: 200 OK
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "data": {
    "metadata": {},
    "graph": {}
  }
}

data는 저장된 시나리오 그래프 객체를 그대로 반환하므로 고정 필드 스키마는 이 API가 정의하지 않습니다.

  • 200: 언어별 그래프 반환
  • 404: 해당 언어 시나리오 없음

공개 더미 시나리오

/v1/dummy-scenarios/**는 기준 커밋에서 조회뿐 아니라 mutation과 원격 검증까지 공개입니다. 운영 안전 기본값이 아니므로 보안과 현재 격차를 반드시 확인하십시오.

GET /v1/dummy-scenarios

  • 인증·테넌트: 공개
  • 성공: 200 OK
  • 응답: 페이지가 아닌 count/list; 목록 항목은 저장된 map projection이므로 고정 shape를 이 API VO가 선언하지 않음
  • 부수 효과: 없음

GET /v1/dummy-scenarios/{id}

  • path id: UUID
  • 성공 응답 data: id, title, content, createdAt
  • 404: 더미 시나리오 없음
  • 부수 효과: 없음
{
  "status": 200,
  "message": "success",
  "data": {
    "id": "01900000-0000-7000-8000-000000000601",
    "title": "검증용 시나리오",
    "content": "{\"metadata\":{},\"graph\":{}}",
    "createdAt": "2026-09-23T10:40:00"
  }
}

POST /v1/dummy-scenarios

PUT /v1/dummy-scenarios/{id}

두 경로의 본문은 같습니다.

필드 필수 제약
title 예 공백 불가
content 예 공백 불가 문자열
{
  "title": "검증용 시나리오",
  "content": "{\"metadata\":{},\"graph\":{}}"
}
  • POST 200: 새 더미 시나리오 저장
  • PUT 200: 지정한 UUID의 제목·내용 수정
  • 400: 필드 검증 또는 UUID 오류
  • 404: PUT 대상 없음

응답은 단건 GET과 같은 DummyScenario입니다. POST와 PUT 자체는 Graph Builder 검증을 호출하지 않습니다.

DELETE /v1/dummy-scenarios/{id}

  • 성공: 200 OK, {"status":200,"message":"success"}
  • 부수 효과: 지정한 더미 시나리오 삭제
  • 400: UUID 형식 오류

POST /v1/dummy-scenarios/validate

  • 인증·테넌트: 공개
  • 저장: 하지 않음
  • Graph Builder 요청: scenario_graph 객체를 validate 경로로 전달
  • 호환 입력: snake_case를 우선하고 camelCase도 허용
{
  "scenario_graph": {
    "metadata": {},
    "graph": {}
  }
}

또는:

{
  "scenarioGraph": {
    "metadata": {},
    "graph": {}
  }
}
{
  "status": 200,
  "message": "success",
  "data": {
    "valid": true,
    "message": "검증 성공"
  }
}
  • 200: 원격 검증 결과. 그래프가 유효하지 않으면 valid=false일 수 있음
  • 400: wrapper가 없거나 객체가 아니거나 빈 객체

이 경로도 validate만 수행하며 저장·deploy·publish를 하지 않습니다.