How to compose a listing's detail page in the Super App. A listing is made
of ordered panels (sections), and each panel holds ordered cards
(offers). The on-screen look is the combination of a panel'spanelType+layoutand a card'sdesign+linkType.
See also: Getting Started for the onboarding
checklist and Integration & API Reference
for the full lifecycle walkthrough.

panelType — what the section accepts| Value | Purpose |
|---|---|
OFFERS | Purchasable in-app offers (bought via POST /marketplace/purchase) |
PROMOTIONS | External promos / deeplinks / webviews — no purchase flow |
OCCASIONS | Seasonal / themed campaign (Ramadan, National Day…) |
CARD_GRID | General catch-all; what the app synthesizes from a legacy cards[]-only payload |
INFO | Text-only section (bodyEn/bodyAr), no cards |
layout — how the row renders
| Value | Look | Recommended for |
|---|---|---|
HERO_CAROUSEL | Swipeable wide cards | OCCASIONS |
GRID | 2-column tile grid | OFFERS |
LIST | Stacked vertical rows (default) | PROMOTIONS / CARD_GRID |
BANNER | Single full-width card, no scroll | one featured hero item |
| Field | Type | Notes |
|---|---|---|
titleEn/Ar | string ≤200 | Section title, shown when showTitle: true |
showTitle | boolean | Default false; mainly used for OCCASIONS |
subtitleEn/Ar | string ≤200 | One-line subtitle |
themeColor | #RRGGBB | Hex accent for the section chrome |
bannerImageUrl | URL/gs:// | Panel hero banner (set via the banner upload endpoint) |
expiresAt | ISO 8601 | Campaign expiry; null = never expires |
bodyEn/Ar | string ≤2000 | Plain body copy — INFO panels only |
featuredStoreId, featuredStoreLabelEn/Ar | — | Featured store reference |
| Field | Type | Notes |
|---|---|---|
design | banner | tile | compact | Card visual treatment (required) |
linkType | deeplink | webview | offer | Launch behavior (required) |
url | string ≤2048 | webview needs http(s); deeplink accepts any scheme; offer may be empty |
titleEn/Ar | string ≤200 | Card title (required) |
descriptionEn/Ar | string ≤2000 | Body copy |
imageUrl | URL/gs:// | Card thumbnail (image upload endpoint) |
price | decimal string | Unit price; omit to keep the card non-purchasable |
originalPrice | decimal string | Pre-discount price, shown struck through |
currency | ISO 4217 | Default SAR |
inStock | boolean | Default true; false soft-disables the card |
perUserLimit | int 1–999 | Max purchases per customer |
badgeLabel | string ≤60 | Attention badge (e.g. "Best seller") |
badgeColor | #RRGGBB | Styles badgeLabel |
bannerImageUrl | URL/gs:// | Hero image for the card's detail screen |
highlightText | string ≤200 | Short highlight copy |
termsEn/Ar | string ≤2000 | Purchase terms shown near checkout |
offerExpiresAt | ISO 8601 | After this moment the card is not purchasable |
fulfillmentMode | INSTANT | PARTNER_CONFIRMATION | Default INSTANT |
| Desired view | panelType | layout | Card design | Card linkType |
|---|---|---|---|---|
| Seasonal hero carousel | OCCASIONS | HERO_CAROUSEL | banner | offer/deeplink |
| Purchasable 2-column grid | OFFERS | GRID | tile | offer |
| Stacked promos / deeplinks | PROMOTIONS | LIST | tile/compact | deeplink/webview |
| Single full-width feature | PROMOTIONS | BANNER | banner | deeplink |
| Text-only section (how it works) | INFO | LIST | — | — |
| Legacy / unclassified dump | CARD_GRID | LIST | tile | any |
showTitle: true, subtitle, themeColor, and bannerImageUrl onOCCASIONS), and bodyEn/Ar on INFO sections.| Status | Meaning | Editable? |
|---|---|---|
DRAFT | Not yet submitted | ✔ |
CHANGES_REQUESTED | Admin asked for changes | ✔ |
REJECTED | Admin rejected | ✔ |
SUBMITTED | Queued for review | — |
IN_REVIEW | Under admin review | — |
APPROVED / PUBLISHED | Live / scheduled to go live | — |
| Method | Endpoint | Purpose |
|---|---|---|
| GET | .../listing/panels | List sections with cards |
| POST | .../listing/panels | Create a section |
| PATCH | .../listing/panels/reorder | Reorder sections |
| PATCH | .../listing/panels/:panelId | Update a section |
| DELETE | .../listing/panels/:panelId | Delete a section (must be empty) |
| POST | .../listing/panels/:panelId/banner | Upload the panel banner |
| Method | Endpoint | Purpose |
|---|---|---|
| GET | .../listing/offers | List cards |
| POST | .../listing/offers | Create a card |
| PATCH | .../listing/offers/reorder | Reorder cards (per panel) |
| PATCH | .../listing/offers/:offerId | Update a card |
| DELETE | .../listing/offers/:offerId | Delete / soft-disable a card |
| POST | .../listing/offers/:offerId/image | Upload the card thumbnail |
| POST | .../listing/offers/:offerId/banner | Upload the card banner |
HERO_CAROUSEL campaign panel viaPOST .../listing/panels:{
"panelType": "OCCASIONS",
"layout": "HERO_CAROUSEL",
"titleEn": "Ramadan Specials",
"titleAr": "عروض رمضان",
"showTitle": true,
"subtitleEn": "Iftar & suhoor deals, all month",
"themeColor": "#16A34A",
"expiresAt": "2026-09-18T21:00:00Z"
}POST .../listing/offers:{
"panelId": "12",
"titleEn": "Silver top-up",
"titleAr": "تعبئة فضية",
"design": "tile",
"linkType": "offer",
"url": "",
"price": "49.99",
"originalPrice": "79.99",
"currency": "SAR",
"badgeLabel": "Best seller",
"badgeColor": "#E8553C",
"fulfillmentMode": "INSTANT"
}panelId to place a card in the implicit ALL_CARDS panel; set it toprice is a decimal string (≤ 7 integer + 2 fraction digits). Omit it to keeppanels[] then each panel's items[] by sortOrderALL_CARDS panel once the firstlayout → LIST; unknown panelType →CARD_GRID.cards[]: the customer detail API still returns a flattenedcards[] (union of all panels[].items[]) for backward compatibility — buildpanels[].inStock=false) instead of deleted to preserve orderpanelType + layout, and cards with the rightdesign + linkType, then reorder both to control the exact on-screen view.DRAFT / CHANGES_REQUESTED / REJECTED;POST .../listing/submit