--- sidebar_position: 4 --- # Go Live: Full Product Onboarding import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; This guide walks through the complete lifecycle for getting a product live on noon — from discovering the right category to confirming the listing is active. By the end, your integration will handle the full create-monitor-fix loop and know exactly what triggers a product going live. ## How it works Every product passes through the same sequence. The fix loop between submission and active can repeat multiple times — once for content gaps, once for image review, once for QC. ```mermaid sequenceDiagram participant You participant API as Content API participant QC as noon QC Team You->>API: ListCategories API-->>You: valid category codes You->>API: ListCategoryAttributes(category_code) API-->>You: attributes + validation rules You->>API: UpsertProduct(skus, brand, category, images, attributes) API-->>You: sku_parent + status loop Until overall_status = OVERALL_STATUS_ACTIVE You->>API: GetContent(sku_parent) API-->>You: completeness, image review, qc.status alt Content below 100% You->>API: UpsertProduct (add missing / fix invalid attributes) else Image invalid or missing You->>API: UpsertProduct (replace or add images) else QC_STATUS_PENDING Note over QC: noon reviews — wait and poll else QC_STATUS_REJECTED You->>API: UpsertProduct (fix per rejection_reasons) end end API-->>You: overall_status = OVERALL_STATUS_ACTIVE ``` **Why the loop exists:** QC only starts after content completeness hits 100%. Images are reviewed independently and in parallel. Each fix cycle requires another `UpsertProduct` call followed by a `GetContent` poll. Plan for at least two cycles on first submission. **Go-live gates — all must be true for a language to become active:** | Gate | What noon checks | Where you see it in GetContent | |---|---|---| | Content completeness | 100% of mandatory attributes present and valid | `statuses[].content.completeness` = `"100%"` | | Image | At least one image is `REVIEW_STATUS_VALID` and `VISIBILITY_STATUS_VISIBLE` | `images[].review_status` + `images[].visibility` | | QC | `QC_STATUS_APPROVED` | `statuses[].qc.status` | | Quota | An available product slot in your monthly allowance | Returned as an error on `UpsertProduct` if exceeded | ## Prerequisites - Authentication set up — see [Getting Credentials][getting-credentials] and [Authenticating Your Requests][authenticating-requests] - An active noon partner account with API access configured ## Step 1 — Discover your category and its attributes `ListCategories` (`POST /content/v1/categories/list`) gives you the valid category code for your product. `ListCategoryAttributes` (`POST /content/v1/categories/attributes/list`) gives you the full attribute contract — every mandatory field and its validation rules. Run both calls once per category and cache the results. Category codes and attribute definitions change infrequently. See [Discover Category Attributes][content-discover-attributes] for the complete implementation including code examples and attribute type rules. **Done when:** you have a category code and a mapped list of mandatory attributes with valid values ready for submission. ## Step 2 — Submit the product `UpsertProduct` (`POST /content/v1/product/upsert`) creates the product or updates it if your `partner_sku` values already exist. Returns `sku_parent` — store it immediately. ```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}], "attributes": { "product_title": { "values": [ {"value": "Acme Runner Sneaker", "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"] # A 200 does not mean content is valid — always check status_id assert result["status"]["status_id"] == 0, result["status"]["message"] ``` ```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 }], attributes: { product_title: { values: [ { value: "Acme Runner Sneaker", language: "LANGUAGE_EN" }, { value: "حذاء اكمي رانر", language: "LANGUAGE_AR" }, ], }, }, }, { headers: { "Content-Type": "application/json", "User-Agent": "MyCatalogApp/1.0.0", "X-Project": "", }, } ); const { sku_parent, status } = response.data; if (status.status_id !== 0) throw new Error(status.message); ``` **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": ""} } ``` **Done when:** `status.status_id` is `0` and `sku_parent` is stored. | Error | Meaning | Fix | |---|---|---| | Non-2xx | Auth failure, missing `X-Project`, invalid category, or quota exceeded | Check `rpcStatus.message` | | `status_id: 3` within 200 | Content stored but has problems | Proceed to Step 3 to diagnose | For the full attribute structuring rules (localizable, multivalued), see [Submit a Product][content-submit-product]. ## Step 3 — Check status and identify blockers `GetContent` (`POST /content/v1/product/content/get`) returns the current completeness, image review state, and QC status. Call it after every `UpsertProduct` to see what changed. ```python response = session.post( "https://noon-api-gateway.noon.partners/content/v1/product/content/get", json={"sku_parent": sku_parent}, headers={"User-Agent": "MyCatalogApp/1.0.0"}, timeout=30, ) response.raise_for_status() content = response.json() for status in content["statuses"]: lang = status["language"] overall = status["overall_status"] completeness = status["content"]["completeness"] qc = status["qc"]["status"] print(f"{lang}: {overall} | completeness={completeness} | qc={qc}") ``` ```javascript const response = await client.post( "https://noon-api-gateway.noon.partners/content/v1/product/content/get", { sku_parent }, { headers: { "Content-Type": "application/json", "User-Agent": "MyCatalogApp/1.0.0" } } ); for (const status of response.data.statuses) { const { language, overall_status, content, qc } = status; console.log(`${language}: ${overall_status} | completeness=${content.completeness} | qc=${qc.status}`); } ``` **Response:** ```json { "sku_parent": "Z1ABC234", "images": [ { "url": "https://cdn.example.com/acme-runner-main.jpg", "sort": 1, "visibility": "VISIBILITY_STATUS_VISIBLE", "review_status": "REVIEW_STATUS_PENDING", "issues": [] } ], "statuses": [ { "language": "LANGUAGE_EN", "content": { "completeness": "80%", "missing_attributes": ["long_description"], "invalid_attributes": [] }, "qc": {"status": "QC_STATUS_NOT_ELIGIBLE", "rejection_reasons": [], "comment": ""}, "overall_status": "OVERALL_STATUS_INACTIVE", "errors": [] } ] } ``` **Done when:** every entry in `statuses` has `overall_status: "OVERALL_STATUS_ACTIVE"`. For the complete diagnostic logic — what each QC status means, image status combinations, and the full error code table — see [Track and Fix][content-track-and-fix]. ## Step 4 — Fix and resubmit Call `UpsertProduct` again with the same `partner_sku` values to update the product with corrected content. Then return to Step 3. Repeat until all statuses are active. ``` Submit → Check → Fix → Check → Fix → … → Active ``` ## Testing Use the noon sandbox environment to run through the full lifecycle before going to production — see [Static Sandbox][sandbox] for setup. Trigger the flow by calling `UpsertProduct` in sandbox with a test product. Then poll `GetContent` to observe the status progression: 1. After first submission: expect `completeness` below 100% if any mandatory attributes are missing, and `review_status: "REVIEW_STATUS_PENDING"` on images. 2. After completing content: expect `completeness: "100%"` and `qc.status` moving from `QC_STATUS_NOT_ELIGIBLE` to `QC_STATUS_PENDING`. 3. After QC approval and a valid image: expect `overall_status: "OVERALL_STATUS_ACTIVE"`. :::note The sandbox (PIPR) returns static responses that match the schema's data types — it does not run real QC review. Expect enum fields like `qc.status` and `overall_status` to return valid values immediately rather than progressing through the real approval lifecycle. Use sandbox to validate your request structure and response parsing, not to simulate the full go-live sequence. ::: Common failure modes: | Symptom | Likely cause | Check | |---|---|---| | `completeness` stuck below 100% after resubmit | Missing or invalid mandatory attribute | `content.missing_attributes` and `content.invalid_attributes` | | `qc.status` stays `QC_STATUS_NOT_ELIGIBLE` | Completeness never reached 100% | Fix all missing/invalid attributes first | | Image stays `REVIEW_STATUS_PENDING` indefinitely | Image review is asynchronous and may take time | Wait and poll; if persistent, check image URL accessibility | | Product active in EN but inactive in AR | Arabic mandatory attributes missing or incomplete | Check `statuses` entry for `LANGUAGE_AR` separately | ## Next steps - [Submit a Product][content-submit-product] — detailed attribute structuring rules and SKU patterns - [Track and Fix][content-track-and-fix] — complete diagnostic guide for all blocker types - [UpsertProduct Reference][content-upsert-product-api] — full request and response schema - [GetContent Reference][content-get-content-api] — full request and response schema