mcpField
S
spree-api-v3

spree-api-v3

github.com/spree開発10226 回インストール4 週間前に更新
リポジトリ

spree-api-v3 について

spree-api-v3は、Spree 6のストアフロント、管理者、マーケットプレイス出品者向けREST APIを解説する開発者向けスキルです。認証、権限、リソースの規約、検索、ページネーション、カートから注文までの流れを扱います。

主な機能

  1. 01Store・Admin・Seller APIの解説
  2. 02認証と権限の説明
  3. 03カート・チェックアウト・注文フローの記述
  4. 04検索・ページネーション・展開項目の解説
  5. 05ID・フィールド・レスポンス形式の説明

活用シーン

  1. 01Spreeストアフロント連携の構築
  2. 02バックオフィスやERPとの接続
  3. 03出品者パネルのAPIアクセス実装
  4. 04APIリクエストの移行や調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/spree/agent-skills --skill spree-api-v3

インストール後、Claude Code で/spree-api-v3を入力して読み込まれたか確認します。

同梱ファイルreferences/scopes.md

詳細

Spree API v3

Spree 6 には /api/v3/ 配下に 3 つの REST サーフェスがあり、共通の規約を使用します。

サーフェスパス呼び出し元認証認可
Store API/api/v3/store/*ストアフロント、モバイルアプリ、購入者X-Spree-Api-Key: pk_… + 任意の Authorization: Bearer <jwt>(aud: store_api)+ ゲスト用 X-Spree-Token所有権(spree-auth-permissions を参照)
Admin API/api/v3/admin/*インテグレーション、バックオフィスアプリ、ダッシュボードX-Spree-Api-Key: sk_… または Authorization: Bearer <jwt>(aud: admin_api)キーのスコープ/ロール権限キー
Seller API/api/v3/seller/*マーケットプレイスの販売者パネル販売者 JWT(aud: seller_api)+ X-Spree-Seller-Id販売者ロールキー + current_seller によるスコープ設定(spree-marketplace を参照)

ある audience 向けに発行されたトークンは、他のサーフェスでは拒否されます。Spree 6 には v2 Storefront API/Platform API はありません。旧インテグレーションを移行する場合は、node_modules/@spree/docs/dist/api-reference/store-api/migrating-from-storefront-api-v2.md を参照してください。

Store API

  • 匿名での閲覧に必要なのは X-Spree-Api-Key: pk_… のみです。公開可能キーはクライアントコードに含めても安全です。
  • ゲストのカート/チェックアウト/注文では、X-Spree-Token: <cart.token> も送信します。
  • サインイン済みの顧客は、POST /api/v3/store/auth/login の Authorization: Bearer <jwt> も送信します。レスポンスボディには token、refresh_token、user が含まれます。POST /auth/refresh と { "refresh_token": "…" } を使って更新します。
  • コンテキストヘッダー:
    • X-Spree-Country はマーケットを解決し、デフォルトのロケールと通貨を設定します。
    • X-Spree-Locale と X-Spree-Currency は、それらのデフォルト値を上書きします。
    • X-Spree-Channel: <code> はチャネルを選択します。指定がない場合はストアのデフォルトが使用されます。チャネルに紐付けられた公開可能キーはチャネル自体を決定し、競合するヘッダーを送信すると channel_mismatch が返されます。
  • レスポンスは顧客に表示できる内容のみです。原価や内部メモは含まれません。ゲストに価格を表示しないチャネルでは、金額フィールドは null で返されるため、型は string | null としてください。
  • Store API はデフォルトで読み取り専用です。書き込みは、カート、チェックアウト、顧客本人のアカウント(/customers/me/*)、ウィッシュリスト、顧客本人の注文に対する返品/申し立て、ニュースレター/会社のセルフサービスに限定されます。
curl -H "X-Spree-Api-Key: pk_xxx" -H "X-Spree-Country: DE" \
     "https://shop.example.com/api/v3/store/products?q[name_cont]=shirt&expand=default_variant"
カートと注文は異なるリソースです

カート(cart_…、/api/v3/store/carts)は変更可能で、ステータスを持ちません。完了すると、変更不可の注文(or_…)が作成されます。

POST   /carts                         → cart (token in response)
POST   /carts/{id}/items              { variant_id, quantity }
PATCH  /carts/{id}                    { email, shipping_address, billing_address, use_shipping, … }
PATCH  /carts/{id}/fulfillments/{id}  { selected_delivery_rate_id }
POST   /carts/{id}/payment_sessions   … gateway flow …
POST   /carts/{id}/complete           → the Order (or an OrderGroup for split marketplace checkouts)
GET    /orders/{id}                   → order by or_ id, OR by the original cart_ id
  • 完了後に GET /carts/{id} を実行すると 404(cart_not_found)が返されます。完了したカートはカート検索の対象外になるためです。結果を確認するには GET /orders/{cart_id} を使います。注文エンドポイントにはカートのプレフィックス付き ID を渡すことができ、そのカートから作成された注文(order.cart_id)が解決されます。ゲストは引き続き同じ X-Spree-Token を使用します。
  • POST /carts/{id}/complete は安全に再試行できます。決済 webhook がすでにカートを完了している場合、再試行すると既存の注文が返されます。
  • カートの requirements[] には、完了を妨げている要件が { step, field, code, message } の形式で一覧表示されます(例:{ "step": "delivery", "field": "delivery_method", "code": "delivery_method_required" })。各ステップはあくまで参考情報であり、完了処理だけが確実な判定基準です。ステータス名ではなく requirements に基づいてチェックアウト UI を構成してください。spree-checkout を参照してください。
  • クーポン:POST /carts/{id}/discount_codes { code }。カートと注文の両方に coupon_code が含まれます。
  • サインイン済みの顧客の注文履歴は GET /customers/me/orders で確認できます。
使用する注文フィールド
  • status(Admin API):draft | placed | canceled。q[status_eq]=placedで絞り込みます。
  • payment_status:none、authorized、partially_paid、paid、partially_refunded、refunded、overcharged、voided。
  • fulfillment_status:unfulfilled、partial、fulfilled、delivered、backorder、canceled(移行済みの注文には、旧形式のpending/ready/shippedが表示される場合があります)。
  • 合計:item_total、delivery_total、discount_total、tax_total(included_/additional_)、fee_total、gift_card_total、store_credit_total、total、amount_due。それぞれに対応するdisplay_*もあります。
  • withdrawal_period_ends_at/within_withdrawal_period(EUの撤回権)。

state、payment_state、shipment_state、ship_total/promo_total/adjustment行はありません。金額は税の明細、割引、手数料として項目別に表示されます(spree-order-totalsを参照)。

Admin API

シークレットキー(連携):X-Spree-Api-Key: sk_…。キーによってストアが選択されます。 JWT(ユーザー):POST /api/v3/admin/auth/login { email, password }(またはSSOの場合は{ provider, … })。レスポンス本文は{ token, user }です。リフレッシュトークンは/api/v3/admin/authをスコープとするHttpOnly cookieとして設定され、POST /auth/refreshには本文が不要です。Admin JWTのデフォルトの有効期限は5分です。複数ストアを扱うホストでは、X-Spree-Store-Idを送信してください。

リクエストに両方の認証情報が含まれる場合は、JWTが優先されます。

権限

Adminの各コントローラーでは、アクションが1つのキーに対応しています。index/showはread_<resource>、その他のすべてのアクション(/orders/{id}/cancelのようなメンバーアクションを含む)はwrite_<resource>に対応します。write_*にはread_*の権限も含まれます。

  • シークレットキーはスコープに照らして検証されます。失敗すると、details.required_scopeを含む403 access_deniedが返されます。
  • JWTで認証されたスタッフは、このストアにおけるロールの権限キーに照らして検証されます。失敗すると、details.required_permissionを含む403が返されます。この詳細のない403は、レコード単位のルールによって拒否されたことを意味します。これらのルールはSpree.ability_classに由来し、Spree::Dependencies.ability_classで差し替え可能です。シークレットキーには代わりにSpree::ApiKeyAbilityが使用されます。spree-auth-permissionsを参照してください。
  • キーによって、表示される行が決まることはありません。すべてのエンドポイントで、現在のストアを通じてスコープが適用されます。
  • シークレットキーのスコープは作成時に固定されます。変更するには、新しいキーを発行して古いキーを失効させてください。

グループおよび対象リソースを含む全スコープの一覧は、references/scopes.mdにあります。実行時には、GET /api/v3/admin/permissionsで同じ一覧を取得でき、RubyではSpree::ApiKey.known_scopesで参照できます。よく使われる項目:

Order sync out ............ read_orders (+ read_payments, read_fulfillments)
Warehouse / 3PL ........... read_orders, write_fulfillments, write_stock
ERP product feed .......... write_products (+ write_categories / write_collections)
Refund automation ......... read_orders, write_refunds
Staff provisioning ........ write_staff
Everything (avoid) ........ write_all

スタッフのロールはデータです(/api/v3/admin/roles)。spree-auth-permissionsを参照してください。

Admin専用のレスポンス特性
  • タイムスタンプ、内部メモ、原価、status、およびストアフロントに表示されないカスタムフィールド。
  • アクターのペア。 アクションを実行したユーザーを記録するレコード(canceler、approver、created_by、refunderなど)では、*_idと*_typeが公開されます。IDはスタッフの場合はadm_…、シークレットキーの場合はkey_…で、*_typeはadmin_userまたはapi_keyです。*_typeは拡張される可能性がある値として扱ってください。expand=cancelerを指定すると、{ id, type, label }が返されます。

共通の規則

接頭辞付き ID

ID は読み取り時も書き込み時も、常に Stripe 形式の接頭辞付き文字列です("variant_id": "variant_k5nR8xLq")。整数の ID は送信も期待もしないでください。一般的な接頭辞は次のとおりです。

PrefixResourcePrefixResource
prod_商品variant_バリアント
cart_カートor_注文
ogrp_OrderGroup(分割チェックアウト)li_LineItem
py_支払いre_返金
ful_フルフィルメントret_返品
tl_TaxLinedisc_割引
fee_手数料cust_顧客
adm_AdminUseraddr_住所
ctg_カテゴリーcoll_コレクション
sl_StockLevelsloc_StockLocation
mkt_マーケットch_チャネル
sel_販売者media_メディア
cf_CustomFieldpromo_プロモーション
gc_ギフトカードrole_ロール
key_ApiKey レコード(pk_/sk_ トークンではありません)store_ストア

接頭辞によってリソースの種類が識別されるため、エンベロープに type フィールドはありません。その他の接頭辞は grep -rn has_prefix_id spree/core/app/models で確認してください。Spree 6 には Adjustment が存在しないため、adj_ はありません。

エンベロープ、ページネーション、ソート

一覧は { data: [...], meta: { page, limit, count, pages, from, to, in, previous, next } } を返します。単一レコードはオブジェクト単体で返されます。ページネーション:?page=2&limit=50(デフォルトは 25、最大は 100)。ソート:?sort=-completed_at,number。

展開とフィールド
  • ?expand=default_variant,media,variants.prices は関連付けをサイドロードします。ドット記法でネストされた関連付けを指定できます。使用可能なキーはリソースごとに異なります(OpenAPI 仕様を参照)。
  • Admin API の展開はセグメントごとに権限チェックされます。order(s) には read_orders、payment(s)/payment_splits には read_payments、customer(s) には read_customers、gift_card(s) には read_gift_cards、store_credit(s) には read_store_credits が必要です(そのため orders.payments には両方の権限が必要です)。呼び出し元が権限を持たないパスは、403 ではなく黙って削除されます。ギフトカードの code は、呼び出し元が read_gift_cards を持っていない場合、末尾 4 文字を除いてマスクされます。
  • ?fields=name,slug はスパースレスポンスを返します(id は常に含まれます)。
フィルタリング(Ransack)

q[<attr>_<predicate>]=value の形式です。例:q[name_cont]=shirt、q[status_eq]=placed、q[completed_at_gteq]=2026-09-01。述語には _eq、_not_eq、_in、_cont、_start、_gt、_gteq、_lt、_lteq、_present、_blank があります。モデルの Ransack 許可リストにない属性は黙って無視されます。そのフィルターが削除された状態で 200 が返されます。Spree.ransack.add_attribute(Spree::Product, :brand_id) で許可リストに追加できます。

許可リストは利用者ごとに絞り込まれます(Ransack の auth_object:Admin は nil、:store、:seller)。

  • Store API は、モデルの storefront_ransackable_associations にある関連付けのみをたどります(products の場合は tags、categories、collections、その他すべてのモデルでは空)。また、モデルの private_ransackable_attributes[:store] にある属性を除外します(例:注文の email、considered_risky、coupon_code と search/multi_search スコープ、バリアントの cost_price、ストアクレジットの memo)。
  • Seller API は関連付けをたどらず、private_ransackable_attributes[:seller] にある属性を除外します(注文の email、considered_risky、coupon_code に加え、search/multi_search スコープ)。
  • Admin API は許可リスト全体を利用できます。そのため、Spree.ransack.add_association(Spree::Product, :brand) を追加すると、スタッフのみ q[brand_name_eq] が機能します。購入者にもこのフィルターを使わせるには、Spree::Product.storefront_ransackable_associations |= %w[brand] を追加してください(ストアフロントでデータを公開してよい関連付けに限ります)。add_attribute で追加した属性は、プライベートとして指定されていない限り、すべての利用者に適用されます。
住所

first_name、last_name、address1、city、postal_code、country_code(ISO2)、state_code を使用してください。カートでは use_shipping: true を指定すると、配送先住所が請求先住所にコピーされます。country_iso/state_abbr は引き続きレスポンスに含まれますが、非推奨です。

リッチテキスト

リッチテキスト属性は、プレーンテキストの description と、サニタイズ済み HTML の description_html の両方が返されます。*_html をレンダリングし、プレーンテキスト側の名前に書き込みます(送信する値は HTML です)。Spree は保存時にサニタイズします。

金額

金額は小数の 文字列("29.99")で、それぞれに書式設定済みの display_* 文字列が対応します。価格が非表示の場合は null になります。書き込み時、価格は配列で指定します:"prices": [{ "currency": "USD", "amount": "29.99" }]。

冪等性

POST/PUT/PATCH/DELETE では Idempotency-Key: <uuid>(最大 255 文字)を送信してください。24 時間以内であれば、同じキーと本文で再試行すると、保存済みのレスポンスが再生されます(ヘッダー Idempotent-Replayed: true)。別の本文でキーを再利用すると、422 idempotency_key_reused が返されます。5xx レスポンスはキャッシュされません。SDK と CLI はキーを自動的に追加します。

エラー

{ "error": { "code": "validation_error", "message": "Email can't be blank",
             "details": { "email": ["can't be blank"] } } }
ステータス代表的な code
401authentication_required、invalid_token、authentication_failed、invalid_refresh_token
403access_denied(details.required_scope / required_permission)、channel_mismatch
404record_not_found、cart_not_found、order_not_found、line_item_not_found、variant_not_found
422validation_error(属性ごとの details。フォームレベルのエラーには base)、cart_cannot_complete、insufficient_stock、payment_failed、idempotency_key_reused
413request_too_large(本文が max_request_body_size を超過、100 KB)
429rate_limit_exceeded

レート制限

Rails の rate_limit をベースとしており、カウンターは Rails.cache に保存されます。このキャッシュはプロセス間で共有されている必要があります(Solid Cache または Redis)。Spree::Api::Config または環境変数で設定します。

設定項目(環境変数)デフォルトバケット
rate_limit_per_key(SPREE_RATE_LIMIT_PER_KEY)ウィンドウあたり 300公開可能キー +クライアント IP(訪問者ごと);キーが送信されない場合はクライアント IP
rate_limit_per_secret_key(SPREE_RATE_LIMIT_PER_SECRET_KEY)ウィンドウあたり 600API 全体でのシークレットキーごと
rate_limit_window(SPREE_RATE_LIMIT_WINDOW)60 s
rate_limit_login5IP ごと:ストア/管理者/販売者のログイン、招待の承諾
rate_limit_register3IP ごと:顧客登録、ニュースレターの購読
rate_limit_refresh10IP ごと:リフレッシュ、ログアウト、管理者認証プロバイダー
rate_limit_password_reset3IP ごと

レスポンスには X-RateLimit-Limit、X-RateLimit-Remaining が含まれ、制限に達した場合は Retry-After も含まれます。受信する決済/フルフィルメントの Webhook エンドポイントには、独自の固定制限として 120/min が設定されています。

デバッグの手順

  • どこでも 401 が返る:Admin API に pk_ キーを送信している、または Store API に sk_ キーを送信している、キーが失効している、管理者 JWT の発行から 5 分以上経過している(Cookie を使ってリフレッシュしてください)、顧客トークンを Admin API に送信している(aud が誤っています)。
  • Admin で 403 が返る:details.required_scope / required_permission を確認してください。キーの場合は、そのスコープを持つ新しいキーを発行してください。スタッフの場合は、このストアでそのキーをスタッフのロールに追加してください。
  • チェックアウト直後にカートが 404 になる:これは想定どおりです。GET /orders/{cart_id} を取得してください。
  • レコードがあるのにリストが空:チャネルが間違っている(X-Spree-Channel またはチャネルに紐づくキー)、そのチャネルで商品が公開されていない、要求された通貨またはマーケットに価格がない、または Ransack フィルターが暗黙に破棄された可能性があります。
  • ターミナルから確認する:spree api get /orders/or_xxx --expand payments,fulfillments(spree-cli を参照)。

さらに詳しく読むには

  • OpenAPI 仕様(正式な情報源):node_modules/@spree/docs/dist/api-reference/store.yaml、admin.yaml、seller.yaml
  • Store API ガイド:node_modules/@spree/docs/dist/api-reference/store-api/(認証、ローカライズ、冪等性、レート制限、クエリ、リレーション)
  • Admin API ガイド:node_modules/@spree/docs/dist/api-reference/admin-api/(認証、エラー、クエリ)
  • Webhook イベント:node_modules/@spree/docs/dist/api-reference/webhooks-events.md
  • 関連スキル:spree-auth-permissions、spree-typescript-sdk、spree-cli、spree-resource(エンドポイントの追加)、spree-checkout、spree-marketplace(Seller API)、spree-events-webhooks。

コメント