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