대화형 답변 생성 API (V2)
관련 문서
목적
이 API는 사용자 메시지를 전달하고, AI의 응답을 반환합니다.
v2 API는
session_id가 없으면 서버에서 새 세션을 발급할 수 있습니다.같은
session_id로 요청하면 이전 대화 흐름을 이어서 답변할 수 있습니다.응답에 포함되는
chat_log_uuid를 이용해, 해당 대화에 대해 도움이 됐어요 / 도움이 안 됐어요 피드백을 저장할 수 있습니다.1. 채팅 응답 생성 API
URL
HTTP 메서드
Plain Text
POST헤더
| 헤더 | 값 | 설명 |
|---|---|---|
| Content-Type | application/json | 요청 본문이 JSON 형식임을 나타냅니다. |
| Authorization | Bearer <api_key> | Sidetalk 대시보드에서 제공된 API 키를 사용하세요. |
본문
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| messages | Array | 예 | 메시지 배열입니다. 마지막 role: "user" 메시지만 실제 질문으로 사용합니다. |
| stream | Boolean | 아니요 | 응답을 스트림으로 받을지 여부입니다. 기본값은 false입니다. |
| session_id | String | 아니요 | 대화 세션 ID입니다. 없으면 서버에서 새로 발급합니다. |
| sessionId | String | 아니요 | session_id의 alias입니다. |
| recommendedQuestions | Boolean | 아니요 | true이면 추천 질문을 응답에 포함합니다. 기본값은 false입니다. |
messages 사용 방식
messages는 배열 형태로 전달하지만, 서버는 전체 대화기록을 요청 본문에서 이어받지 않습니다.실제로는
messages 배열에서 마지막 role: "user" 메시지 1개만 질문으로 사용합니다.대화기록은
messages로 매번 전달하는 것이 아니라, session_id 기준으로 서버에 저장된 대화기록을 사용합니다.예를 들어 아래 요청에서는 마지막 user 메시지만 실제 질문으로 사용됩니다.
JSON
{
"messages": [
{
"role": "user",
"content": "첫 질문"
},
{
"role": "assistant",
"content": "이전 답변"
},
{
"role": "user",
"content": "마지막 질문"
}
]
}실제 질문으로 사용되는 값:
JSON
{
"role": "user",
"content": "마지막 질문"
}session_id 정책
v2에서는
session_id를 서버에서 발급할 수 있습니다.| 상황 | 동작 |
|---|---|
session_id 없음 | 서버에서 새 session_id를 생성하고 응답에 포함합니다. |
session_id 있음 + 유효함 | 해당 세션의 대화기록을 이어서 사용합니다. |
session_id 있음 + 형식이 잘못됨 | 오류를 반환합니다. |
session_id 있음 + 존재하지 않음 | 오류를 반환합니다. |
권장 방식은 다음과 같습니다.
- 첫 요청에서는
session_id없이 호출합니다. - 응답에서 받은
session_id를 저장합니다. - 이후 대화 요청에 같은
session_id를 전달합니다.
recommendedQuestions 정책
recommendedQuestions는 선택사항입니다.| 요청 조건 | 응답 동작 |
|---|---|
생략 또는 false | 추천 질문을 반환하지 않습니다. |
true + 추천 질문 존재 | 추천 질문을 응답에 포함합니다. |
true + 추천 질문 없음 | recommended_questions: []를 반환합니다. |
추천 질문은 마지막
role: "user" 메시지를 기준으로 서버가 자동으로 조회합니다.stream: false에서는 응답 최상위의 recommended_questions 배열로 반환됩니다.stream: true에서는 응답 chunk 내부의 sidetalk_event.type이 recommendedQuestions인지 확인하고, 추천 질문 목록은 sidetalk_event.data.items에서 가져옵니다.추천 질문 항목의 식별자는
knowledgeId로 반환됩니다.요청 예시
새 대화 시작
JSON
curl -X POST https://api.sidetalk.ai/v2/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_KEY}" \
-d '{
"messages": [
{
"role": "user",
"content": "안녕하세요, 도움이 필요해요."
}
],
"stream": false
}'기존 세션 이어서 대화
JSON
curl -X POST https://api.sidetalk.ai/v2/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_KEY}" \
-d '{
"messages": [
{
"role": "user",
"content": "이전 대화 내용 기준으로 다시 설명해줘."
}
],
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"stream": false
}'추천 질문 포함
JSON
curl -X POST https://api.sidetalk.ai/v2/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_KEY}" \
-d '{
"messages": [
{
"role": "user",
"content": "사이드톡이 뭐야?"
}
],
"stream": false,
"recommendedQuestions": true
}'응답 형식
응답은
stream 값에 따라 두 가지 방식으로 제공됩니다.1-1. JSON 응답 (stream: false 또는 생략)
기본 응답 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "안녕하세요, 무엇을 도와드릴까요?"
},
"finish_reason": "stop"
}
]
}응답 필드 설명
| 필드 | 타입 | 설명 |
|---|---|---|
| success | Boolean | 요청 성공 여부입니다. |
| id | String | 응답 ID입니다. chatcmpl- prefix와 chat_log_uuid를 포함합니다. |
| object | String | 응답 객체 타입입니다. chat.completion 값이 반환됩니다. |
| created | Number | 응답 생성 시각입니다. Unix timestamp 형식입니다. |
| model | String | 응답 모델명입니다. 기본값은 sidetalk입니다. |
| session_id | String | 대화 세션 ID입니다. 이후 대화 요청에 사용합니다. |
| chat_log_uuid | String | 해당 대화 로그의 UUID입니다. 피드백 저장 시 사용합니다. |
| choices | Array | 응답 후보 목록입니다. |
| choices[0].index | Number | 응답 후보의 순서입니다. |
| choices[0].message.role | String | 응답 역할입니다. 보통 assistant입니다. |
| choices[0].message.content | String | AI의 전체 응답 텍스트입니다. |
| choices[0].finish_reason | String | 응답 종료 사유입니다. 정상 종료 시 stop 값이 반환됩니다. |
| recommended_questions | Array | recommendedQuestions: true일 때만 반환되는 추천질문 목록입니다. 최대 3개가 반환되며, 추천질문을 찾지 못하면 빈 배열이 반환됩니다. | |
추천 질문 응답 예시
JSON
{
"success": true,
"id": "chatcmpl-example",
"object": "chat.completion",
"created": 1787101993,
"model": "sidetalk",
"session_id": "4b025ddd-6a58-451a-9c29-6f221aa7bc9",
"chat_log_uuid": "a5c88ab2-c7cf-412f-b080-dc6ba74ebeee",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "사이드톡은 질문에 답변하는 AI 서비스입니다."
},
"finish_reason": "stop"
}
],
"recommended_questions": [
{
"knowledgeId": "3356",
"question": "추천 질문1",
"type": "chat_data"
},
{
"knowledgeId": "3357",
"question": "추천 질문2",
"type": "chat_data"
},
{
"knowledgeId": "3358",
"question": "추천 질문3",
"type": "chat_data"
}
]
}추천 질문 항목 설명
| 필드 | 타입 | 설명 |
|---|---|---|
| recommended_questions[].knowledgeId | String | 추천 질문 항목을 식별하는 ID입니다. |
| recommended_questions[].question | String | 사용자에게 노출할 추천 질문 텍스트입니다. |
| recommended_questions[].type | String | 추천 질문 원본 타입입니다. 예: chat_data, notion, file, file_pdf |
1-2. 스트림 응답 (stream: true)
응답은 SSE(Server-Sent Events) 형식으로 전달됩니다.
스트림 응답은 먼저
metadata 이벤트를 1회 반환한 뒤, 답변 조각을 순차적으로 반환합니다.주요 이벤트 종류
| 이벤트명 | 설명 |
|---|---|
| metadata | 세션 ID와 대화 로그 UUID를 포함하는 메타 정보입니다. |
| message | 일반 텍스트 응답 조각입니다. |
| showCard | 카드형 응답 데이터입니다. |
| inputForm | 입력 폼 데이터입니다. |
| reservationBookingForm | 예약 입력 폼 데이터입니다. |
| reservationLookupForm | 예약 조회 폼 데이터입니다. |
| pageRedirect | 페이지 이동용 링크 데이터입니다. |
metadata 이벤트 데이터 구조
| 필드 | 타입 | 설명 |
|---|---|---|
| session_id | String | 현재 대화 세션 ID입니다. |
| chat_log_uuid | String | 현재 대화 로그 UUID입니다. |
metadata 이벤트 예시
JSON
event: metadata
data: {"session_id":"943f15db-3984-4f21-ad64-f0115839c3a6","chat_log_uuid":"2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"}message 이벤트 데이터 구조
| 필드 | 타입 | 설명 |
|---|---|---|
| id | String | 응답 ID입니다. |
| object | String | 응답 조각 객체 타입입니다. chat.completion.chunk 값이 반환됩니다. |
| created | Number | 응답 생성 시각입니다. Unix timestamp 형식입니다. |
| model | String | 응답 모델명입니다. 기본값은 sidetalk입니다. |
| choices | Array | 응답 조각 목록입니다. |
| choices[0].index | Number | 응답 후보의 순서입니다. |
| choices[0].delta.content | String | AI 응답의 일부 텍스트입니다. |
| choices[0].finish_reason | String 또는 null | 응답 종료 사유입니다. 진행 중에는 null, 종료 시 stop입니다. |
스트림 응답 예시
JSON
event: metadata
data: {"session_id":"943f15db-3984-4f21-ad64-f0115839c3a6","chat_log_uuid":"2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"}
data: {"id":"chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3","object":"chat.completion.chunk","created":1783928563,"model":"sidetalk","choices":[{"index":0,"delta":{"content":"안녕하세요, "},"finish_reason":null}]}
data: {"id":"chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3","object":"chat.completion.chunk","created":1783928563,"model":"sidetalk","choices":[{"index":0,"delta":{"content":"무엇을 도와드릴까요?"},"finish_reason":null}]}
data: {"id":"chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3","object":"chat.completion.chunk","created":1783928563,"model":"sidetalk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE] 스트림 처리 시 주의사항
- 각 이벤트는
\n\n기준으로 구분됩니다. event: metadata는 스트림 시작 시 1회 전달됩니다.- 일반 텍스트 응답은
choices[0].delta.content에 포함됩니다. finish_reason이stop이면 응답 생성이 정상 종료된 것입니다.data: [DONE]이 오면 스트림이 완전히 종료된 것입니다.session_id는 이후 대화 요청에 사용해야 하므로 저장하는 것을 권장합니다.chat_log_uuid는 피드백 API 호출 시 필요하므로 저장하는 것을 권장합니다.showCard,inputForm,reservationBookingForm,reservationLookupForm,pageRedirect이벤트는 일반 텍스트가 아니라 구조화된 에이전트 응답 데이터입니다.sidetalk_event.type이recommendedQuestions인 chunk를 확인합니다.- 추천 질문 목록은
sidetalk_event.data.items에서 가져옵니다. - 추천 질문 항목의 ID는
knowledgeId입니다.
1-3. 구조화 이벤트 응답
채팅 응답에는 일반 텍스트 외에도 카드, 입력 폼, 예약 폼, 페이지 이동 같은 구조화 이벤트가 포함될 수 있습니다.
구조화 이벤트는 일반 답변 텍스트가 아니라 클라이언트 UI에서 별도로 처리해야 하는 데이터입니다.
지원되는 이벤트 타입은 다음과 같습니다.
recommended_questions는 기본 응답에 포함되지 않습니다. 요청에 recommendedQuestions: true를 명시한 경우에만 반환됩니다. recommendedQuestions를 생략하거나 false로 설정하면 추천 질문 필드와 추천 질문 이벤트가 반환되지 않습니다.| type | 설명 |
|---|---|
| showCard | 카드형 응답 데이터입니다. |
| inputForm | 사용자 입력 폼 데이터입니다. |
| reservationBookingForm | 예약 접수 폼 데이터입니다. |
| reservationLookupForm | 예약 조회 폼 데이터입니다. |
| pageRedirect | 페이지 이동 또는 링크 안내 데이터입니다. |
| recommendedQuestions | 추천 질문 데이터입니다. stream:true에서만 전달됩니다. |
JSON 응답에서 구조화 이벤트 (stream: false)
stream: false 응답에서는 구조화 이벤트가 최상위 sidetalk_events 배열에 포함됩니다.구조화 이벤트가 없으면
sidetalk_events 필드는 생략될 수 있습니다.showCard 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "아래 정보를 확인해주세요."
},
"finish_reason": "stop"
}
],
"sidetalk_events": [
{
"type": "showCard",
"data": {
"title": "상품 안내",
"description": "추천 상품 목록입니다.",
"items": [
{
"title": "상품 A",
"description": "상품 A 설명",
"url": "https://example.com/product-a"
}
]
}
}
]
}inputForm 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "상담을 위해 아래 정보를 입력해주세요."
},
"finish_reason": "stop"
}
],
"sidetalk_events": [
{
"type": "inputForm",
"data": {
"title": "상담 정보 입력",
"fields": [
{
"name": "name",
"label": "이름",
"type": "text",
"required": true
},
{
"name": "phone",
"label": "연락처",
"type": "tel",
"required": true
}
]
}
}
]
}reservationBookingForm 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "예약을 위해 아래 정보를 입력해주세요."
},
"finish_reason": "stop"
}
],
"sidetalk_events": [
{
"type": "reservationBookingForm",
"data": {
"title": "예약하기",
"form_id": "reservation_01",
"fields": [
{
"name": "date",
"label": "예약일",
"type": "date",
"required": true
},
{
"name": "time",
"label": "예약 시간",
"type": "time",
"required": true
}
]
}
}
]
}reservationLookupForm 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "예약 조회를 위해 아래 정보를 입력해주세요."
},
"finish_reason": "stop"
}
],
"sidetalk_events": [
{
"type": "reservationLookupForm",
"data": {
"title": "예약 조회",
"fields": [
{
"name": "phone",
"label": "연락처",
"type": "tel",
"required": true
}
]
}
}
]
}pageRedirect 예시
JSON
{
"success": true,
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion",
"created": 1783928563,
"model": "sidetalk",
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "요청하신 페이지로 이동할 수 있습니다."
},
"finish_reason": "stop"
}
],
"sidetalk_events": [
{
"type": "pageRedirect",
"data": {
"url": "https://example.com/order",
"label": "주문 페이지로 이동"
}
}
]
}스트림 응답에서 구조화 이벤트 (stream: true)
stream: true 응답에서는 구조화 이벤트도 chat.completion.chunk 형식으로 전달됩니다.일반 텍스트는
choices[0].delta.content에 포함됩니다.구조화 이벤트는
sidetalk_event 필드에 포함됩니다.showCard 예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "showCard",
"data": {
"title": "상품 안내",
"description": "추천 상품 목록입니다.",
"items": [
{
"title": "상품 A",
"description": "상품 A 설명",
"url": "https://example.com/product-a"
}
]
}
}
}inputForm 예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "inputForm",
"data": {
"title": "상담 정보 입력",
"fields": [
{
"name": "name",
"label": "이름",
"type": "text",
"required": true
},
{
"name": "phone",
"label": "연락처",
"type": "tel",
"required": true
}
]
}
}
}reservationBookingForm 예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "reservationBookingForm",
"data": {
"title": "예약하기",
"form_id": "reservation_01",
"fields": [
{
"name": "date",
"label": "예약일",
"type": "date",
"required": true
},
{
"name": "time",
"label": "예약 시간",
"type": "time",
"required": true
}
]
}
}
}reservationLookupForm 예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "reservationLookupForm",
"data": {
"title": "예약 조회",
"fields": [
{
"name": "phone",
"label": "연락처",
"type": "tel",
"required": true
}
]
}
}
}pageRedirect 예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "pageRedirect",
"data": {
"url": "https://example.com/order",
"label": "주문 페이지로 이동"
}
}
}recommendedQuestions예시
JSON
data: {
"id": "chatcmpl-2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"object": "chat.completion.chunk",
"created": 1783928563,
"model": "sidetalk",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": null
}
],
"sidetalk_event": {
"type": "recommendedQuestions",
"data": {
"items": [
{
"knowledgeId": "3356",
"question": "추천 질문1",
"type": "chat_data"
}
]
}
}
}필드 설명
sidetalk_events
| 필드 | 타입 | 설명 |
|---|---|---|
| sidetalk_events | Array | 구조화 이벤트 목록입니다. |
| sidetalk_events[].type | String | 이벤트 타입입니다. |
| sidetalk_events[].data | Object | 이벤트별 데이터입니다. |
sidetalk_event
| 필드 | 타입 | 설명 |
|---|---|---|
| sidetalk_event | Object | 구조화 이벤트 데이터입니다. |
| sidetalk_event.type | String | 이벤트 타입입니다. |
| sidetalk_event.data | Object | 이벤트별 데이터입니다. |
처리 예시
JavaScript
function handleSidetalkEvent(event) {
if (!event) return;
const eventType = event.type;
const eventData = event.data;
if (eventType === "showCard") {
console.log("카드 응답:", eventData);
}
if (eventType === "inputForm") {
console.log("입력 폼:", eventData);
}
if (eventType === "reservationBookingForm") {
console.log("예약 입력 폼:", eventData);
}
if (eventType === "reservationLookupForm") {
console.log("예약 조회 폼:", eventData);
}
if (eventType === "pageRedirect") {
console.log("페이지 이동:", eventData);
}
if (eventType === "recommendedQuestions") {
console.log("추천 질문:", eventData);
}
}JSON 응답 처리
JavaScript
const events = data.sidetalk_events || [];
for (const event of events) {
handleSidetalkEvent(event);
}
//추천 질문
const recommendedQuestions = data.recommended_questions || [];
console.log("추천 질문:", recommendedQuestions);스트림 응답 처리
JavaScript
if (data.sidetalk_event) {
handleSidetalkEvent(data.sidetalk_event);
}주의사항
- 구조화 이벤트의
data구조는 사이트 설정, 에이전트 설정, 폼 설정에 따라 달라질 수 있습니다. - 클라이언트에서는
data내부 필드를 고정 구조로 가정하지 않는 것을 권장합니다. - 일반 텍스트는
choices[0].message.content또는choices[0].delta.content에서 처리합니다. - 구조화 이벤트는
sidetalk_events또는sidetalk_event에서 별도로 처리합니다. - 추천 질문은
stream: false에서 최상위recommended_questions배열로 반환됩니다. - 추천 질문은
stream: true에서sidetalk_event.data.items에 포함됩니다. sidetalk_events는stream: false응답에서 사용됩니다.sidetalk_event는stream: true응답의 각 chunk에서 사용됩니다.metadata이벤트는 스트림 시작 시 세션 정보를 전달하기 위한 이벤트입니다.data: [DONE]은 스트림 종료를 의미합니다.
2. 대화 피드백 API
대화 응답에서 받은
chat_log_uuid를 이용해, 해당 대화에 대한 피드백을 저장할 수 있습니다.v2 채팅 응답을 사용하더라도 피드백 API는 기존 경로를 사용합니다.
URL
Plain Text
https://api.sidetalk.ai/v1/chat/feedbackHTTP 메서드
Plain Text
POST헤더
| 헤더 | 값 | 설명 |
|---|---|---|
| Content-Type | application/json | 요청 본문이 JSON 형식임을 나타냅니다. |
| Authorization | Bearer <api_key> | Sidetalk 대시보드에서 제공된 API 키를 사용하세요. |
본문
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| session_id | String | 예 | 피드백을 저장할 대화의 세션 ID입니다. |
| chat_log_uuid | String | 예 | v2 chat/completions 응답에서 받은 대화 UUID입니다. |
| chat_log_feedback | String | 예 | 피드백 값입니다. 1은 도움이 됨, 2는 도움이 안 됨을 의미합니다. |
요청 예시
JSON
curl -X POST https://api.sidetalk.ai/v1/chat/feedback \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_KEY}" \
-d '{
"session_id": "943f15db-3984-4f21-ad64-f0115839c3a6",
"chat_log_uuid": "2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3",
"chat_log_feedback": "1"
}'성공 응답 예시
JSON
{
"success": true,
"updated": true,
"already_saved": false,
"feedback": "1"
}3. JavaScript 예시
3-1. JSON 응답 처리
JavaScript
async function sendMessageV2(message, sessionId = "") {
const response = await fetch("https://api.sidetalk.ai/v2/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer [API_KEY]"
},
body: JSON.stringify({
messages: [
{
role: "user",
content: message
}
],
session_id: sessionId,
stream: false
})
});
const data = await response.json();
if (!response.ok) {
console.error("Server Error:", data.error?.message || "Unknown error");
return null;
}
const answer = data.choices?.[0]?.message?.content || "";
const finishReason = data.choices?.[0]?.finish_reason || "";
const nextSessionId = data.session_id;
const chatLogUuid = data.chat_log_uuid;
console.log("AI 답변:", answer);
console.log("finish_reason:", finishReason);
console.log("session_id:", nextSessionId);
console.log("chat_log_uuid:", chatLogUuid);
return {
answer,
finishReason,
sessionId: nextSessionId,
chatLogUuid
};
}3-2. 스트림 응답 처리
JavaScript
async function sendMessageStreamV2(message, sessionId = "") {
const response = await fetch("https://api.sidetalk.ai/v2/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer [API_KEY]"
},
body: JSON.stringify({
messages: [
{
role: "user",
content: message
}
],
session_id: sessionId,
stream: true
})
});
if (!response.ok) {
const errorData = await response.json();
console.error("Server Error:", errorData.error?.message || "Unknown error");
return null;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let savedSessionId = sessionId;
let savedChatLogUuid = "";
let fullAnswer = "";
let finishReason = "";
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const blocks = buffer.split("\n\n");
buffer = blocks.pop() || "";
for (const block of blocks) {
if (!block.trim()) continue;
const lines = block.split("\n");
let eventName = "message";
const dataLines = [];
for (const line of lines) {
if (line.startsWith("event:")) {
eventName = line.replace("event:", "").trim();
}
if (line.startsWith("data:")) {
dataLines.push(line.replace("data:", "").trimStart());
}
}
const dataStr = dataLines.join("\n").trim();
if (!dataStr) continue;
if (dataStr === "[DONE]") {
return {
answer: fullAnswer,
finishReason,
sessionId: savedSessionId,
chatLogUuid: savedChatLogUuid
};
}
const data = JSON.parse(dataStr);
if (eventName === "metadata") {
savedSessionId = data.session_id;
savedChatLogUuid = data.chat_log_uuid;
continue;
}
if (eventName === "message") {
const choice = data.choices?.[0] || {};
const chunk = choice.delta?.content || "";
if (chunk) {
fullAnswer += chunk;
console.log("chunk:", chunk);
}
if (choice.finish_reason) {
finishReason = choice.finish_reason;
console.log("finish_reason:", finishReason);
}
continue;
}
if (eventName === "showCard") {
console.log("showCard:", data);
}
if (eventName === "inputForm") {
console.log("inputForm:", data);
}
if (eventName === "pageRedirect") {
console.log("pageRedirect:", data);
}
}
}
return {
answer: fullAnswer,
finishReason,
sessionId: savedSessionId,
chatLogUuid: savedChatLogUuid
};
}3-3. 피드백 저장
JavaScript
async function sendFeedback({ sessionId, chatLogUuid, isHelpful }) {
const response = await fetch("https://api.sidetalk.ai/v1/chat/feedback", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer [API_KEY]"
},
body: JSON.stringify({
session_id: sessionId,
chat_log_uuid: chatLogUuid,
chat_log_feedback: isHelpful ? "1" : "2"
})
});
const data = await response.json();
if (!response.ok || !data.success) {
console.error("Feedback Error:", data.error || data.message || "Unknown error");
return null;
}
return data;
}4. 오류 응답
v2 채팅 API에서 오류가 발생하면 다음과 같은 형식으로 반환됩니다.
JSON
{
"error": {
"message": "messages is required",
"type": "invalid_request_error",
"code": "message_required"
}
}오류 응답에는
choices와 finish_reason이 포함되지 않습니다.주요 오류 케이스
| HTTP 상태 | code | 설명 |
|---|---|---|
| 400 | message_required | messages가 누락되었거나, 사용할 수 있는 role: "user" 메시지가 없는 경우 |
| 400 | invalid_session_id_format | session_id 형식이 잘못된 경우 |
| 404 | session_id_not_found | 전달한 session_id가 현재 사이트에 존재하지 않는 경우 |
| 403 | Authentication error | API 인증에 실패한 경우 |
| 500 | server_error | 서버 내부 오류가 발생한 경우 |
5. 참고 사항
- 실제 질문으로 사용하는 값은
messages배열의 마지막role: "user"메시지 1개입니다. - 이전 대화기록은
messages로 전달하지 않고,session_id기준으로 서버에 저장된 대화기록을 사용합니다. session_id가 없으면 서버에서 새로 발급합니다.session_id는 서버에서 발급된 값을 사용하는 것을 권장합니다.session_id가 있으면 현재 사이트에 존재하는 세션인지 확인합니다.- 스트림 응답에서는
metadata이벤트에서session_id와chat_log_uuid를 먼저 저장하는 것을 권장합니다. - 피드백 저장에는
session_id와chat_log_uuid가 모두 필요합니다. - v1
/v1/chat/completions는 기존 호환성을 위해 기존 응답 형식을 유지합니다. - 새 세션 자동 발급과 v2 응답 구조를 사용하려면
/v2/chat/completions사용을 권장합니다.
