SiteChat向けに商品カタログをアップロードする方法
ご自身のウェブサイトで SiteChat を利用している場合(Shopifyストアではない場合)、構造化された商品レコードをShoplyに送信することで、購入者がSiteChatのチャットや商品検索でそれらを見つけられるようになります。
これらのエンドポイントでは、Shoply Merchant API の他の機能と同じ Settings → Store owner API secret を使用します。また、SiteChat管理画面の Product Catalog ページでは、サインイン中の管理者アクセストークンを使ってこれらを呼び出せます。Shopifyストアは引き続きShopify経由でカタログデータを同期します。これらのアップロード用ルートは、Magentoカタログをすでに同期していないSiteChatアカウントでのみ利用できます。
SiteChat管理画面では、Product Catalog を開くと、Shopify風の管理エリアとして Products、Collections、Inventory が表示されます。商品を追加・編集したり、CSV/Excelをインポートしたり、商品を手動コレクションにまとめたり、在庫数を調整したりできます。
利用できるAPIは何ですか?
| API | Method | 何をするか |
|---|---|---|
/merchant/products/batch | POST | 1回のリクエストで最大50件の商品を作成または完全置換します |
/merchant/products | POST | 1件の商品を作成または完全置換します |
/merchant/products | GET | source と external_id により、インポート済み商品1件を読み戻します |
/merchant/products | PATCH | 商品1件を部分更新します。バリエーションやmetafieldsを置き換えるには、GETで取得したバージョンが必要です |
/merchant/products | DELETE | 商品をソフトアーカイブします(status=archived) |
/merchant/products/list | GET | 管理画面のテーブル用に、インポート済み商品をページネーションしながら取得します |
/merchant/products/images | POST | 商品画像をアップロードし、公開HTTPS URLを受け取ります |
/merchant/collections | GET / POST | 手動コレクションを一覧表示または作成します |
/merchant/collections/{id} | GET / PUT / DELETE | コレクション1件を読み取り、置換、またはアーカイブします |
/merchant/inventory | GET | 在庫行(商品およびバリエーションの数量)を一覧表示します |
/merchant/inventory/adjust | POST | 商品またはバリエーションの追跡対象数量を設定します |
商品の保存に成功すると、Shoplyにストアインデックスの再構築が依頼されます。その再構築が完了するまでは、レスポンスの index_status: "pending" が返されます。公開済み商品はその後、商品インデックスとSiteChatチャットで使用されるナレッジの両方に表示されます。
これらのAPIを使えるのは誰ですか?
- ストアは SiteChat アカウントである必要があります(
app_platformが SiteChat)。 - Shopifyストアのキー(
*.myshopify.comドメインを含む)は拒否されます。 - 認証には、有効なストアオーナーAPIシークレット、またはリクエスト内の正確な
store_keyに対応するSiteChat管理者アクセストークンのいずれかを使う必要があります。 - Shopify Admin APIトークンではこれらのルートを呼び出せません。
オーナーシークレットの作成またはローテーションは、他のMerchant API連携と同じ方法で行います: Shoply Merchant APIの使い方。SiteChat管理コンソールでは、Product Catalog を開くことで、シークレットを自分で管理しなくても商品、コレクション、在庫の管理や、CSV/Excelファイルのインポートができます。
認証はどのように動作しますか?
サーバーコネクタから
信頼できるバックエンドからHTTPS経由でAPIを呼び出します。Authorization ヘッダーにはJSON文字列を入れます:
{
"store_key": "YOUR_SITECHAT_STORE_KEY",
"store_owner_api_secrete": "YOUR_STORE_OWNER_API_SECRET"
}store_owner_api_secrete は公開フィールド名であり、歴史的なスペルを含んでいます。store_owner_api_secret も受け付けられます。これは Bearer トークンではありません。
SiteChat管理画面から
Product Catalog ページは、代わりにサインイン中の管理者セッションを送信します:
{
"store_key": "YOUR_SITECHAT_STORE_KEY",
"admin_auth_token": "YOUR_SITECHAT_ACCESS_TOKEN"
}access_token は admin_auth_token の別名として受け付けられます。
store_key クエリパラメータは、ヘッダーと一致している必要があります。オーナーシークレットはサーバーサイドの環境変数にのみ保存してください。ストアフロントのスクリプト、URL、公開リポジトリには絶対に含めないでください。
export SHOPLY_STORE_KEY="your-sitechat-store-key"
export SHOPLY_STORE_OWNER_API_SECRETE="shoply_owner_key_example123.secret-value"商品はどのようにアップロードしますか?
POST https://api.shoplyai.ai/merchant/products/batch?store_key=YOUR_SITECHAT_STORE_KEY
{
"source": "woocommerce-main",
"products": [
{
"external_id": "123",
"title": "Trail shoes",
"description": "Water-resistant hiking shoes.",
"url": "https://example.com/products/trail-shoes",
"currency": "USD",
"price": "49.95",
"original_price": "59.95",
"available": true,
"status": "published",
"images": ["https://example.com/images/trail-shoes.jpg"],
"categories": ["Footwear"],
"metafields": {"material": "Leather"},
"variants": [
{
"external_id": "124",
"title": "Brown / 42",
"sku": "TRAIL-BR-42",
"price": "49.95",
"available": true,
"metafields": {"color": "Brown", "size": "42"}
}
]
}
]
}必須フィールドとルール
- 必須の商品フィールドは
external_id、title、url、currency、price、availableです。statusのデフォルトはpublishedです。 - 任意の在庫:
tracks_inventoryをtrueにし、商品またはバリエーションに0以上のquantityを設定します。するとShoplyは在庫から販売可否(quantity > 0)を導出し、管理一覧用にtotal_inventoryを保存します。 sourceはカタログ接続名を示します(必ずしもプラットフォームとは限りません)。woocommerce-mainのような安定した名前を使用してください。使用可能な文字は小文字、数字、アンダースコア、ハイフンで、最大64文字です。管理画面で作成した商品は、デフォルトで source がadminになります。- 商品の識別子は、ストア、source、external ID の組み合わせです。バッチおよび単体の
POSTアップサートは 完全置換 であり、パッチではありません。省略した任意フィールドはクリアされます。部分更新にはPATCHを使用してください。 - バッチには1〜50件の商品を含めることができ、リクエストサイズは最大2,000,000バイトです。各商品の検証済みJSONは128,000バイトまで、バリエーションは最大250件です。
- 価格には10進文字列を推奨します。通貨は3文字の大文字コードです。
- 商品URLおよび画像URLはHTTP(S)である必要があります。インポート時にそれらのURLが自動取得されることはありません。
- 検索可能な商品仕様には公開
metafieldsを使用してください(SiteChat版のShopify商品metafieldsに相当)。値はプレーンな文字列である必要があります。従来のattributesも別名として受け付けられます。非公開の商品metadataは現在サポートされていません。 draftおよびarchivedの商品は、次回の公開インデックスから除外されます。公開済みで売り切れの商品は、販売可否情報付きで引き続きインデックスに残ります。- 手動コレクションには、タイトル、説明、ステータス、および商品の所属リスト(
source+external_id)が保存されます。スマート/ルールベースのコレクションは、このリリースではサポートされていません。
成功レスポンスの例
{
"results": [
{
"external_id": "123",
"product_id": "sitechat_product::woocommerce-main::<sha256-of-external-id>",
"status": "stored"
}
],
"index_status": "pending",
"index_revision": 1
}Shoplyは書き込み前にリクエスト全体を検証します。HTTP 200 であっても、一部の failed 結果に error: "storage_error" が含まれる場合があります。すべての結果を確認し、失敗した商品を再試行してください。index_status が request_failed の場合、保存自体は成功していますがインデックス処理がスケジュールされていません。その場合も、保存済み商品を再試行してください。
Pythonの例
import json
import os
import requests
store_key = os.environ["SHOPLY_STORE_KEY"]
headers = {
"Authorization": json.dumps({
"store_key": store_key,
"store_owner_api_secrete": os.environ["SHOPLY_STORE_OWNER_API_SECRETE"],
})
}
response = requests.post(
"https://api.shoplyai.ai/merchant/products/batch",
params={"store_key": store_key},
headers=headers,
json={
"source": "custom",
"products": [{
"external_id": "123",
"title": "Trail shoes",
"url": "https://example.com/products/trail-shoes",
"currency": "USD",
"price": "49.95",
"available": True,
}],
},
timeout=60,
)
response.raise_for_status()
result = response.json()
failed = [item["external_id"] for item in result["results"] if item["status"] != "stored"]
if failed:
raise RuntimeError(f"Products need retry: {failed}")
if result["index_status"] == "request_failed":
raise RuntimeError("Products saved, but retry the batch to request indexing")インポート済み商品はどのように確認しますか?
GET https://api.shoplyai.ai/merchant/products?store_key=YOUR_SITECHAT_STORE_KEY&source=woocommerce-main&external_id=123
同じ Authorization ヘッダーを使用します。レスポンスには schema_version、updated_at、index_status、および正規化された product が含まれます。存在しないレコードにはHTTP 404 が返されます。
バックグラウンドのインデクサーが再構築を公開すると、商品ごとの読み戻しステータスは indexed、excluded(draftまたはarchivedの商品)、または limit_exceeded になります。管理画面で一覧表示するには GET /merchant/products/list を使用してください。次回の公開インデックスから商品を除外したい場合は、DELETE /merchant/products によるソフトアーカイブ(または status を archived / draft に設定)を使ってください。このリリースにはハードデリートはありません。
record = requests.get(
"https://api.shoplyai.ai/merchant/products",
params={
"store_key": store_key,
"source": "woocommerce-main",
"external_id": "123",
},
headers=headers,
timeout=30,
)
record.raise_for_status()
print(record.json()["index_status"], record.json()["product"]["title"])バリエーションやMetafieldsを安全に更新するには?
variants、metafields、または従来の attributes フィールドを含むPATCHを行う場合は、まずGETで商品を読み取ってください。そのレスポンスをもとに編集内容を作成し、そのトップレベルの updated_at の値を expected_updated_at としてPATCH本文に含めます。これはバリエーションやmetafieldsをクリアする場合にも適用されます。
current_response = requests.get(
"https://api.shoplyai.ai/merchant/products",
params={"store_key": store_key, "source": "woocommerce-main", "external_id": "123"},
headers=headers,
timeout=30,
)
current_response.raise_for_status()
current = current_response.json()
updated_metafields = {**current["product"].get("metafields", {}), "material": "Leather"}
response = requests.patch(
"https://api.shoplyai.ai/merchant/products",
params={"store_key": store_key},
headers=headers,
json={
"source": "woocommerce-main",
"external_id": "123",
"expected_updated_at": current["updated_at"],
"metafields": updated_metafields,
},
timeout=30,
)
response.raise_for_status()HTTP 409 は、バージョンが不足しているか、別の更新によって商品が変更されたことを意味します。再試行する前に最新の商品を読み取り、意図した変更をもう一度マージしてください。古いバリエーション一覧に新しいバージョンを付けて送らないでください。新しい在庫値を上書きしてしまう可能性があります。
これらのフィールドをバージョンなしでPATCHしている既存の連携は更新が必要です。スカラー値のみのPATCHリクエスト、専用の在庫調整、またはPOSTやバッチインポートによる完全置換では、このフィールドは不要です。完全置換は引き続き商品全体を置き換えます。
商品画像はアップロードできますか?
はい。画像がすでに永続的な公開HTTPS URLで利用可能な場合は、そのURLを商品の images リストまたはバリエーションの image フィールドに入れてください。公開S3 HTTPS URLは利用できます。生の s3:// パスや短期間で失効する署名付きURLは利用できません。
ファイルをShoplyでホストしたい場合は、バックエンドから生の画像バイトをアップロードします:
POST https://api.shoplyai.ai/merchant/products/images?store_key=YOUR_SITECHAT_STORE_KEY
Content-Type: image/png送信するのは 生のファイルバイト であり、JSON、base64、multipart form data ではありません。JPEG、PNG、WebP が受け付けられ、上限は 10 MiB、2,000万ピクセル です。アニメーション画像はサポートされていません。Shoplyは画像をWebPに変換し、埋め込まれたメタデータを削除します。
with open("trail-shoes.png", "rb") as image_file:
response = requests.post(
"https://api.shoplyai.ai/merchant/products/images",
params={"store_key": store_key},
headers={**headers, "Content-Type": "image/png"},
data=image_file,
timeout=60,
)
response.raise_for_status()
image_url = response.json()["url"]HTTP 201 では url、content_type、size_bytes、width、height が返されます。画像だけをアップロードしても、それが商品に紐づけられたり、インデックス処理が依頼されたりすることはありません。返されたURLを完全な商品データに含め、/merchant/products/batch を通じて再度送信してください。
同じアカウントで同じ画像を再度アップロードすると、そのURLは再利用されます。画像が変更されると、新しいURLが発行されます。このリリースには画像削除エンドポイントはありません。
商品はいつチャットと検索に表示されますか?
バッチ保存が成功すると、Shoplyはバックグラウンドでのインデックス再構築を依頼します。ワーカーが新しいインデックスを公開するまで、レスポンスには index_status: "pending" が表示されます。完了時間についての固定保証はありません。
- 公開済み商品は、商品検索とSiteChatチャットで使われるナレッジの両方に入ります。
- draftおよびarchivedの商品は、次回の再構築で除外されます。
- インデックス処理がスケジュールされなかった場合(
index_status: "request_failed")、すでに保存に成功している商品についてバッチを再試行してください。
どのようなエラーが想定されますか?
| HTTP status | 意味 |
|---|---|
403 | シークレットが不足、無効、期限切れ、または失効済みである/ストアが違う/またはSiteChat以外のアカウント |
404 | 要求されたインポート済み商品が存在しない |
413 | リクエストまたは画像がサイズ制限を超えている |
415 | 画像アップロードで未対応の Content-Type が使われた |
422 | 無効なフィールド、重複ID、アニメーション画像またはサイズ超過画像、またはモデル制限超過 |
503 | 一時的なストレージ障害 |
シークレットの有効期限は 90日 です。期限切れになる前に Settings → Store owner API secret で置き換え用を作成し、不要になったシークレットは失効させてください。同じシークレットは、Merchant API guide に記載されている分析、会話、ナレッジの各エンドポイントにも使用できます。
連携についてサポートが必要な場合は、Shoply AIにお問い合わせください。
