Skip to main content

Go Live: Full Product Onboarding

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.

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:

GateWhat noon checksWhere you see it in GetContent
Content completeness100% of mandatory attributes present and validstatuses[].content.completeness = "100%"
ImageAt least one image is REVIEW_STATUS_VALID and VISIBILITY_STATUS_VISIBLEimages[].review_status + images[].visibility
QCQC_STATUS_APPROVEDstatuses[].qc.status
QuotaAn available product slot in your monthly allowanceReturned as an error on UpsertProduct if exceeded

Prerequisites

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

# 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": "<project_code>"},
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"]

Response:

{
"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.

ErrorMeaningFix
Non-2xxAuth failure, missing X-Project, invalid category, or quota exceededCheck rpcStatus.message
status_id: 3 within 200Content stored but has problemsProceed to Step 3 to diagnose

For the full attribute structuring rules (localizable, multivalued), see Submit a 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.

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}")

Response:

{
"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.

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

SymptomLikely causeCheck
completeness stuck below 100% after resubmitMissing or invalid mandatory attributecontent.missing_attributes and content.invalid_attributes
qc.status stays QC_STATUS_NOT_ELIGIBLECompleteness never reached 100%Fix all missing/invalid attributes first
Image stays REVIEW_STATUS_PENDING indefinitelyImage review is asynchronous and may take timeWait and poll; if persistent, check image URL accessibility
Product active in EN but inactive in ARArabic mandatory attributes missing or incompleteCheck statuses entry for LANGUAGE_AR separately

Next steps