Products API
All endpoints are under /api/v1/{platform}/products where platform is shopify or bigcommerce.
List products
GET /api/v1/shopify/products?limit=50&offset=0&storeId=...
Authorization: Bearer sk_live_...
X-Tenant-Id: <tenant-id>
Response:
{
"products": [
{
"id": "...",
"title": "...",
"vendor": "...",
"price": 29.99,
"currency": "EUR",
"identity": "...",
"enrichment_status": "enriched",
"manually_edited": false,
"last_enriched_at": "2026-04-14T...",
"url": "/api/shopify/products/...",
"llm_url": "/api/shopify/products/...llm"
}
],
"total": 281,
"pagination": { "limit": 50, "offset": 0, "count": 50, "has_more": true }
}
Get one product (full semantic payload)
GET /api/v1/shopify/products/:id
Returns the full SemanticProduct with core_identity, reasoning, synthetic_properties, technical_details, metadata.
LLM-optimized variant
GET /api/v1/shopify/products/:id.llm
Returns a condensed JSON tuned for LLM consumption — identity, reasoning, quality_scores, pricing, market_context, plus a pre-computed summary_for_ai string.
JSON-LD (schema.org/Product)
GET /api/v1/shopify/products/:id.jsonld
Returns application/ld+json with @type: Product, offers, brand, and scores mapped to additionalProperty[] as PropertyValue. If FAQ entries exist for the product, bundles Product + FAQPage into a single @graph array. Crawler-ready.
Markdown (text/markdown)
GET /api/v1/shopify/products/:id.md
Returns text/markdown; charset=utf-8 with a structured Markdown document: title, core identity, product details, quality scores table, buying guide, market context, search keywords, and images. Addresses the Cloudflare agent-readiness metric for LLM discoverability.
SEO meta signals
GET /api/v1/shopify/products/:id.meta
Returns SEO signals in 3 description lengths (≤160 chars, ≤300, unlimited) + canonical URL + keywords + links object pointing to all other signal formats (.jsonld, .llm, .md, self).
Signal endpoints for ALL entity types
Every entity type (products, collections, categories, pages) exposes the same four signal format endpoints. The contract is identical — only the base path changes.
Base paths
| Platform | Entity | Base path |
|---|---|---|
| Shopify | Products | /api/v1/shopify/products |
| Shopify | Collections | /api/v1/shopify/collections |
| Shopify | Pages | /api/v1/shopify/pages |
| BigCommerce | Products | /api/v1/bigcommerce/products |
| BigCommerce | Categories | /api/v1/bigcommerce/categories |
| BigCommerce | Pages | /api/v1/bigcommerce/pages |
Signal formats (available on ALL entities)
| Suffix | Content-Type | Description |
|---|---|---|
/:id.jsonld | application/ld+json | Schema.org JSON-LD. Bundles entity schema + FAQPage in @graph if FAQ entries exist. |
/:id.llm | application/json | LLM-optimized representation with identity, context, keywords, enrichment data. |
/:id.md | text/markdown; charset=utf-8 | Structured Markdown for LLM crawlers. Cloudflare agent-readiness metric. |
/:id.meta | application/json | All SEO signals: meta title, description, keywords, OG tags, + links to all other formats. |
Examples
# Collection JSON-LD (Shopify)
GET /api/v1/shopify/collections/summer-2026.jsonld
# Category Markdown (BigCommerce)
GET /api/v1/bigcommerce/categories/spray-bottles.md
# Page meta signals (both platforms)
GET /api/v1/shopify/pages/about-us.meta
GET /api/v1/bigcommerce/pages/shipping-policy.meta
.meta response shape (all entities)
{
"collection_id": "summer-2026",
"meta_title": "Summer 2026 | Store Name",
"meta_description": "AI-enriched description...",
"keywords": ["summer", "collection", "2026"],
"og": {
"og_title": "...",
"og_description": "...",
"og_type": "website",
"og_image": { "url": "...", "alt": "..." },
"og_url": "...",
"og_site_name": "..."
},
"links": {
"jsonld": "/api/v1/shopify/collections/summer-2026.jsonld",
"llm": "/api/v1/shopify/collections/summer-2026.llm",
"md": "/api/v1/shopify/collections/summer-2026.md",
"self": "/api/v1/shopify/collections/summer-2026"
}
}
Re-enrich a single product
POST /api/v1/shopify/enrich/:id
Authorization: Bearer <JWT>
Content-Type: application/json
{
"hints": {
"target_audience": "...",
"material_composition": "..."
}
}
Hints are optional; they're the same fields the Enrichment Wizard collects.
Bulk re-enrich
POST /api/v1/shopify/enrich/all?pending=true&storeId=<storeId>
pending=true only re-enriches products flagged enrichment_pending. Without it, re-enriches every product in the tenant (dangerous — use the dashboard's two-step confirm).
Versions + rollback
GET /api/v1/shopify/enrich/:id/versions → list past versions
POST /api/v1/shopify/enrich/:id/rollback?version=N → restore version N
The rollback itself creates a new version snapshot so it's reversible.
Update a product's enrichment manually
PATCH /api/v1/shopify/products/:id # also available on /bigcommerce/
{
"core_identity": "...",
"reasoning": { ... },
"synthetic_properties": {
"durability_score": 8,
"competitors_owner_provided": [
{ "brand": "Nike", "model": "Air Max 90", "ref_price": "€120", "notes": "direct competitor" }
]
}
}
Whitelisted top-level fields: core_identity, reasoning, synthetic_properties. Flags the product with:
metadata.manually_edited = true— future re-enrichments show an OVERWRITE two-step confirm in the dashboardmetadata.competitors_source = 'owner'— only whensynthetic_properties.competitors_owner_providedis a non-empty array. Sets authority on the competitors list so re-enrichment won't regenerate it.
Update category / collection metadata
PATCH /api/v1/shopify/collections/:id # Shopify → "collections"
PATCH /api/v1/bigcommerce/categories/:id # BigCommerce → "categories"
Whitelisted fields for bulk-editing SEO metadata:
{
"name": "...",
"description": "...",
"slug": "...",
"imageUrl": "https://...",
"metaTitle": "...",
"metaDescription": "..."
}
Tenant-scoped — the request's tenant must own the entity or the endpoint returns 404. Empty string values are coerced to null. Returns the updated row.
Verify storefront — per-product diff
GET /api/v1/shopify/diff/:id?store_url=https://yourstore.com
Fetches your live storefront and compares every signal Clione propagated (meta_description, meta_title, jsonld, canonical, meta_keywords) against what the HTML actually contains right now.
Per-signal statuses: identical | different | missing_in_storefront | never_propagated | propagation_failed | parse_error.
Response includes a summary (match_rate excludes never_propagated), a per-signal diff, and — for JSON-LD — a structural breakdown of added/removed/changed keys.
Side effect: refreshes verifiedAt + verifiedPresent on the underlying PropagationRecords.
Verify storefront — batch
GET /api/v1/shopify/diff?store_url=https://yourstore.com&product_ids=id1,id2,id3
Same contract as /diff/:id but across an array of products. Aggregates totals across all products + returns per-product results under results[]. One slow product won't kill the batch — failures come back as { entity_id, error, summary: null }.
Used by the dashboard's Verify storefront button on the store overview page. Hard-capped to ~50 products per call in the UI to keep response time manageable.