spree-api-v3 について
spree-api-v3は、Spree 6のストアフロント、管理者、マーケットプレイス出品者向けREST APIを解説する開発者向けスキルです。認証、権限、リソースの規約、検索、ページネーション、カートから注文までの流れを扱います。
主な機能
- 01Store・Admin・Seller APIの解説
- 02認証と権限の説明
- 03カート・チェックアウト・注文フローの記述
- 04検索・ページネーション・展開項目の解説
- 05ID・フィールド・レスポンス形式の説明
活用シーン
- 01Spreeストアフロント連携の構築
- 02バックオフィスやERPとの接続
- 03出品者パネルのAPIアクセス実装
- 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を含む403access_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 は送信も期待もしないでください。一般的な接頭辞は次のとおりです。
| Prefix | Resource | Prefix | Resource |
|---|---|---|---|
prod_ | 商品 | variant_ | バリアント |
cart_ | カート | or_ | 注文 |
ogrp_ | OrderGroup(分割チェックアウト) | li_ | LineItem |
py_ | 支払い | re_ | 返金 |
ful_ | フルフィルメント | ret_ | 返品 |
tl_ | TaxLine | disc_ | 割引 |
fee_ | 手数料 | cust_ | 顧客 |
adm_ | AdminUser | addr_ | 住所 |
ctg_ | カテゴリー | coll_ | コレクション |
sl_ | StockLevel | sloc_ | StockLocation |
mkt_ | マーケット | ch_ | チャネル |
sel_ | 販売者 | media_ | メディア |
cf_ | CustomField | promo_ | プロモーション |
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 |
|---|---|
| 401 | authentication_required、invalid_token、authentication_failed、invalid_refresh_token |
| 403 | access_denied(details.required_scope / required_permission)、channel_mismatch |
| 404 | record_not_found、cart_not_found、order_not_found、line_item_not_found、variant_not_found |
| 422 | validation_error(属性ごとの details。フォームレベルのエラーには base)、cart_cannot_complete、insufficient_stock、payment_failed、idempotency_key_reused |
| 413 | request_too_large(本文が max_request_body_size を超過、100 KB) |
| 429 | rate_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) | ウィンドウあたり 600 | API 全体でのシークレットキーごと |
rate_limit_window(SPREE_RATE_LIMIT_WINDOW) | 60 s | |
rate_limit_login | 5 | IP ごと:ストア/管理者/販売者のログイン、招待の承諾 |
rate_limit_register | 3 | IP ごと:顧客登録、ニュースレターの購読 |
rate_limit_refresh | 10 | IP ごと:リフレッシュ、ログアウト、管理者認証プロバイダー |
rate_limit_password_reset | 3 | IP ごと |
レスポンスには 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。