メインコンテンツへスキップ
Client SDK キー(osk_ プレフィックス)で認証します。レコメンド実行は非同期(202) + ポーリング構造です。

フロー

1. POST   /v1/profile/{profile_id}/session/start        (プロフィールセッション開始 → session_id 発行, stamp=READY)
2. POST   /v1/chat/{profile_id}                         (チャットセッション作成 → chat_id 発行)
3. POST   /v1/chat/{profile_id}/recommend               (kickoff → result: true)
4. GET    /v1/chat/{profile_id}/recommend/{chat_id}     (0.5 秒間隔でポーリング)
5. DELETE /v1/chat/{profile_id}/end/{chat_id}       (チャット終了、ユーザー状態変更)
クライアントは profile_id と 1 番で受け取った session_id (2 番では chat_id にリネームされて返却、UUID は同一) を保持すれば OK。認証セッションと混同しないよう /chat エンドポイントでは chat_id キーで統一しています。name / gender / age / locale はサーバがプロフィール行から読み取り、自動で LLM プレリュードを組み立てます。

セッションステータス

ポーリング制御用。
意味クライアント動作
in_progressパイプライン実行中0.5 秒後に再ポーリング
completedレコメンド完了 (todos / notification_items 添付)消費してポーリング終了
errorパイプライン失敗再 kickoff もしくはセッション終了

1. チャットセッション作成

POST /v1/chat/{profile_id}
リクエスト body は不要。プロフィールに登録済みの name / gender / age / locale をサーバが読み取って LLM の user_query プレリュードを自動生成します。 前提: POST /v1/profile/{profile_id}/session/startプロフィールセッションを先に開始 しておく必要があります。未開始だと SESSION_NOT_STARTED 409。

レスポンス 200

{
  "success": true,
  "data": {
    "chat_id": "a14b2f9c-3d5e-4f2a-9a8b-1c2d3e4f5a6b"
  }
}
chat_id はプロフィールセッション ID (/v1/profile/{profile_id}/session/startsession_id) と同じ UUID。以降の recommend/poll/delete 全てでそのまま使用します。現在の stamp 状態が必要なら GET /v1/profile/{profile_id}/session を呼んでください。

2. レコメンド kickoff

POST /v1/chat/{profile_id}/recommend
前提: プロフィールセッションが アクティブ + stamp=READY であること (/session/start 直後のデフォルト)。CHAT など他スタンプでは STAGE_NOT_READY 409、終了済みセッションでは SESSION_INACTIVE 409 で拒否されます。

リクエスト

{
  "chat_id": "a14b2f9c-3d5e-4f2a-9a8b-1c2d3e4f5a6b",
  "prompt": "今夜のおかず何がいい?"
}
フィールド必須説明
chat_idstring2 番で受け取ったチャットセッション ID
promptstringユーザー問い合わせ。内部で llm-demo の user_query にマッピング

レスポンス 200

{
  "success": true,
  "data": { "result": true }
}
result: true はレコメンドパイプラインの kickoff 成功を示します。結果は ポーリングエンドポイント (GET /v1/chat/{profile_id}/recommend/{chat_id}) で取得してください。失敗時は success: false + エラーコードで返り、result フィールドは含まれません。

3. ポーリング

GET /v1/chat/{profile_id}/recommend/{chat_id}
推奨間隔 0.5 秒。レスポンスは status によって形が変わります。読み取り専用 — ステージ遷移は DELETE のみ。 既に終了したチャット (DELETE 済み or llm-demo 1 時間 TTL 経過) をポーリングすると NOT_FOUND 404 (chat session ended)。

status: "in_progress"

{
  "success": true,
  "data": {
    "created_at": "2026-04-23T05:28:08.056493+00:00",
    "updated_at": "2026-04-23T05:29:10.001000+00:00",
    "status": "in_progress",
    "back_list_status": "in_progress",
    "todos": null,
    "recommended_items": null,
    "notification_items": null
  }
}
ポーリング継続。todos / notification_items はパイプライン確定前は null

status: "completed" (完了)

{
  "success": true,
  "data": {
    "created_at": "2026-04-23T05:28:08.056493+00:00",
    "updated_at": "2026-04-23T05:29:30.922285+00:00",
    "status": "completed",
    "back_list_status": "completed",
    "todos": {
      "recipe_title":       "매콤 소고기 카레",
      "intro_message":      "오늘 메뉴는 매콤 소고기 카레 어떠세요? 제가 장보기 목록을 정리해 드렸어요.",
      "recipe_description": "일본식 카레. 소고기 + 양파 + 감자를 기본으로 카레 루를 녹여 20분 끓이면 완성.",
      "items": [
        {
          "quantity": 1,
          "product": {
            "id": 196,
            "name": "소 목심 얇은썰기 300g",
            "barcode": "4907065390564",
            "price": 5900,
            "price_unit": "₩",
            "section_code": "MT",
            "section_name": "육류",
            "src_url": "https://cdn.ones1ght.com/store/product/no_image.png"
          }
        }
      ],
      "total": 5900
    },
    "recommended_items": [
      {
        "reason": "카레 재료와 동시구매율이 높은 상품입니다 (lift 4.2, 동시구매 확률 16.9%). 현재 쇼핑 리스트의 카레 루, 양파와 함께 구매하면 조리 완성도가 높아집니다.",
        "product": {
          "id": 512,
          "name": "카베르네 소비뇽 750ml",
          "barcode": "4901234567890",
          "price": 12000,
          "price_unit": "₩",
          "section_code": "AL",
          "section_name": "주류",
          "src_url": "https://cdn.ones1ght.com/store/product/no_image.png"
        }
      }
    ],
    "notification_items": [
      {
        "discount_pct": 5,
        "priority_score": 0.95,
        "rank": 1,
        "product": {
          "id": 860,
          "name": "히게타 간장 1L",
          "barcode": "4906244055584",
          "price": 4400,
          "price_unit": "₩",
          "section_code": "PA",
          "section_name": "식료품",
          "src_url": "https://cdn.ones1ght.com/store/product/no_image.png"
        }
      }
    ]
  }
}
完了判定: status == "completed"
  • todos — 買い物リスト (旧 shopping_list)。recipe_title / intro_message / recipe_description は llm-demo meal 出力から取得 — intro_message が上段チャット吹き出し用のフック、recipe_description はカード本文。各 items[*]{quantity, product} に圧縮、store マッチ不成立の要素は除外、total は Σ(product.price × quantity)。
  • recommended_itemsメニューに合う cross-sell (llm-demo upsell_surface.surface_recommendations 原本、通常 3~5個)。各要素は {reason, product}reason は llm-demo reason_detail 原文 (プロンプト契約上 lift / confidence 等の数値を含む) をそのまま。iOS が一度に 1 個レンダリング、「추천받기 / もっと見る」ボタンはクライアント側でカート遷移として処理。
  • notification_items — 店内ゾーン通知候補 (back_list.back_recommendations 原本)。各要素 {discount_pct, priority_score, rank, product} — 識別子は product.id のみ。rankpriority_score 降順 1-indexed、store マッチ不成立の要素は除外。product に将来の追加フィールド (座標等) が入ってもそのまま公開。

status: "error"

{
  "success": true,
  "data": {
    "created_at": "...",
    "updated_at": "...",
    "status": "error",
    "back_list_status": "not_started",
    "last_error": "meal_suggest failed: upstream timeout",
    "todos": null,
    "recommended_items": null,
    "notification_items": null
  }
}
last_error は llm-demo パイプライン失敗理由 (例: meal_suggest failed: upstream timeout)。error 時のみ含まれ、success / in-progress 応答には出ません。再 kickoff もしくはセッション終了。 備考: llm-demo 内部項目 (conversation_state / preferences / profile_context / meal / upsell_surface / timing) はレスポンスに含まれません。

4. チャット終了、ユーザー状態変更

DELETE /v1/chat/{profile_id}/end/{chat_id}
DELETE /v1/chat/{profile_id}/end/{chat_id}?next_session_status=PAYMENT
llm-demo チャットセッションは常にクローズ。プロフィールセッション はクエリパラメータで分岐: DELETE = チャット終了 + 次ステージへの移行。どちらの変形でも llm-demo チャットセッションはクローズされます。
next_session_status動作
未指定プロフィールセッションに自動 COMPLETE スタンプ + 全体終了 (アクティブスレッド整理、is_active=false、stamps 履歴返却)
値ありカスタムスタンプを記録後、プロフィールセッションは 活性維持 (次ステージへ移行)
プロフィールセッションのライフサイクル — アプリ起動から決済完了までの 1 サイクル:
READY  →  CHAT  →  [custom, custom, ...]  →  COMPLETE
 ↑        ↑         ↑                         ↑
session   recommend  DELETE ?next_session_status=  DELETE (no param)
/start    kickoff    X (自由ラベル)             自動 COMPLETE + セッション終了
予約語:
  • READY/session/start 直後に自動付与
  • CHAT/recommend kickoff で自動付与
  • COMPLETE — DELETE (no param) で自動付与、サイクル終結
この 3 つを ?next_session_status に指定すると RESERVED_STAMP 409。CHAT ~ COMPLETE の間はクライアントが自由にラベル (SHOPPING, PAYMENT など) を付けて、アプリクラッシュからの復帰時に画面復元に使用します。

レスポンス 200 — パラメータ未指定 (セッション終了)

{
  "success": true,
  "data": {
    "chat_id": "...",
    "profile_id": "...",
    "started_at": "2026-04-23T05:28:00Z",
    "ended_at": "2026-04-23T05:35:00Z",
    "stamps": [
      { "status": "READY",    "timestamp": "2026-04-23T05:28:00Z" },
      { "status": "CHAT",     "timestamp": "2026-04-23T05:29:05Z" },
      { "status": "PAYMENT",  "timestamp": "2026-04-23T05:33:00Z" },
      { "status": "COMPLETE", "timestamp": "2026-04-23T05:35:00Z" }
    ]
  }
}

レスポンス 200 — ?next_session_status=PAYMENT (スタンプ)

{
  "success": true,
  "data": {
    "chat_id": "...",
    "status": "PAYMENT",
    "timestamp": "2026-04-23T05:32:00Z"
  }
}
明示的な DELETE がなくても 1 時間非活動 で自動終了。

エラーコード

HTTPcode原因
400INVALID_REQUESTbody バリデーション失敗
404NOT_FOUNDprofile / session が存在しない or 終了済み
409SESSION_NOT_STARTEDプロフィールセッション未開始でチャットセッション作成を試みた
409SESSION_INACTIVE終了済みセッションに対して recommend を呼び出した
409STAGE_NOT_READYrecommend 呼び出し時にスタンプが READY ではない
500INTERNAL_ERRORアップストリーム障害等

フル例

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

# 1. プロフィールセッション開始
CHAT_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')   # same UUID as chat_id

# 2. LLM チャットセッション作成 (body なし)
curl -X POST https://api.ones1ght.com/v1/chat/$PROFILE_ID \
  -H "Authorization: Bearer $SDK_KEY"

# 3. レコメンド kickoff ({ "result": true } を確認できたらポーリングへ)
curl -X POST https://api.ones1ght.com/v1/chat/$PROFILE_ID/recommend \
  -H "Authorization: Bearer $SDK_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"chat_id\":\"$CHAT_ID\",\"prompt\":\"今夜のおかず何がいい?\"}"

# 4. ポーリング (0.5s)
while true; do
  RESP=$(curl -s https://api.ones1ght.com/v1/chat/$PROFILE_ID/recommend/$CHAT_ID \
    -H "Authorization: Bearer $SDK_KEY")
  STATUS=$(echo "$RESP" | jq -r '.data.status')
  [ "$STATUS" = "completed" ] && { echo "$RESP" | jq '.data'; break; }
  [ "$STATUS" = "error" ]     && { echo "$RESP" | jq '.data'; break; }
  sleep 0.5
done

# 5. 終了
curl -X DELETE https://api.ones1ght.com/v1/chat/$PROFILE_ID/session/$CHAT_ID \
  -H "Authorization: Bearer $SDK_KEY"