공통 스키마와 데이터 타입¶
이 페이지는 기준 커밋의 request/VO와 공통 response 클래스로 확인되는 와이어 필드만 정리합니다. 필드가 없는 곳에 편의를 위한 값을 추가하지 마십시오. 도메인 의미와 공유 DB 계약은 공유 데이터 계약, 인증 값은 로그인과 토큰 인증, credential 경계는 API key와 client-secret을 참고하십시오.
JSON과 시간 표기¶
- JSON 필드명은 lower camelCase입니다.
- UUID는 하이픈을 포함한 표준 문자열입니다.
LocalDateTime응답은 offset 없는 ISO-8601 문자열입니다.- client-secret의
expiresAt만 UTC ISO-8601 instant 문자열(...Z)로 계약되어 있습니다. - 응답 직렬화에서
null필드는 생략될 수 있습니다. 호출자는 명시적null과 필드 부재를 동일한 nullable 상태로 처리해야 합니다.
공통 성공 envelope¶
데이터 단건¶
| 필드 | 타입 | 의미 |
|---|---|---|
status |
integer | 공통 성공 객체의 값은 200 |
message |
string | "success" |
data |
endpoint별 | 단건, 객체 배열 또는 map |
실제 HTTP status가 201이어도 body의 status는 200일 수 있습니다.
데이터 없는 성공¶
페이지 없는 목록¶
count는 list의 현재 항목 수입니다. 빈 목록은 count: 0입니다.
페이지 요청과 응답¶
요청 query:
| 필드 | 타입 | 기본 | 정규화 |
|---|---|---|---|
page |
integer | 1 | 1 미만이면 1 |
size |
integer | 20 | 1 미만이면 1, 최대 10,000 |
사용자별 대화 통계는 추가로 size를 최대 1,000으로 제한합니다. 정렬은 endpoint가
고정하며 공개 sort query 계약이 아닙니다.
{
"status": 200,
"message": "success",
"page": {
"page": 1,
"size": 20,
"totalItems": 42,
"totalPages": 3,
"hasNext": true,
"hasPrevious": false,
"first": true,
"last": false
},
"count": 20,
"list": []
}
| 필드 | 타입 | 의미 |
|---|---|---|
page.page |
integer | 1부터 시작하는 현재 페이지 |
page.size |
integer | 적용된 페이지 크기 |
page.totalItems |
integer | 전체 검색 결과 수 |
page.totalPages |
integer | 전체 페이지 수 |
page.hasNext |
boolean | 다음 페이지 존재 |
page.hasPrevious |
boolean | 이전 페이지 존재 |
page.first |
boolean | 첫 페이지 |
page.last |
boolean | 마지막 페이지 |
count |
integer | 현재 list의 항목 수 |
list |
array | endpoint별 항목 |
오류 envelope¶
{
"status": 409,
"message": "요청을 처리할 수 없습니다.",
"data": {
"code": "VOICE_PRESET_INACTIVE",
"voicePreset": "female_warm"
}
}
| 필드 | 타입 | 필수 | 의미 |
|---|---|---|---|
status |
integer | 예 | HTTP 상태 |
message |
string | 조건부 | 지역화된 오류 메시지 |
data |
any | 아니요 | 기계 판독 코드나 관련 값 |
모든 오류가 data.code를 보장하지는 않습니다. 명시된 코드형 오류만 코드로 분기해야 합니다.
DB 제약명, SQL, driver 원문과 민감한 값은 응답 계약이 아닙니다.
식별자¶
| 이름 | 타입 | 출처·범위 |
|---|---|---|
| member ID | UUID | access JWT sub; 회원 응답에는 별도 ID 필드가 없음 |
workspaceId |
UUID | X-Workspace-Id, API key 귀속 값, client-secret claim |
scenario.id |
UUID | 워크스페이스 일반 시나리오 PK |
scenario.slug |
string | 워크스페이스 안에서 활성 유일 |
template id |
UUID | 템플릿 등록 응답에만 노출 |
template slug |
string | 전역 활성 템플릿 조회 키 |
historyId |
UUID | 시나리오 이력 PK |
dialogId |
UUID | 공유 DB 대화 PK |
sessionId |
string | Socket Backend 세션 식별자; UUID 타입으로 가정하지 않음 |
messageId |
UUID | 대화 메시지 PK |
groupId |
UUID | 사용자 그룹 PK |
그룹 memberId |
UUID | 그룹 구성원 레코드 PK; 회원 PK나 externalUserId가 아님 |
externalUserId |
string | 외부 시스템 사용자 식별자 |
API key id |
UUID | API key 레코드 및 감사 주체 식별자 |
jti |
UUID 문자열 | JWT별 랜덤 식별자 |
다른 워크스페이스의 UUID를 지정하면 많은 조회가 403이 아니라 404를 반환합니다. 이는 존재 여부를 숨기는 계약입니다.
Credential 스키마¶
로그인 토큰 응답¶
{
"tokenType": "Bearer",
"accessToken": "<access-jwt>",
"refreshToken": "<refresh-jwt>",
"expiresIn": 1800
}
| 필드 | 타입 | 의미 |
|---|---|---|
tokenType |
string | "Bearer" |
accessToken |
string | App Backend REST용 HMAC access JWT |
refreshToken |
string | 토큰 회전 전용 HMAC refresh JWT |
expiresIn |
integer | access token의 초 단위 TTL |
API key 발급·목록¶
발급 data:
| 필드 | 타입 | 의미 |
|---|---|---|
id |
UUID | API key PK |
name |
string | 요청한 표시명 |
key |
string | 원본 키, 발급 응답에서만 1회 노출 |
keyPrefix |
string | 이후 식별용 표시 접두 |
keyType |
string | LIVE 또는 TEST |
createdAt |
LocalDateTime | 발급 시각 |
목록 data[]:
| 필드 | 타입 | nullable | 의미 |
|---|---|---|---|
id |
UUID | 아니요 | API key PK |
keyName |
string | 아니요 | 표시명 |
keyPrefix |
string | 아니요 | 표시 접두 |
keyType |
string | 아니요 | LIVE/TEST |
keyStatus |
string | 아니요 | ACTIVE/REVOKED/ROTATED |
lastUsedAt |
LocalDateTime | 예 | 마지막 사용 시각 |
createdAt |
LocalDateTime | 아니요 | 발급 시각 |
목록에는 key와 secretHash가 없습니다.
Ed25519 client-secret 발급 응답¶
{
"clientSecret": {
"value": "<ed25519-client-secret-jwt>",
"expiresInSeconds": 1800,
"expiresAt": "2026-09-23T01:30:00Z"
}
}
| 필드 | 타입 | 의미 |
|---|---|---|
clientSecret.value |
string | Ed25519/EdDSA 서명 JWT |
clientSecret.expiresInSeconds |
integer | clamp 후 실제 TTL |
clientSecret.expiresAt |
string | UTC ISO-8601 만료 instant |
JOSE header:
Payload claim:
| claim | 타입 | 값·의미 |
|---|---|---|
iss |
string | sohri-app-backend |
aud |
string | sohri-realtime; 배열이 아닌 단일 문자열 |
sub |
string | 발급 회원 UUID |
workspaceId |
string | 대상 워크스페이스 UUID |
jti |
string | 랜덤 UUID |
iat |
integer | epoch seconds |
exp |
integer | epoch seconds, iat + 실제 TTL |
apiKeyId claim은 없습니다. client-secret은 access JWT와 다른 키 체계이며 API key도
아닙니다. App Backend는 값을 저장하거나 사용 횟수를 추적하지 않으므로 만료 전 재사용
가능합니다.
시나리오 스키마¶
일반 시나리오 요청¶
{
"slug": "health-check",
"title": "건강 체크",
"language": "ko",
"content": "{\"metadata\":{},\"graph\":{}}",
"isTemplate": false
}
| 필드 | 타입 | nullable | 의미 |
|---|---|---|---|
slug |
string | 아니요 | 워크스페이스 조회 키 |
title |
string | 예 | 표시 제목 |
language |
string | 예 | 언어 코드 |
content |
string | 아니요 | 시나리오 전체 JSON을 담은 문자열 |
isTemplate |
boolean | 예 | 전역 템플릿 기반 여부의 Boolean provenance |
content 자체가 객체가 아니라 JSON 문자열인 점에 유의하십시오. 일반 POST는 이 문자열을
Graph Builder에서 검증하지 않습니다.
일반 시나리오 상세¶
| 필드 | 타입 | nullable |
|---|---|---|
id |
UUID | 아니요 |
slug |
string | 아니요 |
title |
string | 예 |
language |
string | 예 |
status |
string | 아니요 |
isTemplate |
boolean | 아니요 |
scenarioVersion |
integer | 아니요 |
content |
string | 아니요 |
createdAt |
LocalDateTime | 아니요 |
updatedAt |
LocalDateTime | 아니요 |
목록 항목은 여기서 isTemplate, content, updatedAt을 제외합니다.
시나리오 이력¶
목록 항목:
| 필드 | 타입 |
|---|---|
id |
UUID |
scenarioId |
UUID |
historyType |
string (I/U/D) |
scenarioStatus |
string |
title |
string, nullable |
language |
string, nullable |
scenarioVersion |
integer |
createdAt |
LocalDateTime |
updatedAt |
LocalDateTime |
상세는 위 필드에 content: string을 추가합니다. 현재 이력 응답에는 isTemplate 필드가
없습니다.
전역 템플릿 요청과 content¶
{
"slug": "senior-care",
"name": "시니어 케어",
"description": null,
"scenarioContent": {
"metadata": {
"slug": "senior-care",
"name": "시니어 케어",
"description": null
},
"prompts": {},
"graph": {}
}
}
| 필드 | 타입 | 필수 |
|---|---|---|
slug |
string | 예 |
name |
string | 예 |
description |
string 또는 null | 아니요 |
scenarioContent |
object | 예 |
scenarioContent에서 App Backend가 정의하는 최상위 구조:
| 필드 | 타입 | 필수 | 제약 |
|---|---|---|---|
metadata |
object | 예 | slug, name, 선택 description을 포함하며 상위 값과 일치 |
prompts |
object | 예 | 하위 shape는 App Backend가 정의하지 않음 |
graph |
object | 예 | 하위 shape는 App Backend가 정의하지 않음 |
slots |
object 또는 null | 아니요 | 값이 있으면 객체 |
이 네 필드 외의 하위 graph/node/prompt 필드는 App Backend VO나 검증기가 계약하지 않으므로 이 문서에서 임의로 열거하지 않습니다. 전체 객체는 UTF-8 직렬화 기준 1 MiB 이하입니다.
등록 응답은 id, slug, name, description, scenarioContent, createdAt, updatedAt입니다.
목록 항목은 slug, name, description, updatedAt만 가집니다.
대화 목록 항목¶
| 필드 | 타입 | nullable | 의미 |
|---|---|---|---|
dialogId |
UUID | 아니요 | 대화 PK |
scenarioId |
UUID | 예 | 연결 시나리오 |
sessionId |
string | 아니요 | Socket 세션 ID |
turnCount |
integer | 아니요 | 대화 턴 수 |
currentRiskLevel |
string | 예 | 현재 위기 단계 |
sourceType |
string | 아니요 | 대화 출처 |
sttProvider |
string | 아니요 | 저장된 STT provider |
startedAt |
LocalDateTime | 예 | 시작 시각 |
endedAt |
LocalDateTime | 예 | 종료 시각 |
createdAt |
LocalDateTime | 아니요 | 생성 시각 |
대화 상세¶
최상위 data:
| 필드 | 타입 | nullable |
|---|---|---|
dialogId |
UUID | 아니요 |
sessionId |
string | 아니요 |
summary |
DialogSummary |
예 |
biomarkerSummary |
BiomarkerSummary |
예 |
messages |
DialogMessage[] |
아니요 |
DialogSummary:
| 필드 | 타입 |
|---|---|
dialogSummary |
string |
dialogKeyword |
string |
dialogEmotion |
string |
BiomarkerSummary:
| 필드 | 타입 |
|---|---|
summary |
string |
대화 메시지와 분석¶
DialogMessage:
| 필드 | 타입 | nullable | 제공 조건 |
|---|---|---|---|
messageId |
UUID | 아니요 | 항상 |
messageTurns |
integer | 아니요 | 항상; 연속 번호 보장 없음 |
speakerType |
string | 아니요 | USER 또는 BOT |
sttText |
string | 아니요 | 항상 |
confidenceScore |
number | 예 | 저장 값이 있을 때 |
audio |
Audio |
예 | 아카이브 음성 행이 있을 때 |
createdAt |
LocalDateTime | 아니요 | 항상 |
keywords |
Keyword[] |
아니요 | 없으면 빈 배열 |
emotion |
Emotion |
예 | 분석이 있을 때 |
dementia |
Dementia |
예 | detail=true이고 분석이 있을 때 |
depression |
Depression |
예 | detail=true이고 분석이 있을 때 |
biomarker |
Biomarker |
예 | detail=true이고 분석이 있을 때 |
Audio:
| 필드 | 타입 | nullable |
|---|---|---|
url |
string | 아니요 |
format |
string | 예 |
duration |
number | 예 |
rate |
integer | 예 |
size |
integer | 예 |
url은 인증이 필요한 App Backend 프록시 절대 URL이며 서명 URL이 아닙니다.
Keyword:
| 필드 | 타입 |
|---|---|
keyword |
string |
riskCategory |
string |
detectedAt |
LocalDateTime |
Emotion:
| 필드 | 타입 | nullable | 조건 |
|---|---|---|---|
bestEmotion |
string | 아니요 | 감정 분석이 있을 때 |
detail |
EmotionDetail |
예 | detail=true |
EmotionDetail은 sadness, neutral, happiness, angry integer 점수를 가집니다.
Dementia는 control, dementia integer 점수를 가집니다.
Depression은 negative, positive integer 점수를 가집니다.
Biomarker:
| 필드 | 타입 |
|---|---|
metrics |
BiomarkerMetric[] |
BiomarkerMetric:
| 필드 | 타입 |
|---|---|
key |
string |
name |
string |
unit |
string |
level |
string |
value |
decimal number |
분석 점수의 범위나 riskCategory, level, status의 전체 enum은 현재 응답 VO가 제한하지
않으므로 이 문서에서 추가 enum을 만들지 않습니다.
통계 shape¶
전체 통계 data:
사용자별 통계 목록 항목:
| 필드 | 타입 | nullable |
|---|---|---|
userId |
string | 아니요 |
userName |
string | 예 |
oneMinuteOrLongerCount |
integer | 아니요 |