이 API 는 Client SDK 키(osk_ 접두사) 로 인증합니다. 추천 실행은 비동기(202) + 폴링 구조입니다.
전체 흐름
1. POST /v1/profile/{profile_id}/session/start (Profile 세션 시작 → session_id 발급)
2. POST /v1/chat/{profile_id} (LLM 세션 생성; body 없음)
3. POST /v1/profile/{profile_id}/session/stamp (status="CHAT" 스탬프)
4. POST /v1/chat/{profile_id}/recommend (추천 kickoff, 202)
5. GET /v1/chat/{profile_id}/recommend/{session_id} (0.5 s 간격 폴링)
6. DELETE /v1/chat/{profile_id}/session/{session_id} (세션 종료)
클라이언트는 profile_id 와 세션 시작 응답으로 받은 session_id 만 기억하면 됩니다. 프로필에 저장된 name / gender / age / locale 은 서버가 자동으로 읽어 LLM 프롤로그를 만듭니다.
세션 상태
응답의 status 로 폴링을 제어합니다.
| 값 | 의미 | 클라이언트 동작 |
|---|
CHAT | 파이프라인 실행 중 | 0.5 s 후 재폴링 |
READY | 직전 추천 결과 첨부됨 | back_list_status == "completed" 면 result 사용 |
ERROR | 파이프라인 실패 | last_error 확인 |
ENDED | 세션 종료됨 (DELETE or 1 h 비활동) | 404 반환, 새 세션 필요 |
1. 채팅 세션 생성
POST /v1/chat/{profile_id}
요청 body 는 필요하지 않습니다. 프로필에 이미 등록된 name / gender / age / locale 을 서버가 꺼내 LLM user_query 프롤로그를 자동 생성합니다.
선행 조건: POST /v1/profile/{profile_id}/session/start 로 Profile 세션이 이미 시작되어 있어야 합니다. 없으면 SESSION_NOT_STARTED 409.
Response 200
{
"success": true,
"data": {
"session_id": "a14b2f9c-3d5e-4f2a-9a8b-1c2d3e4f5a6b",
"profile_id": "...",
"query_results": {
"session": { "session_id": "...", "status": "READY", "back_list_status": "not_started" }
}
}
}
session_id 는 Profile 세션 ID 와 동일한 값이며, 이후 모든 recommend/poll/delete 호출에 그대로 사용합니다.
2. 추천 kickoff
POST /v1/chat/{profile_id}/recommend
선행 조건: 세션의 최신 stamp 상태가 CHAT 이어야 합니다. 그 외 상태에서는 STAGE_NOT_CHAT 409 로 거부. stamp 전환은 POST /v1/profile/{profile_id}/session/stamp 로 수행.
Request
{
"session_id": "a14b2f9c-3d5e-4f2a-9a8b-1c2d3e4f5a6b",
"prompt": "오늘 저녁 카레 땡기는데"
}
| 필드 | 타입 | 필수 | 설명 |
|---|
session_id | string | O | 1번에서 받은 세션 ID |
prompt | string | O | 사용자 질의. 서버 내부에서 llm-demo 의 user_query 로 전달됨 |
Response 202
{
"success": true,
"data": {
"session": { "session_id": "...", "status": "CHAT", "back_list_status": "not_started" }
}
}
같은 세션에 이미 CHAT 추천이 돌고 있으면 409 Conflict.
3. 폴링
GET /v1/chat/{profile_id}/recommend/{session_id}
권장 폴링 간격 0.5 s. 응답 shape 은 status 에 따라 달라집니다.
Response — status: "CHAT" (진행 중)
{
"success": true,
"data": {
"session_id": "...",
"status": "CHAT",
"back_list_status": "not_started",
"updated_at": "...",
"last_error": null
}
}
폴링 계속.
Response — status: "READY" (완료)
{
"success": true,
"data": {
"session_id": "...",
"status": "READY",
"back_list_status": "completed",
"updated_at": "...",
"last_error": null,
"result": {
"shopping_list": {
"items": [
{
"item_name_en": "...",
"price_yen": 380,
"zone_name": "...",
"id": 1,
"matched": true,
"product": {
"id": 1,
"name": "라무네 캔디 29g",
"barcode": "4909254512030",
"price": 1400,
"price_unit": "₩",
"section_code": "CK",
"section_name": "계산대",
"src_url": "https://cdn.ones1ght.com/store/product/no_image.png"
}
}
],
"total_yen": 2450
},
"back_list": {
"back_recommendations": [
{
"recommendation_id": "...",
"item_name_en": "...",
"price_yen": 180,
"reason_type": "copurchase",
"store_reasoning": { "method": "copurchase_lift", "scoring_equation": "..." }
}
]
}
}
}
}
완료 판정: status == "READY" AND back_list_status == "completed".
Response — status: "ERROR"
{
"success": true,
"data": {
"session_id": "...",
"status": "ERROR",
"back_list_status": "not_started",
"last_error": "upstream timeout",
"result": { "shopping_list": null, "back_list": null }
}
}
last_error 확인 후 새 추천을 kickoff 하거나 세션 종료.
참고: conversation_state / preferences / profile_context / meal / upsell_surface / timing 등 llm-demo 내부 필드는 응답에 포함되지 않습니다.
4. 세션 종료
DELETE /v1/chat/{profile_id}/session/{session_id}
Response 200
{
"success": true,
"data": {
"session_id": "...",
"ended": true,
"ended_at": "2026-04-22T09:05:00Z",
"reason": "explicit"
}
}
명시 호출이 없어도 1 시간 비활동 이후 자동 종료됩니다.
에러 코드
| HTTP | code | 원인 |
|---|
| 400 | INVALID_REQUEST | body 검증 실패 |
| 404 | NOT_FOUND | profile / session 미존재 or 종료됨 |
| 409 | SESSION_NOT_STARTED | Profile 세션 시작 전 채팅 세션 생성 시도 |
| 409 | STAGE_NOT_CHAT | recommend 호출 시 stamp 가 CHAT 아님 |
| 409 | CONFLICT | 같은 세션에 이미 CHAT 추천 진행 중 |
| 500 | INTERNAL_ERROR | 업스트림 장애 등 |
전체 예제
SDK_KEY="osk_..."
PROFILE_ID="..."
# 1. Profile 세션 시작
SESSION_ID=$(curl -s -X POST https://api.ones1ght.com/v1/profile/$PROFILE_ID/session/start \
-H "Authorization: Bearer $SDK_KEY" | jq -r '.data.session_id')
# 2. LLM 채팅 세션 생성 (body 없음)
curl -X POST https://api.ones1ght.com/v1/chat/$PROFILE_ID \
-H "Authorization: Bearer $SDK_KEY"
# 3. stamp CHAT 으로 전환
curl -X POST https://api.ones1ght.com/v1/profile/$PROFILE_ID/session/stamp \
-H "Authorization: Bearer $SDK_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"CHAT"}'
# 4. 추천 kickoff
curl -X POST https://api.ones1ght.com/v1/chat/$PROFILE_ID/recommend \
-H "Authorization: Bearer $SDK_KEY" \
-H "Content-Type: application/json" \
-d "{\"session_id\":\"$SESSION_ID\",\"prompt\":\"오늘 저녁 카레 땡기는데\"}"
# 5. 폴링 (0.5s 간격)
while true; do
RESP=$(curl -s https://api.ones1ght.com/v1/chat/$PROFILE_ID/recommend/$SESSION_ID \
-H "Authorization: Bearer $SDK_KEY")
STATUS=$(echo "$RESP" | jq -r '.data.status')
[ "$STATUS" = "READY" ] && { echo "$RESP" | jq '.data.result'; break; }
[ "$STATUS" = "ERROR" ] && { echo "$RESP" | jq '.data.last_error'; break; }
sleep 0.5
done
# 6. 세션 종료
curl -X DELETE https://api.ones1ght.com/v1/chat/$PROFILE_ID/session/$SESSION_ID \
-H "Authorization: Bearer $SDK_KEY"