Get product by ID
const url = 'https://api.example.com/products/example';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.example.com/products/example \ --header 'Authorization: Bearer <token>'Retrieve the full enriched product record by its unique identifier.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Responses
Section titled “ Responses ”The product record.
The external product representation — what GET /products/search
and GET /products/{productId} return to an M2M caller. This is the
FULL contract: every field this schema documents is everything the
endpoint can ever return to this caller class. status is internal
metadata and deliberately absent, as are the remaining cost/margin
fields other than supplierCost — see the “Field Policy” section
above.
object
Whether the product should be published/shown on the consuming integrator’s site (e.g. WooCommerce published vs. draft). For a client bound to a web channel, this is always an explicit true/false reflecting that channel’s visibility. For an unbound client, it reflects the product’s stored visibility flag, which may be omitted when no flag is stored — treat a missing value as not visible.
Public-facing short description shown to API consumers. Still labelled “Internal Notes” in the CRM UI pending the
Product colour/finish.
Trade selling price.
Recommended retail price (RRP).
Wholesale/cost-basis price. Is returned; treat as commercially sensitive and never expose it to end consumers.
object
Product documents (spec sheets, manuals). Same {url, name} shape as images.
object
Supplier/ERP identifier — also what q= search matches for identifier lookups. Used for an ERP catalogue-sync integration.
Alternate supplier identifier.
Second alternate supplier identifier.
Website navigation/category tree path.
Top-level product classification.
Second-level product classification.
Third-level product classification.
Product range/collection name shown on the integrator’s site. Distinct from range, which is frequently empty for the same products.
A PROJECTED view of related-items relationships, never
the raw persisted shape. productCode/erpCode are inline display
fields present ONLY on GET /products/{productId} (enrichment runs
before projection there); GET /products/search entries carry only
productId/relationType/qty/source. note/name/imageUrl/archived
are never present; spares is never exposed at all.
object
Inline display field — GET /products/{productId} only.
Inline display field — GET /products/{productId} only.
Present only on server-derived reverse (used by) entries.
Example
{ "erpCode": "B018704", "webHierarchy": "Bathrooms;Accessories;Heated Towel Rails", "retailRange": "Luv", "relatedProducts": [ { "relationType": "required", "source": "reverse" } ]}Authentication required or failed.
object
Machine-readable code. Best-effort — the generic 500 path carries none.
Unique request ID for tracing.
object
Example
{ "error": "UNAUTHORIZED", "message": "Valid authentication token required."}Insufficient permissions (missing scope or Admin role).
object
Machine-readable code. Best-effort — the generic 500 path carries none.
Unique request ID for tracing.
object
Example
{ "error": "FORBIDDEN", "message": "Insufficient scope for this operation."}Resource not found.
object
Machine-readable code. Best-effort — the generic 500 path carries none.
Unique request ID for tracing.
object
Example
{ "error": "NOT_FOUND", "message": "Product not found"}