Skip to main content
Authenticated with the Client SDK key (osk_ prefix). Recommend runs async (202) + polling.

Flow

1. POST   /v1/profile/{profile_id}/session/start        (start Profile session → session_id)
2. POST   /v1/chat/{profile_id}                         (create LLM session; no body)
3. POST   /v1/profile/{profile_id}/session/stamp        (status="CHAT" stamp)
4. POST   /v1/chat/{profile_id}/recommend               (kickoff, 202)
5. GET    /v1/chat/{profile_id}/recommend/{session_id}  (poll at 0.5s intervals)
6. DELETE /v1/chat/{profile_id}/session/{session_id}    (end session)
The client only tracks profile_id and the session_id returned from step 1. name / gender / age / locale are read server-side from the profile row and turned into the LLM prelude automatically.

Session status

Drives polling.
ValueMeaningClient action
CHATPipeline runningRe-poll after 0.5s
READYRecommendation attachedIf back_list_status == "completed" → consume result
ERRORPipeline failedCheck last_error
ENDEDSession ended (DELETE or 1h idle)404 — create a new session

1. Create chat session

POST /v1/chat/{profile_id}
No request body. The server reads name / gender / age / locale directly from the profile row (populated at /v1/profile registration) and prepends a natural-language block to the LLM user_query. Prerequisite: POST /v1/profile/{profile_id}/session/start must have been called first. Otherwise → 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 equals the Profile session id and is reused across all subsequent calls.

2. Kickoff a recommendation

POST /v1/chat/{profile_id}/recommend
Prerequisite: the session’s latest stamp must be CHAT. Any other stage → STAGE_NOT_CHAT 409. Set the stamp via POST /v1/profile/{profile_id}/session/stamp.

Request

{
  "session_id": "a14b2f9c-3d5e-4f2a-9a8b-1c2d3e4f5a6b",
  "prompt": "What should I cook for dinner?"
}
FieldTypeRequiredDescription
session_idstringyesSession id from step 1
promptstringyesNatural-language query. Mapped to llm-demo’s user_query internally

Response 202

{
  "success": true,
  "data": {
    "session": { "session_id": "...", "status": "CHAT", "back_list_status": "not_started" }
  }
}
Re-kickoff while still CHAT → 409 Conflict.

3. Poll

GET /v1/chat/{profile_id}/recommend/{session_id}
Recommended interval: 0.5s. Response shape depends on status.

status: "CHAT" (in flight)

{
  "success": true,
  "data": {
    "session_id": "...",
    "status": "CHAT",
    "back_list_status": "not_started",
    "updated_at": "...",
    "last_error": null
  }
}
Keep polling.

status: "READY" (done)

{
  "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": "..." }
          }
        ]
      }
    }
  }
}
Completion check: status == "READY" AND back_list_status == "completed".

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 }
  }
}
Inspect last_error, then either kickoff again or end the session. Note: llm-demo internals — conversation_state / preferences / profile_context / meal / upsell_surface / timing — are not included in the response.

4. End session

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"
  }
}
Sessions auto-end after 1 hour of inactivity even without an explicit DELETE.

Error codes

HTTPcodeCause
400INVALID_REQUESTBody validation failed
404NOT_FOUNDProfile / session missing or ended
409SESSION_NOT_STARTEDChat session create attempted before Profile session start
409STAGE_NOT_CHATRecommend called while stamp isn’t CHAT
409CONFLICTSession already has a CHAT recommendation in flight
500INTERNAL_ERRORUpstream failure

Full example

SDK_KEY="osk_..."
PROFILE_ID="..."

# 1. Start profile session
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. Create LLM chat session (no 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\":\"Dinner ideas?\"}"

# 5. Poll (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. End
curl -X DELETE https://api.ones1ght.com/v1/chat/$PROFILE_ID/session/$SESSION_ID \
  -H "Authorization: Bearer $SDK_KEY"