noon-shared

star 7

Common noon Seller Center mechanics — login (OTP auto-fetch), page-structure URL map, My Catalog read access, common modals, button-click patterns, project-ID discovery. Prerequisite: every other noon-* skill (noon-listing, noon-fbn, noon-exports, noon-ads) expects this loaded for auth and shared UI patterns.

zpoint By zpoint schedule Updated 5/14/2026

name: noon-shared description: "Common noon Seller Center mechanics — login (OTP auto-fetch), page-structure URL map, My Catalog read access, common modals, button-click patterns, project-ID discovery. Prerequisite: every other noon-* skill (noon-listing, noon-fbn, noon-exports, noon-ads) expects this loaded for auth and shared UI patterns."

Noon — Shared (login, navigation, common patterns)

This skill covers what every noon Seller Center task needs: authentication, the page-structure URL map, My Catalog read access, and shared UI patterns (modals, button clicks). Operation-specific skills (noon-listing, noon-fbn, noon-exports, noon-ads) load this first.

1. Login — Auto-fetch OTP When Email Is Bound

Noon uses email OTP only (no password). Before doing ANYTHING, decide how the OTP will be retrieved — do NOT bother the user if an email MCP integration can do it for you.

Decision flow (follow in order, do NOT skip steps)

  1. Open the login page and look at the email it's about to send OTP to:

    browser-use open "https://login.noon.partners/en/"
    sleep 3 && browser-use state      # find the email on the "We've sent..."
                                      # screen OR the prefilled channelIdentifier
    

    Ziniao often auto-fills or remembers a session. The page shows Log in with OTP sent to <email>.

  2. Check whether the task prompt includes a "## Email System" section (the runner injects it when the store has connected email accounts). Compare the bound emails to the Noon login email shown on screen:

    • Match → fetch the OTP yourself via email MCP (Case A).
    • No email system section, OR emails do not match → ask the user (Case B). Never guess — mismatched emails mean a different inbox.

Case A — Auto-fetch OTP via email MCP (preferred)

This works when the bound email address equals the Noon login email. One round trip, no user prompt:

# 1. Trigger immediate IMAP poll (don't wait 5 min for auto-sync):
vibe_seller_sync_email_now(account_email='<bound-email>')

# 2. Get the per-account DB path:
vibe_seller_email_info(store_id='<store-id>')
# Returns: { accounts: [{ email, db_path, ... }] }

Then read the latest Noon OTP email. The body is HTML — the 6-digit code sits on its own line inside a styled <div>. Extract it with a regex on body_html (not body_text, which has escaped markup):

sqlite3 <db_path> "SELECT body_html FROM emails \
  WHERE folder='INBOX' \
    AND sender LIKE '%verify@noon.com%' \
    AND subject='Verify your email' \
  ORDER BY date DESC LIMIT 1" \
| python3 -c "
import sys, re
html = sys.stdin.read()
# The OTP is a standalone 6-digit number inside a styled div.
# Reject repeated digits (000000, 333333 etc. — those are hex colors).
for m in re.findall(r'>\s*(\d{6})\s*<', html):
    if len(set(m)) > 1:
        print(m); break
"

Then fill it and continue:

browser-use input <otp-input> "<code>"    # input targets by index
browser-use click <continue-btn>
browser-use state
browser-use click <maybe-later-btn>       # dismiss passkey prompt

Don't ask the user at all in Case A. The whole point of the binding is that the agent can finish login unattended.

Case B — Ask the user (only when Case A doesn't apply)

Trigger the OTP, then ask via AskUserQuestion (which requires 2+ options, not 1 — a single-option call fails validation):

AskUserQuestion(questions=[{
  "question": "I've triggered the Noon OTP email to <email>. Please enter the 6-digit code in the browser, then tell me when you're ready.",
  "header": "OTP",
  "options": [
    {"label": "Entered, continue", "description": "I typed the OTP"},
    {"label": "Didn't arrive",     "description": "Resend or switch account"}
  ],
  "multiSelect": false
}])

If the user can't receive the OTP, don't guess or retry — escalate and let them resolve.

After login — discover {project_id} from the URL

browser-use click <maybe-later-btn>  # dismiss passkey prompt
# Redirects to welcome.noon.partners/... with project=PRJ{project_id}

Do NOT ask the user for the project ID if the store profile (stores/<slug>/STORE.md) doesn't have it. Read it from the post- login URL — the welcome / store-home page always carries ?project=PRJ{project_id} (e.g. NNNNNN). Capture it once, then reuse for the rest of the task. SA and AE share the same numeric project ID; only URL country suffixes (/en-sa/ vs /en-ae/) differ.

Optionally persist what you learned back to the store profile:

vibe_seller_write_workspace_file(
  path="stores/<slug>/metadata.json",
  content='{"platform_countries": {"noon": ["SA", "AE"]}, "noon_project_id": "<project_id>"}'
)

2. Page Structure Cheat Sheet

Most post-login URLs require ?project=PRJ{project_id} (the login and welcome pages are the only exceptions). {project_id} is the numeric project identifier. The store identifier in path segments like STR{project_id}-N{CC} reuses the same numeric value with STR prefix and country suffix. Direct URL navigation works for most pages; sidebar only for Support/Help.

Page URL
Create listing noon-catalog.noon.partners/en/catalog/create
Edit listing noon-catalog.noon.partners/en/catalog/{sku}/d?code={code}&offerTab=noon
My Catalog noon-catalog.noon.partners/en/catalog
Catalog Imports noon-catalog.noon.partners/en/imports
Catalog Exports noon-catalog.noon.partners/en/exports
FBN My ASN & Storage fbn.noon.partners/en-{cc}/asn
FBN Create ASN fbn.noon.partners/en-{cc}/asn/createasn
FBN My Inventory fbn.noon.partners/en-{cc}/inventory
Sales reports.noon.partners/en/sales/
Transaction View noon-payments.noon.partners/en/transaction-view
Ad Manager admanager.noon.partners/en-{cc}/home?mpCode=noon
Campaign Detail admanager.noon.partners/en-{cc}/campaign/details/{id}?mpCode=noon
Create Campaign admanager.noon.partners/en-{cc}/campaign/start?mpCode=noon
Vantage vantage.noon.partners/en/

3. My Catalog (read access)

URL: https://noon-catalog.noon.partners/en/catalog?project=PRJ{project_id}

Tabs: noon (default), supermall, global.

Each row has:

  • Product title + Brand
  • PSKU (Partner SKU) + SKU (noon ID), copyable
  • Price, Sale/Promo badge
  • Estimated Fees (FBN + FBP separately)
  • Active Net Stock (FBN + FBP, links to inventory)
  • Performance (Views, Units Sold, Sales)
  • Seller Status toggle
  • Live Status + "View Issues" link

Click the product title anchor to open the edit page (see noon-listing skill).

For ad-tuning audits, this catalog read is the prerequisite step that establishes "what the SKU actually is" before reading any campaign — see noon-ads/references/ads-tuning.md.

4. Common Modals

Save Changes Warning

When leaving a page with unsaved changes:

"Do you want to save the changes you made to this page?" [Discard Changes] [Save Changes]

Always click Save Changes unless intentionally reverting.

Penalty Warning (ASN)

Before reserving FBN quota — see noon-fbn skill. Click Cancel to exit without commitment.

Tour Modal

First-time visits show tours. Dismiss with "Skip for now".

Hotjar Survey

NPS survey at bottom-right. Dismiss with aria-label="Hide survey".

Passkey Prompt (After Login)

Dismiss with "Maybe Later".

5. Button Clicking Patterns

JS Click for Off-Screen Buttons

Listing wizard "Next" buttons are often outside viewport:

browser-use eval "
var r=''; document.querySelectorAll('button').forEach(function(b,i){
  r+=i+':'+b.textContent.trim().substring(0,30)+':d='+b.disabled+' | ';
}); r
"
# Find the right index, then:
browser-use eval "document.querySelectorAll('button')[0].click()"

Iframe Sidebar Clicks

Toolbar iframe is cross-origin; use get bbox on the parent category button, then click below it at estimated offset:

browser-use click <category-btn>     # expand
browser-use get bbox <category-btn>  # returns {x, y, width, height}
# Sub-items are ~24-30px tall below, indented ~35px from left
browser-use click <x+35> <y+50>      # first sub-item

6. Tips (general)

  • Always use direct URLs for known pages (faster, more reliable).
  • Login OTP — prefer email MCP over asking the user. If the store has a bound email account matching the Noon login email, fetch the OTP yourself (Case A). Only ask when no binding applies.
  • Project ID is required in the URL: ?project=PRJ{project_id}.
  • Country switching varies by page — Sales/Transaction use bottom-left store dropdown; Ad Manager uses URL country suffix; FBN paths embed the country code directly. Check per-page.
  • Save changes before navigating — warning modal can silently lose data if you click Discard.
  • browser-use input <idx> <value> vs browser-use type <value>. input targets an element by index; type types into whatever is currently focused. They are not interchangeable — type <idx> <value> fails with a usage error.
  • AskUserQuestion needs 2+ options — a single-option call fails schema validation; always give at least a confirm and a "didn't work" option.
  • Captures → /tmp/<run-slug>/. Per-run live data goes to a temp dir, never under ~/.vibe-seller/knowledge/.

See also

  • noon-listing — create / edit SKUs (price, stock, content)
  • noon-fbn — FBN/ASN, inventory, barcode print
  • noon-exports — Sales report, Transaction View, Catalog Exports
  • noon-ads — Ad Manager (campaigns, tuning, keyword research, negatives)
Install via CLI
npx skills add https://github.com/zpoint/vibe-seller --skill noon-shared
Repository Details
star Stars 7
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator