Published

How to Upload Your Catalog for SiteChat

A secure merchant key unlocks catalog upload APIs that send products and images into SiteChat search and chat

If you use SiteChat on your own website (not a Shopify store), you can send structured product records to Shoply so shoppers can find them in SiteChat chat and product search.

These endpoints use the same Settings → Store owner API secret as the rest of the Shoply Merchant API, and the SiteChat admin Product Catalog page can call them with your signed-in admin access token. Shopify stores continue to sync catalog data through Shopify; these upload routes are available only for SiteChat accounts that do not already sync a Magento catalog.

In the SiteChat admin, Product Catalog opens a Shopify-style area with Products, Collections, and Inventory. You can add or edit products, import CSV/Excel, group products into manual collections, and adjust stock quantities.

What APIs Are Available?

APIMethodWhat it does
/merchant/products/batchPOSTCreate or fully replace up to 50 products in one request
/merchant/productsPOSTCreate or fully replace one product
/merchant/productsGETRead back one imported product by source and external_id
/merchant/productsPATCHPartially update one product (read-merge-write)
/merchant/productsDELETESoft-archive a product (status=archived)
/merchant/products/listGETPage through imported products for admin tables
/merchant/products/imagesPOSTUpload a product image and receive a public HTTPS URL
/merchant/collectionsGET / POSTList or create manual collections
/merchant/collections/{id}GET / PUT / DELETERead, replace, or archive one collection
/merchant/inventoryGETList stock rows (product and variant quantities)
/merchant/inventory/adjustPOSTSet tracked quantity for a product or variant

Successful product saves ask Shoply to rebuild the store index. Until that rebuild finishes, responses report index_status: "pending". Published products then appear in both the product index and the knowledge used by SiteChat chat.

Who Can Use These APIs?

  • Your store must be a SiteChat account (app_platform is SiteChat).
  • Shopify store keys (including any *.myshopify.com domain) are rejected.
  • Authentication must use either a valid store-owner API secret or a SiteChat admin access token for the exact store_key in the request.
  • Shopify Admin API tokens cannot call these routes.

Create or rotate the owner secret the same way as other Merchant API integrations: How to Use the Shoply Merchant API. In the SiteChat admin console, open Product Catalog to manage products, collections, and inventory—or import a CSV or Excel file—without managing the secret yourself.

How Does Authentication Work?

From a server connector

Call the API from a trusted backend over HTTPS. Put a JSON string in the Authorization header:

json
{ "store_key": "YOUR_SITECHAT_STORE_KEY", "store_owner_api_secrete": "YOUR_STORE_OWNER_API_SECRET" }

store_owner_api_secrete is the public field name, including the historical spelling. store_owner_api_secret is also accepted. This is not a Bearer token.

From the SiteChat admin

The Product Catalog page sends your signed-in admin session instead:

json
{ "store_key": "YOUR_SITECHAT_STORE_KEY", "admin_auth_token": "YOUR_SITECHAT_ACCESS_TOKEN" }

access_token is accepted as an alias for admin_auth_token.

The store_key query parameter must match the header. Keep owner secrets in server-side environment variables only—never in a storefront script, URL, or public repository.

bash
export SHOPLY_STORE_KEY="your-sitechat-store-key" export SHOPLY_STORE_OWNER_API_SECRETE="shoply_owner_key_example123.secret-value"

How Do I Upload Products?

POST https://api.shoplyai.ai/merchant/products/batch?store_key=YOUR_SITECHAT_STORE_KEY

json
{ "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"} } ] } ] }

Required fields and rules

  • Required product fields: external_id, title, url, currency, price, and available. status defaults to published.
  • Optional inventory: set tracks_inventory to true and a nonnegative quantity on the product or variant. Shoply then derives availability from stock (quantity > 0) and stores total_inventory for admin lists.
  • source names the catalog connection (not necessarily a platform). Use a stable name such as woocommerce-main. Allowed characters: lowercase letters, numbers, underscores, and hyphens; up to 64 characters. Form-created products in admin default to source admin.
  • Product identity is the combination of store, source, and external ID. Batch and single POST upserts are full replacements, not patches: omitted optional fields are cleared. Use PATCH for partial updates.
  • Batches contain 1–50 products and at most 2,000,000 request bytes. Each product’s validated JSON is limited to 128,000 bytes, with at most 250 variants.
  • Prefer decimal strings for prices. Currency is a three-letter uppercase code.
  • Product and image URLs must be HTTP(S). Importing does not fetch those URLs for you.
  • Use public metafields for searchable product specs (SiteChat’s analogue of Shopify product metafields). Values must be plain strings. Legacy attributes is accepted as an alias. Private product metadata is no longer supported.
  • draft and archived products are left out of the next published index. Sold-out published products stay indexed with availability attached.
  • Manual collections store a title, description, status, and a list of product memberships (source + external_id). Smart/rule-based collections are not supported in this release.

Sample success response

json
{ "results": [ { "external_id": "123", "product_id": "sitechat_product::woocommerce-main::<sha256-of-external-id>", "status": "stored" } ], "index_status": "pending", "index_revision": 1 }

Shoply validates the whole request before writing. An HTTP 200 can still list some failed results with error: "storage_error". Inspect every result and retry failed products. If index_status is request_failed, storage succeeded but indexing was not scheduled—retry the stored products as well.

Python example

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")

How Do I Verify an Imported Product?

GET https://api.shoplyai.ai/merchant/products?store_key=YOUR_SITECHAT_STORE_KEY&source=woocommerce-main&external_id=123

Use the same Authorization header. The response includes schema_version, updated_at, index_status, and the normalized product. Missing records return HTTP 404.

After the background indexer publishes a rebuild, per-product readback status becomes indexed, excluded (for draft or archived products), or limit_exceeded. Use GET /merchant/products/list for admin listing. Soft-archive with DELETE /merchant/products (or set status to archived / draft) to keep a product out of the next published index; there is no hard-delete in this release.

python
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"])

Can I Upload Product Images?

Yes. If an image is already available at a lasting public HTTPS URL, put that URL in the product images list or a variant’s image field. Public S3 HTTPS URLs work. Raw s3:// paths and short-lived signed URLs do not.

To have Shoply host the file, upload raw image bytes from your backend:

text
POST https://api.shoplyai.ai/merchant/products/images?store_key=YOUR_SITECHAT_STORE_KEY Content-Type: image/png

Send the raw file bytes, not JSON, base64, or multipart form data. JPEG, PNG, and WebP are accepted, up to 10 MiB and 20 million pixels. Animated images are not supported. Shoply converts the image to WebP and removes embedded metadata.

python
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 returns url, content_type, size_bytes, width, and height. Uploading an image alone does not attach it to a product or request indexing. Include the returned URL in a complete product and submit it again through /merchant/products/batch.

The same image uploaded again for the same account reuses its URL. A changed image receives a new URL. There is no image-delete endpoint in this release.

After a successful batch save, Shoply requests a background index rebuild. Responses report index_status: "pending" until the worker publishes the new index. There is no fixed completion-time guarantee.

  • Published products enter both product search and the knowledge used by SiteChat chat.
  • Draft and archived products are excluded on the next rebuild.
  • If indexing was not scheduled (index_status: "request_failed"), retry the batch for the products that already stored successfully.

What Errors Should I Expect?

HTTP statusMeaning
403Missing, invalid, expired, or revoked secret; wrong store; or a non-SiteChat account
404The requested imported product does not exist
413The request or image exceeds the size limit
415Image upload used an unsupported Content-Type
422Invalid fields, duplicate IDs, animated or oversized images, or model limits exceeded
503Temporary storage failure

Secrets expire after 90 days. Create a replacement in Settings → Store owner API secret before expiry, and revoke any secret you no longer need. The same secret can also call analytics, conversation, and knowledge endpoints documented in the Merchant API guide.

For help with an integration, contact Shoply AI.