Sidetalk사용자 가이드
사이드톡 홈페이지 ↗대시보드 열기

대화형 답변 생성 API (V2)

관련 문서


목적

이 API는 사용자 메시지를 전달하고, AI의 응답을 반환합니다.
v2 API는 session_id가 없으면 서버에서 새 세션을 발급할 수 있습니다.
같은 session_id로 요청하면 이전 대화 흐름을 이어서 답변할 수 있습니다.
응답에 포함되는 chat_log_uuid를 이용해, 해당 대화에 대해 도움이 됐어요 / 도움이 안 됐어요 피드백을 저장할 수 있습니다.

1. 채팅 응답 생성 API

URL


HTTP 메서드

Plain Text
POST

헤더

헤더값설명
Content-Typeapplication/json요청 본문이 JSON 형식임을 나타냅니다.
AuthorizationBearer <api_key>Sidetalk 대시보드에서 제공된 API 키를 사용하세요.

본문

파라미터타입필수설명
messagesArray예메시지 배열입니다. 마지막 role: "user" 메시지만 실제 질문으로 사용합니다.
streamBoolean아니요응답을 스트림으로 받을지 여부입니다. 기본값은 false입니다.
session_idString아니요대화 세션 ID입니다. 없으면 서버에서 새로 발급합니다.
sessionIdString아니요session_id의 alias입니다.
recommendedQuestionsBoolean아니요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 있음 + 존재하지 않음오류를 반환합니다.
권장 방식은 다음과 같습니다.
  1. 첫 요청에서는 session_id 없이 호출합니다.
  2. 응답에서 받은 session_id를 저장합니다.
  3. 이후 대화 요청에 같은 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"
    }
  ]
}

응답 필드 설명

필드타입설명
successBoolean요청 성공 여부입니다.
idString응답 ID입니다. chatcmpl- prefix와 chat_log_uuid를 포함합니다.
objectString응답 객체 타입입니다. chat.completion 값이 반환됩니다.
createdNumber응답 생성 시각입니다. Unix timestamp 형식입니다.
modelString응답 모델명입니다. 기본값은 sidetalk입니다.
session_idString대화 세션 ID입니다. 이후 대화 요청에 사용합니다.
chat_log_uuidString해당 대화 로그의 UUID입니다. 피드백 저장 시 사용합니다.
choicesArray응답 후보 목록입니다.
choices[0].indexNumber응답 후보의 순서입니다.
choices[0].message.roleString응답 역할입니다. 보통 assistant입니다.
choices[0].message.contentStringAI의 전체 응답 텍스트입니다.
choices[0].finish_reasonString응답 종료 사유입니다. 정상 종료 시 stop 값이 반환됩니다.
recommended_questionsArrayrecommendedQuestions: 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[].knowledgeIdString추천 질문 항목을 식별하는 ID입니다.
recommended_questions[].questionString사용자에게 노출할 추천 질문 텍스트입니다.
recommended_questions[].typeString추천 질문 원본 타입입니다. 예: 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_idString현재 대화 세션 ID입니다.
chat_log_uuidString현재 대화 로그 UUID입니다.

metadata 이벤트 예시

JSON
event: metadata
data: {"session_id":"943f15db-3984-4f21-ad64-f0115839c3a6","chat_log_uuid":"2b9cc8fb-3cc6-4d40-aef6-d44af3d074f3"}

message 이벤트 데이터 구조

필드타입설명
idString응답 ID입니다.
objectString응답 조각 객체 타입입니다. chat.completion.chunk 값이 반환됩니다.
createdNumber응답 생성 시각입니다. Unix timestamp 형식입니다.
modelString응답 모델명입니다. 기본값은 sidetalk입니다.
choicesArray응답 조각 목록입니다.
choices[0].indexNumber응답 후보의 순서입니다.
choices[0].delta.contentStringAI 응답의 일부 텍스트입니다.
choices[0].finish_reasonString 또는 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_eventsArray구조화 이벤트 목록입니다.
sidetalk_events[].typeString이벤트 타입입니다.
sidetalk_events[].dataObject이벤트별 데이터입니다.

sidetalk_event

필드타입설명
sidetalk_eventObject구조화 이벤트 데이터입니다.
sidetalk_event.typeString이벤트 타입입니다.
sidetalk_event.dataObject이벤트별 데이터입니다.

처리 예시

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/feedback

HTTP 메서드

Plain Text
POST

헤더

헤더값설명
Content-Typeapplication/json요청 본문이 JSON 형식임을 나타냅니다.
AuthorizationBearer <api_key>Sidetalk 대시보드에서 제공된 API 키를 사용하세요.

본문

파라미터타입필수설명
session_idString예피드백을 저장할 대화의 세션 ID입니다.
chat_log_uuidString예v2 chat/completions 응답에서 받은 대화 UUID입니다.
chat_log_feedbackString예피드백 값입니다. 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설명
400message_requiredmessages가 누락되었거나, 사용할 수 있는 role: "user" 메시지가 없는 경우
400invalid_session_id_formatsession_id 형식이 잘못된 경우
404session_id_not_found전달한 session_id가 현재 사이트에 존재하지 않는 경우
403Authentication errorAPI 인증에 실패한 경우
500server_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 사용을 권장합니다.