--- sidebar_position: 2 --- # Submit a Product import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; This guide shows you how to submit a product to noon using `UpsertProduct`. By the end, you'll have a product stored in noon's catalog with a `sku_parent` you can use to track its status. ## Prerequisites - Authentication set up — see [Getting Credentials][getting-credentials] and [Authenticating Your Requests][authenticating-requests] - A valid category code and attribute mapping for your product — see [Discover Category Attributes][content-discover-attributes] ## Step 1 — Submit the product `UpsertProduct` (`POST /content/v1/product/upsert`) creates a product if the `partner_sku` values are new, or updates the existing product they belong to. Pass `X-Project` as a required header to scope the call to your project. The example below submits a two-size shoe product with localizable title and brand attributes. ```python # get_authenticated_session() is provided in docs/snippets/auth.mdx — see Authenticating Your Requests session = get_authenticated_session() response = session.post( "https://noon-api-gateway.noon.partners/content/v1/product/upsert", json={ "skus": [ {"partner_sku": "MY-SNK-42", "size": "42"}, {"partner_sku": "MY-SNK-43", "size": "43"}, ], "brand": "Acme", "category": "apparel-shoes-sneakers", "images": [ {"url": "https://cdn.example.com/acme-runner-main.jpg", "sort": 1}, {"url": "https://cdn.example.com/acme-runner-side.jpg", "sort": 2}, ], "attributes": { "product_title": { "values": [ {"value": "Acme Runner Sneaker", "language": "LANGUAGE_EN"}, {"value": "حذاء اكمي رانر", "language": "LANGUAGE_AR"}, ] }, "long_description": { "values": [ {"value": "Lightweight everyday runner with cushioned sole.", "language": "LANGUAGE_EN"}, {"value": "حذاء خفيف الوزن للاستخدام اليومي مع نعل مبطن.", "language": "LANGUAGE_AR"}, ] }, }, }, headers={ "User-Agent": "MyCatalogApp/1.0.0", "X-Project": "", }, timeout=30, ) response.raise_for_status() result = response.json() sku_parent = result["sku_parent"] ``` ```javascript // getAuthenticatedClient() is provided in docs/snippets/auth.mdx — see Authenticating Your Requests const client = await getAuthenticatedClient(); const response = await client.post( "https://noon-api-gateway.noon.partners/content/v1/product/upsert", { skus: [ { partner_sku: "MY-SNK-42", size: "42" }, { partner_sku: "MY-SNK-43", size: "43" }, ], brand: "Acme", category: "apparel-shoes-sneakers", images: [ { url: "https://cdn.example.com/acme-runner-main.jpg", sort: 1 }, { url: "https://cdn.example.com/acme-runner-side.jpg", sort: 2 }, ], attributes: { product_title: { values: [ { value: "Acme Runner Sneaker", language: "LANGUAGE_EN" }, { value: "حذاء اكمي رانر", language: "LANGUAGE_AR" }, ], }, long_description: { values: [ { value: "Lightweight everyday runner with cushioned sole.", language: "LANGUAGE_EN" }, { value: "حذاء خفيف الوزن للاستخدام اليومي مع نعل مبطن.", language: "LANGUAGE_AR" }, ], }, }, }, { headers: { "Content-Type": "application/json", "User-Agent": "MyCatalogApp/1.0.0", "X-Project": "", }, } ); const { sku_parent } = response.data; ``` **Response:** ```json { "sku_parent": "Z1ABC234", "variants": [ {"sku": "N12345678", "partner_sku": "MY-SNK-42", "psku_code": "P0012345", "size": "42"}, {"sku": "N12345679", "partner_sku": "MY-SNK-43", "psku_code": "P0012346", "size": "43"} ], "status": {"status_id": 0, "status_code": "OK", "message": ""} } ``` You'll know this worked when `status.status_id` is `0` and `sku_parent` is present in the response. **Store `sku_parent` immediately.** It is the only handle accepted by `GetContent` and the stable identity for all future updates to this product. | Condition | What it means | What to do | |---|---|---| | Non-2xx response | Request failed entirely — auth error, missing `X-Project`, quota exceeded, or invalid category | Check `rpcStatus.message`. For quota errors, the message states your limit and period. | | `status.status_id: 3` (`INVALID_ARGUMENT`) within a 200 | Product was stored but has content problems | Note the `sku_parent`, then call `GetContent` for the detailed breakdown — see [Track and Fix][content-track-and-fix]. | :::warning A 200 response does not mean your content is valid. Always check `status.status_id` — `0` means accepted cleanly; `3` means stored with problems. ::: ## SKU and size rules - **Single-SKU product:** `size` is optional. Omit it if your product has no size dimension. - **Multi-SKU product:** every SKU in the request must have `size` set. - **Updates:** all `partner_sku` values in a single request must belong to the same parent product. noon uses `partner_sku` as the identity key — sending an existing `partner_sku` triggers an update, not a create. - `partner_sku` must be globally unique across all your products, not just within a single product. ## Structuring attribute values Attributes are sent as a map of `attribute_code` → `{ "values": [...] }`. How you populate `values` depends on the attribute's flags from `ListCategoryAttributes`: **Non-localizable, single value:** ```json "colour": { "values": [{"value": "Red"}] } ``` **Localizable (is_localizable: true) — one value per language:** ```json "product_title": { "values": [ {"value": "Acme Runner Sneaker", "language": "LANGUAGE_EN"}, {"value": "حذاء اكمي رانر", "language": "LANGUAGE_AR"} ] } ``` **Multivalued (is_multivalued: true) — use sort to order values:** ```json "feature_bullet": { "values": [ {"value": "Cushioned sole", "language": "LANGUAGE_EN", "sort": 1}, {"value": "Breathable mesh upper", "language": "LANGUAGE_EN", "sort": 2} ] } ``` **Metric (ATTRIBUTE_TYPE_METRIC) — numeric value and unit as separate attributes:** Metric attributes are always submitted as a pair: the numeric attribute and a corresponding `_unit` attribute. The unit value must exactly match one of the strings in `attribute_metric_units` from `ListCategoryAttributes`. Both must be present in the same `UpsertProduct` call — you cannot update one without the other. ```json "product_length": { "values": [{"value": 10, "language": "LANGUAGE_EN"}] }, "product_length_unit": { "values": [{"value": "millimeter", "language": "LANGUAGE_EN"}] } ``` ## Next steps - [Track and Fix][content-track-and-fix] — check why your product isn't live yet and fix it - [UpsertProduct Reference][content-upsert-product-api] — full request and response schema