> For the complete documentation index, see [llms.txt](https://genos-docs.gitbook.io/default/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://genos-docs.gitbook.io/default/v1.9.5/advanced-tutorials/guides/api/chat.md).

# 채팅 API 사용

채팅 애플리케이션을 외부 프로그램에서 호출하는 방법입니다. 모든 요청은 `{GenOS 주소}/api/genportal` 아래로 보냅니다.

| 단계       | 메서드 · 경로                                          | 용도                              |
| -------- | ------------------------------------------------- | ------------------------------- |
| 로그인      | `POST /auth/login`                                | access token · refresh token 발급 |
| 토큰 재발급   | `POST /auth/refresh`                              | access token 재발급                |
| 채팅 정보 조회 | `GET /chat-service/info?chat_endpoint={endpoint}` | 채팅 앱 설정(피드백 방식 등) 조회            |
| 채팅 요청    | `POST /chat`                                      | 질문을 보내고 답변을 SSE 스트림으로 받음        |
| 피드백      | `POST /chat-service/feedback`                     | 답변에 만족·불만족 피드백 등록·삭제            |

> 이전 버전의 `{GenOS 주소}/app/api/chat` 주소(`/chat/v2/query/{endpoint}`, `/chat/feedback/v2` 등)는 더 이상 쓰지 않습니다.

## 보안

### 로그인

* 채팅 API에 사용할 JWT(access token)를 얻기 위해 로그인합니다.

```python
import requests

genos_url = 'GenOS 주소'
url = f"{genos_url}/api/genportal"

body = {
    "user_id": "genos_user_01",
    "password": "<PASSWD>"
}
res = requests.post(f"{url}/auth/login", json=body)
print(res.json())
```

* 정상 출력은 아래와 같습니다. 응답의 `data`에 `access_token` · `refresh_token` · `user` 등이 들어 있습니다.

```json
{
    "code": 0,
    "errMsg": "success",
    "data": {
        "access_token": "<JWT 1>",
        "refresh_token": "<JWT 2>",
        "user": { "user_id": "genos_user_01", "name": "GenOS 사용자 01", ... },
        "current_login_ip": "123.123.123.123",
        ...
    }
}
```

* access\_token, refresh\_token의 값을 저장합니다.
* 성공·실패는 HTTP 상태 코드가 아니라 본문의 `code`로 판단합니다. 비밀번호 오류 등은 HTTP 200에 `code: 1`로, 관리자에 의한 로그인 제한·장기 미로그인 잠금은 HTTP 403에 `code: 2`로 내려옵니다.

### (Opt.) Access 토큰 재발급

* refresh token을 갖고 있다면, access token의 재발급을 요청할 수 있습니다.
* refresh token은 바뀌지 않으며, 응답에는 새 access token만 들어 있습니다.

```python
body = {
    "refresh_token": refresh_token
}
res = requests.post(f"{url}/auth/refresh", json=body)
print(res.json())
```

* 정상 출력은 아래와 같습니다.

```json
{
    "code": 0,
    "errMsg": "success",
    "data": {
        "access_token": "<JWT 3>"
    }
}
```

## 채팅

* 사용할 채팅 애플리케이션의 endpoint(`jlig_i3hi_i743` 형식)가 필요합니다. 채팅 화면 주소 `/app/chat/{endpoint}`의 마지막 부분입니다.
* 이후 모든 요청에는 `Authorization: Bearer <access token>` 헤더를 붙입니다.

### 정보 조회

* 해당 채팅의 주요 정보를 조회합니다.

```python
endpoint = 'jlig_i3hi_i743'
headers = {
    "Authorization": "Bearer <JWT 1>"
}
res = requests.get(f"{url}/chat-service/info", params={"chat_endpoint": endpoint}, headers=headers)
print(res.json())
```

* 사용이 승인된 채팅이면 `data`에 다음과 같은 값이 들어 있습니다(일부만 표시).

```json
{
    "code": 0,
    "errMsg": "success",
    "data": {
        "chat_id": 112,                       // 채팅 서비스 ID
        "chat_rev_id": 187,                   // 채팅 서비스 리비전 ID
        "approval_result": "AP0001",
        "chat_name": "검색 UI",
        "title": "검색 UI",
        "workflow_id": 226,                   // 연결된 워크플로우 ID
        "greeting": "안녕하세요. 무엇을 도와드릴까요?",  // 인사말
        "chat_samples": [],                   // 예시 질문
        "negative_feedback_mode": "REQUIRED", // '싫어요' 사유 수집: NONE / OPTIONAL / REQUIRED
        "feedback_type": "FILL_IN",           // '싫어요' 사유 입력 방식: FILL_IN / CHOICE
        "feedback_questions": [],             // '싫어요' 선택지 (CHOICE 일 때)
        "allow_other_feedback": true,
        "positive_feedback_mode": "NONE",     // '좋아요' 사유 수집
        "positive_feedback_type": "FILL_IN",
        "positive_feedback_questions": [],
        "workflow_plugins": [],               // 파일 기반 대화 등 플러그인 정보
        ...
    }
}
```

* 사용 승인을 받지 않은 채팅(미신청·승인 대기·반려)은 `approval_result`와 리비전 정보만 내려오고, `chat_id` · `workflow_id` 등 채팅 정보는 비어 있습니다.
* 피드백을 보낼 때 피드백 관련 값(`*_feedback_mode` · `*_feedback_type` · `*_feedback_questions`)을 참고합니다.

### 채팅 요청

* 질문을 보내면 답변이 **SSE(Server-Sent Events) 스트림**으로 옵니다. 한 번에 JSON으로 받는 방식은 없습니다.
* 스트림은 `data: {"event": "...", "data": ...}` 형식의 줄로 이어집니다.

```python
import json
import requests

body = {
    "message": "Hi, How are you?",
    "endpoint": endpoint
}
answer, session_id, message_id = "", None, None
with requests.post(f"{url}/chat", headers=headers, json=body, stream=True) as res:
    for line in res.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data:"):
            continue
        ev = json.loads(line[len("data:"):].strip())
        if ev["event"] == "session_id":
            session_id = ev["data"]                       # 대화 세션 ID
        elif ev["event"] == "message_id" and ev["data"].get("role") == "assistant":
            message_id = ev["data"]["id"]                  # 이 답변의 메시지 ID (피드백에 사용)
        elif ev["event"] == "token":
            answer += ev["data"]                           # 답변 토큰
        elif ev["event"] == "error":
            print("error:", ev["data"])

print(answer)
print(session_id, message_id)
```

* 주요 이벤트는 다음과 같습니다.

| event             | 내용                                                                         |
| ----------------- | -------------------------------------------------------------------------- |
| `session_id`      | 대화 세션 ID. 이어서 질문할 때와 피드백에 사용합니다.                                           |
| `message_id`      | 메시지 ID. `data.role`이 `user`(질문) · `assistant`(답변)로 나뉩니다. 피드백에는 답변 ID를 씁니다. |
| `token`           | 답변 토큰. 차례로 이어 붙이면 전체 답변이 됩니다.                                              |
| `sourceDocuments` | 답변에 참고한 문서 정보                                                              |
| `end`             | 답변 종료                                                                      |
| `error`           | 오류                                                                         |

* 스트림이 시작되기 전에 실패하면(권한 없음 등) SSE가 아니라 `{"code": 1, ...}` 형식의 JSON이 내려옵니다.

### 멀티 턴 대화

* 대화 이력을 기반으로 대화하려면, 첫 요청에서 받은 `session_id`를 다음 요청 body에 넣습니다.
* `session_id`를 빼면 새 세션(새 대화)으로 시작합니다.

```python
body = {
    "message": "What is my name?",
    "endpoint": endpoint,
    "session_id": session_id   # 첫 요청의 session_id 이벤트 값
}
# 이후 처리는 채팅 요청과 같습니다.
```

## 피드백

* 채팅 요청에서 받은 `session_id`와 답변 `message_id`로 답변에 피드백을 남깁니다.
* `thumbs`는 `up`(만족) · `down`(불만족) · `none`(피드백 삭제) 중 하나입니다.
* 사유 수집 정책은 채팅 앱 설정이 기준입니다. 정책을 확인하기 위해 `endpoint`는 꼭 함께 보냅니다.
  * 수집 방식이 `NONE`이면 보낸 사유는 저장되지 않습니다.
  * `REQUIRED`인데 사유가 비어 있으면 요청이 거부됩니다.

### 만족 피드백 · 삭제

```python
body = {
    "endpoint": endpoint,
    "session_id": session_id,
    "message_id": message_id,
    "thumbs": "up"  # "none" 이면 피드백 삭제
}
res = requests.post(f"{url}/chat-service/feedback", headers=headers, json=body)
print(res.json())
```

* 정상 출력은 아래와 같습니다.

```json
{"code": 0, "errMsg": "success", "data": null}
```

### 불만족 피드백

#### 주관식

* 채팅 정보 조회 시 `feedback_type`이 `FILL_IN`이면 사유를 직접 입력하는 채팅입니다.

```python
body = {
    "endpoint": endpoint,
    "session_id": session_id,
    "message_id": message_id,
    "thumbs": "down",
    "messages": [{ "comment": "The answer is unnecessarily long." }]
}
res = requests.post(f"{url}/chat-service/feedback", headers=headers, json=body)
print(res.json())
```

* 정상 출력은 위와 동일합니다.

#### 객관식

* 채팅 정보 조회 시 `feedback_type`이 `CHOICE`이면 선택지에서 고르는 채팅입니다.
  * `feedback_questions`: 선택지 목록
  * `allow_other_feedback`: 선택지 외에 「기타」 의견을 받을지 여부
* `messages`에는 선택지마다 한 항목씩 넣습니다.
  * `question`: 선택지 문구
  * `is_checked`: 사용자가 그 선택지를 골랐는지 여부
  * `comment`: 선택한 항목에 덧붙인 의견(선택)
  * 「기타」 의견은 `is_other: true`로 보냅니다.

```python
body = {
    "endpoint": endpoint,
    "session_id": session_id,
    "message_id": message_id,
    "thumbs": "down",
    "messages": [
        { "question": "길어요", "comment": "불필요해요", "is_checked": True },
        { "question": "틀렸어요", "comment": "", "is_checked": False },
        { "question": "기타", "comment": "", "is_checked": False, "is_other": True }
    ]
}
res = requests.post(f"{url}/chat-service/feedback", headers=headers, json=body)
print(res.json())
```

* 정상 출력은 위와 동일합니다.
* 만족 피드백의 사유도 같은 방식이며, 채팅 정보 조회의 `positive_feedback_*` 값을 따릅니다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://genos-docs.gitbook.io/default/v1.9.5/advanced-tutorials/guides/api/chat.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
