Promotions, banners & packs
Banners per placement, promotions and daily offers with their eligible products, and V2 profitable packs — targeted server-side to the caller, open to guests without prices.
What the reseller app and web show under « Promotions » and in the carousels (cahier §4.6, §5.5, reference screen 7): banners, daily offers with a countdown, scheduled promotions and — in V2 — profitable packs. Built in SRV-19 (packs: SRV-31); consumed by MRA-06, WEB-05 and, for promotions.*, by the delegate app (visit report « promotions présentées » and first-order assistance).
- Targeting is applied on the server. A promotion or banner targets everyone, wilayas, segments, customer types, brands or volumes (PRM-02); the caller only ever receives what targets them, and the audience rules themselves are never returned. A promotion that does not target the caller is
404 NOT_FOUND. - Guests welcome, without prices. These operations are
optional: guests see the promotions and banners open to all resellers, withprice: nulland no amounts that would reveal a price (AUTH-10). - Applied by the cart. What is shown here is applied automatically by the cart engine — dates, cap and target included (PRM-04); there is no coupon to enter in V1.
- No stock numbers. A promotion's quantity cap is shown as a status (
capStatus), never as a remaining quantity (STK-07).
| Method | Path | Operation | Access | Apps | Release |
|---|---|---|---|---|---|
| GET | /banners | banners.listActive banners for a placement, targeted to the caller — guests allowed, without prices | guest or session | RA · RW | V1 |
| GET | /promotions | promotions.listPromotions and daily offers available to the caller — guests allowed, without prices | guest or session | RA · RW · DA | V1 |
| GET | /promotions/{promotionId} | promotions.getPromotion detail: conditions, dates, remaining stock cap, eligible products — guests allowed, without prices | guest or session | RA · RW · DA | V1 |
| GET | /packs | packs.listPacks available to the caller (V2) — guests allowed, without prices | guest or session | RA · RW | V2 |
| GET | /packs/{packId} | packs.getPack detail (V2) — guests allowed, without prices | guest or session | RA · RW | V2 |
Every operation can also answer 426 UPGRADE_REQUIRED, 429 RATE_LIMITED, 500 INTERNAL_ERROR and 503 MAINTENANCE; with a session also 401 UNAUTHENTICATED and 403 ACCOUNT_SUSPENDED — see the error registry.

Who sees what
| Caller | Promotions and banners returned | Amounts |
|---|---|---|
| Guest | Active items whose audience is every reseller | price: null; value hidden (null) for AMOUNT_OFF and FIXED_PRICE; percentages shown (« -30 % ») |
| Reseller | Active items whose audience matches the reseller (wilaya, segment, customer type, volume rules) | Prices for the reseller's customer type |
Delegate (promotions.* only) | With resellerId: what that reseller sees, with their prices. Without: promotions open to all resellers or targeting a wilaya of the delegate's zones, without prices | As for the reseller, or none |
Active means status ACTIVE, startsAt ≤ now < endsAt, cap not reached — the engine re-checks the dates itself (promotion lifecycle).
Schemas
interface Banner {
id: Uuid;
placement: "HOME_CAROUSEL" | "CATALOG_TOP";
eyebrow: string | null; // "OFFRE DU JOUR"
title: string;
subtitle: string | null;
imageUrl: string; // public CDN, 1200 × 600 WebP (mobile crops to 2:1)
imageAlt: string;
theme: "DARK" | "LIGHT"; // text colour over the image
cta: { label: string; target: BannerTarget } | null;
countdownEndsAt: Iso | null; // « Se termine dans 12 h 45 m 30 s »
startsAt: Iso;
endsAt: Iso | null;
sortOrder: number;
}
type BannerTarget =
| { type: "PROMOTION"; promotionId: Uuid }
| { type: "PRODUCT"; productId: Uuid }
| { type: "CATEGORY"; categoryId: Uuid }
| { type: "BRAND"; brandId: Uuid }
| { type: "PACK"; packId: Uuid } // V2
| { type: "URL"; url: string }; // https only, hoco.dz domains
interface PromotionSummary {
id: Uuid;
label: string; // "Offre flash -30 %"
description: string | null;
type: "PERCENT_OFF" | "AMOUNT_OFF" | "FIXED_PRICE" | "FREE_DELIVERY";
value: number | null; // PERCENT_OFF: basis points · AMOUNT_OFF: HT centimes off per unit ·
// FIXED_PRICE: unit price HT · FREE_DELIVERY: null · null for guests on AMOUNT_OFF / FIXED_PRICE
badge: string; // ready-to-show: "-30 %", "Prix spécial", "Livraison offerte"
isDailyOffer: boolean;
startsAt: Iso;
endsAt: Iso;
imageUrl: string | null;
scope: { kind: "ALL" | "PRODUCTS" | "CATEGORIES" | "BRANDS" | "MIXED"; summary: string; productCount: number };
conditions: { minQtyPerLine: number | null; minOrderAmountTtc: Money | null };
capStatus: "NONE" | "AVAILABLE" | "ALMOST_GONE"; // ALMOST_GONE from 80 % of the cap consumed
stackable: boolean; // combinable with other promotions (PRM-03)
}
interface PromotionDetail extends PromotionSummary {
terms: string[]; // conditions in plain language, shown as a list
eligibleProducts: { items: ProductCard[]; nextCursor: string | null };
}
interface PackSummary { // V2
id: Uuid;
name: string; // "Pack Charge & Câble"
description: string; // "Chargeur rapide HOCO + Câble USB-C"
imageUrl: string;
price: Price | null; // pack price for the caller's customer type; null for guests
itemsValueTtc: Money | null; // the items at the caller's unit prices
savingTtc: Money | null;
savingRate: BasisPoints; // « Économisez 20 % » — shown to guests too
availability: "IN_STOCK" | "OUT_OF_STOCK"; // OUT_OF_STOCK when any item is
maxPacksPerOrder: number | null;
endsAt: Iso | null;
}
interface PackDetail extends PackSummary {
items: { product: ProductCard; variantId: Uuid; sku: string; variantLabel: string; qty: number }[];
terms: string[];
}BannerPlacement (HOME_CAROUSEL, CATALOG_TOP), BannerTarget.type and capStatus are not yet in enums.json (to add).
Operations
GET /banners
banners.list · Access optional — guest or reseller · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements PRM-05, PRM-02
Purpose. The active banners of one placement for the caller, in display order — the home carousel (also embedded in catalog.home) and the strip on top of the catalog.
Request
| Query | Type | Required | Notes |
|---|---|---|---|
placement | HOME_CAROUSEL | CATALOG_TOP | yes |
GET /v1/banners?placement=CATALOG_TOPResponse — 200 OK, Cache-Control: private, max-age=60: { items: Banner[] }, at most 6, not paginated.
{
"items": [
{
"id": "01a19d02-aa40-73d1-8f56-e60bbab760fc",
"placement": "CATALOG_TOP",
"eyebrow": "OFFRE FLASH",
"title": "Jusqu'à -30 % sur une sélection HOCO",
"subtitle": "Aujourd'hui seulement",
"imageUrl": "https://cdn.hoco.dz/banners/2026/11/flash-hoco-30-strip.webp",
"imageAlt": "Chargeur, câble et écouteurs HOCO avec le badge -30 %",
"theme": "DARK",
"cta": { "label": "Voir l'offre", "target": { "type": "PROMOTION", "promotionId": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17" } },
"countdownEndsAt": "2026-11-03T22:59:59Z",
"startsAt": "2026-11-03T07:00:00Z",
"endsAt": "2026-11-03T22:59:59Z",
"sortOrder": 1
}
]
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | placement missing or unknown. |
Rules & side effects
- A banner is returned when it is active (dates, not paused, never a
DRAFT), targets the caller, and its linked promotion (if any) is active — a banner of an ended or paused promotion disappears at once (lifecycle). Targeting (SRV-42): wilayas, segments, customer types and named resellers, kinds ANDed and values ORed; a guest gets the banners marked for guests (audience.guests). - Images are public CDN files managed in BO-51 (
admin.banners.crud); the client prefetches them. A mobile app (X-App-Platformandroidorios) gets the banner's mobile image (imageMobile) when it has one. - Countdowns run on
countdownEndsAtminus the server time (catalog.home→serverTime, or theDateresponse header).countdownEndsAtis set when the banner hascountdownon (by default, a banner of a daily offer).
Used by — carousel of RA-09 and RW-02 (through catalog.home), catalog strip of RA-12 and RW-03, top of RA-24.
GET /promotions
promotions.list · Access optional — guest, reseller, or delegate (resellerId optional) · Apps RA, RW, DA · Idempotency-Key — · Release V1 · Requirements PRM-01, PRM-02
Contract 1.1.0 (SRV-41): brandId (repeatable) keeps the offers that apply to at least one product of these brands visible to the caller (an ALL scope counts).
Purpose. « Promotions » and « Offres du jour »: the promotions active for the caller, daily offers first, then by end date.
Request
| Query | Type | Required | Default | Notes |
|---|---|---|---|---|
dailyOnly | boolean | no | false | Only isDailyOffer promotions (the « Offres du jour » carousel) |
type | PromotionType, repeatable | no | — | |
resellerId | Uuid | no | — | Delegates only; scope-checked |
cursor, limit | — | no | limit 20 | Max 100 |
Response — 200 OK, Cache-Control: private, max-age=60: { items: PromotionSummary[], nextCursor }.
{
"items": [
{
"id": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17",
"label": "Offre flash -30 %",
"description": "Jusqu'à -30 % sur une sélection de produits HOCO, aujourd'hui seulement.",
"type": "PERCENT_OFF",
"value": 3000,
"badge": "-30 %",
"isDailyOffer": true,
"startsAt": "2026-11-03T07:00:00Z",
"endsAt": "2026-11-03T22:59:59Z",
"imageUrl": "https://cdn.hoco.dz/promotions/2026/11/flash-hoco-30.webp",
"scope": { "kind": "PRODUCTS", "summary": "6 produits HOCO", "productCount": 6 },
"conditions": { "minQtyPerLine": null, "minOrderAmountTtc": null },
"capStatus": "AVAILABLE",
"stackable": false
},
{
"id": "01a17249-c680-746a-84fd-f70a4efea668",
"label": "-10 % sur les câbles ACEFAST",
"description": "Tous les câbles ACEFAST à -10 %, dès 10 pièces par référence.",
"type": "PERCENT_OFF",
"value": 1000,
"badge": "-10 %",
"isDailyOffer": false,
"startsAt": "2026-10-25T23:00:00Z",
"endsAt": "2026-11-15T22:59:59Z",
"imageUrl": "https://cdn.hoco.dz/promotions/2026/10/acefast-cables.webp",
"scope": { "kind": "MIXED", "summary": "Câbles ACEFAST", "productCount": 14 },
"conditions": { "minQtyPerLine": 10, "minOrderAmountTtc": null },
"capStatus": "NONE",
"stackable": false
},
{
"id": "01a181bc-da80-7da6-9f6e-161582e00776",
"label": "Z36 à 1 880 DA",
"description": "Le chargeur voiture HOCO Z36 38W à prix spécial, dans la limite du stock promotionnel.",
"type": "FIXED_PRICE",
"value": 157983,
"badge": "Prix spécial",
"isDailyOffer": false,
"startsAt": "2026-10-28T08:00:00Z",
"endsAt": "2026-11-10T22:59:59Z",
"imageUrl": "https://cdn.hoco.dz/promotions/2026/10/z36-prix-special.webp",
"scope": { "kind": "PRODUCTS", "summary": "HOCO Z36 Chargeur voiture 38W", "productCount": 1 },
"conditions": { "minQtyPerLine": null, "minOrderAmountTtc": null },
"capStatus": "ALMOST_GONE",
"stackable": false
}
],
"nextCursor": null
}For a guest, the Z36 item comes back with "value": null (a fixed price is a price) while the two percentages stay visible.
| Field | Notes |
|---|---|
value | Interpreted with type; FIXED_PRICE and AMOUNT_OFF are HT amounts like every stored price — the product cards carry the resulting TTC price (ProductCard.promotion.price). |
scope.summary | Human summary written by the back-office (« 6 produits HOCO »); the products themselves come from promotions.get. |
capStatus | ALMOST_GONE shows « Bientôt épuisée »; a promotion whose cap is reached is ENDED and no longer listed. |
conditions.minQtyPerLine | The cart applies the promotion only to lines reaching this quantity. |
Errors
| Status | Code | When |
|---|---|---|
| 403 | OUT_OF_SCOPE | Delegate's resellerId outside scope. |
Rules & side effects — read-only; delegates use the list to record « promotions présentées » in the visit report (delegate.visits.report).
Used by — RA-24 (« Promotions »), RW-10, visit report DA-12, DA-17.
GET /promotions/{promotionId}
promotions.get · Access optional — guest, reseller, or delegate (resellerId optional) · Apps RA, RW, DA · Idempotency-Key — · Release V1 · Requirements PRM-01, PRM-04
Purpose. « Détail de l'offre »: conditions, dates, cap status and the eligible products (as product cards, with the promotional price for the caller).
Request
| Path / query | Type | Required | Notes |
|---|---|---|---|
promotionId | Uuid | yes | |
cursor, limit | — | no | Paging of eligibleProducts (limit default 20, max 100) |
resellerId | Uuid | no | Delegates only |
Response — 200 OK: a PromotionDetail.
{
"id": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17",
"label": "Offre flash -30 %",
"description": "Jusqu'à -30 % sur une sélection de produits HOCO, aujourd'hui seulement.",
"type": "PERCENT_OFF",
"value": 3000,
"badge": "-30 %",
"isDailyOffer": true,
"startsAt": "2026-11-03T07:00:00Z",
"endsAt": "2026-11-03T22:59:59Z",
"imageUrl": "https://cdn.hoco.dz/promotions/2026/11/flash-hoco-30.webp",
"scope": { "kind": "PRODUCTS", "summary": "6 produits HOCO", "productCount": 6 },
"conditions": { "minQtyPerLine": null, "minOrderAmountTtc": null },
"capStatus": "AVAILABLE",
"stackable": false,
"terms": [
"Remise de 30 % sur le prix revendeur des produits listés.",
"Valable jusqu'à 23h59 aujourd'hui, dans la limite du stock promotionnel.",
"Non cumulable avec une autre promotion."
],
"eligibleProducts": {
"items": [
{
"id": "01a15889-fa80-7ff3-b294-f863a2ee34a6",
"name": "HOCO J86A Power Bank 10000mAh",
"brand": { "id": "01a05bfb-7000-73a7-ac3c-c71515d5e3b3", "name": "HOCO" },
"category": { "id": "01a05c04-ab48-7b23-9612-4a7450cd39ef", "name": "Power banks", "parentId": null },
"imageUrl": "https://cdn.hoco.dz/products/j86a/j86a-black-1-480.webp",
"defaultVariantId": "01a15889-fe68-733d-8056-75eec8b226f5",
"price": { "ht": 302521, "ttc": 360000, "vatRate": 1900 },
"priceLocked": false,
"recommendedPrice": 450000,
"availability": "IN_STOCK",
"promotion": { "id": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17", "label": "Offre flash -30 %", "price": { "ht": 211765, "ttc": 252000, "vatRate": 1900 } },
"isNew": true,
"isFavorite": false
},
{
"id": "01a094d8-5280-7779-b2fc-bf0e216609a3",
"name": "HOCO EW41 Écouteurs TWS",
"brand": { "id": "01a05bfb-7000-73a7-ac3c-c71515d5e3b3", "name": "HOCO" },
"category": { "id": "01a05c04-a760-7afa-a91b-ad281947800e", "name": "Écouteurs", "parentId": null },
"imageUrl": "https://cdn.hoco.dz/products/ew41/ew41-white-1-480.webp",
"defaultVariantId": "01a094d8-5668-7b74-a958-fc54830e96a2",
"price": { "ht": 579832, "ttc": 690000, "vatRate": 1900 },
"priceLocked": false,
"recommendedPrice": 850000,
"availability": "IN_STOCK",
"promotion": { "id": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17", "label": "Offre flash -30 %", "price": { "ht": 405882, "ttc": 483000, "vatRate": 1900 } },
"isNew": false,
"isFavorite": false
}
],
"nextCursor": "eyJpIjoiMDFhMDk0ZDgifQ"
}
}Errors
| Status | Code | When |
|---|---|---|
| 403 | OUT_OF_SCOPE | Delegate's resellerId outside scope. |
| 404 | NOT_FOUND | Unknown, not active, or not targeting the caller (a guest asking for a targeted promotion gets 404 too). |
Rules & side effects — eligible products follow the catalog rules (visibility, availability, no quantity); out-of-stock products are listed after the others. « Ajouter » on a card calls cart.items.add; the promotion then appears on the cart line.
Used by — RA-25 (« Détail de l'offre »), RW-10, DA-17; banner and daily-offer taps.
GET /packs
packs.list · Access optional — guest or reseller · Apps RA, RW · Idempotency-Key — · Release V2 · Requirements PRM-06
Purpose. « Packs rentables » (reference screen 7): bundles of several references at a pack price, added to the cart as a unit (cart.packs.add). Specified with the V2 contract (SRV-29); baseline below.
Request — cursor, limit (default 20, max 100).
Response — 200 OK: { items: PackSummary[], nextCursor }.
{
"items": [
{
"id": "01a49fbf-a000-7f4e-99b1-a4afd2a3a603",
"name": "Pack Charge & Câble",
"description": "Chargeur rapide HOCO N32 + Câble USB-C X89",
"imageUrl": "https://cdn.hoco.dz/packs/charge-cable.webp",
"price": { "ht": 1588235, "ttc": 1890000, "vatRate": 1900 },
"itemsValueTtc": 2369000,
"savingTtc": 479000,
"savingRate": 2022,
"availability": "IN_STOCK",
"maxPacksPerOrder": 5,
"endsAt": null
},
{
"id": "01a49fbf-a3e8-7f5f-8e92-6670b3d624d4",
"name": "Pack Audio HOCO",
"description": "Écouteurs TWS HOCO + Étui de protection",
"imageUrl": "https://cdn.hoco.dz/packs/audio.webp",
"price": { "ht": 1344538, "ttc": 1600000, "vatRate": 1900 },
"itemsValueTtc": 1880000,
"savingTtc": 280000,
"savingRate": 1489,
"availability": "IN_STOCK",
"maxPacksPerOrder": 5,
"endsAt": "2027-06-30T22:59:59Z"
},
{
"id": "01a49fbf-a7d0-7e57-8d53-52398557acea",
"name": "Pack Voiture HOCO",
"description": "Support voiture HOCO + Chargeur voiture Z36",
"imageUrl": "https://cdn.hoco.dz/packs/voiture.webp",
"price": { "ht": 1252101, "ttc": 1490000, "vatRate": 1900 },
"itemsValueTtc": 1817000,
"savingTtc": 327000,
"savingRate": 1800,
"availability": "OUT_OF_STOCK",
"maxPacksPerOrder": 3,
"endsAt": null
}
],
"nextCursor": null
}Guests receive the same packs (those open to all resellers) with price, itemsValueTtc and savingTtc set to null; savingRate stays (« Économisez 20 % »).
Errors
| Status | Code | When |
|---|---|---|
| 403 | FEATURE_DISABLED | Packs are switched off (packs.enabled = false, details.feature = "packs"). |
Rules & side effects — assumption (V2): a pack has one price per customer type, like a variant; itemsValueTtc uses the caller's unit prices, so the saving shown is the caller's real saving. Targeting and visibility as for promotions; a pack is OUT_OF_STOCK as soon as one item is.
GET /packs/{packId}
packs.get · Access optional — guest or reseller · Apps RA, RW · Idempotency-Key — · Release V2 · Requirements PRM-06
Purpose. A pack with its items and quantities.
Request — path packId.
Response — 200 OK: a PackDetail.
{
"id": "01a49fbf-a000-7f4e-99b1-a4afd2a3a603",
"name": "Pack Charge & Câble",
"description": "Chargeur rapide HOCO N32 + Câble USB-C X89",
"imageUrl": "https://cdn.hoco.dz/packs/charge-cable.webp",
"price": { "ht": 1588235, "ttc": 1890000, "vatRate": 1900 },
"itemsValueTtc": 2369000,
"savingTtc": 479000,
"savingRate": 2022,
"availability": "IN_STOCK",
"maxPacksPerOrder": 5,
"endsAt": null,
"items": [
{
"product": {
"id": "01a08ac2-8900-7f6d-937c-c81b37e89a5b",
"name": "HOCO N32 Chargeur rapide 30W (USB-C + USB-A)",
"brand": { "id": "01a05bfb-7000-73a7-ac3c-c71515d5e3b3", "name": "HOCO" },
"category": { "id": "01a05c04-9ba8-779f-8030-3262210b2afb", "name": "Chargeurs muraux", "parentId": "01a05c04-97c0-76c5-9c89-9cf30f53d5b0" },
"imageUrl": "https://cdn.hoco.dz/products/n32/n32-black-1-480.webp",
"defaultVariantId": "01a08ac2-8ce8-7069-8328-28fd7158a572",
"price": { "ht": 209244, "ttc": 249000, "vatRate": 1900 },
"priceLocked": false,
"recommendedPrice": 320000,
"availability": "IN_STOCK",
"isNew": false,
"isFavorite": true
},
"variantId": "01a08ac2-8ce8-7069-8328-28fd7158a572",
"sku": "N32_BLACK_EU",
"variantLabel": "Noir",
"qty": 6
},
{
"product": {
"id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb",
"name": "HOCO X89 Câble USB-C",
"brand": { "id": "01a05bfb-7000-73a7-ac3c-c71515d5e3b3", "name": "HOCO" },
"category": { "id": "01a05c04-a378-7a15-a596-11824871a56b", "name": "Câbles", "parentId": null },
"imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-480.webp",
"defaultVariantId": "01a08ac7-20c8-70bf-b753-d181dfe71ff6",
"price": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"priceLocked": false,
"recommendedPrice": 160000,
"availability": "IN_STOCK",
"isNew": false,
"isFavorite": true
},
"variantId": "01a08ac7-20c8-70bf-b753-d181dfe71ff6",
"sku": "X89_BLACK_1M",
"variantLabel": "Noir · 1 m",
"qty": 7
}
],
"terms": [
"6 chargeurs HOCO N32 noirs et 7 câbles HOCO X89 noirs (1 m) au prix du pack.",
"Non cumulable avec les promotions en cours sur ces produits.",
"5 packs maximum par commande."
]
}Errors
| Status | Code | When |
|---|---|---|
| 403 | FEATURE_DISABLED | Packs switched off. |
| 404 | NOT_FOUND | Unknown, inactive, or not targeting the caller. |
Rules & side effects — items are fixed (no substitution in V1 of packs); itemsValueTtc = 6 × 2 490,00 + 7 × 1 250,00 = 23 690,00 DA for a Demi-gros reseller, saving 4 790,00 DA.
Reseller finance
The reseller's balance, credit and due dates, invoices with their allocations and PDF, payments and declarations, and the declaration of a transfer, CCP or BaridiMob payment with proof.
Reseller profile, addresses & documents
The reseller's store profile with status, customer type, assigned delegate and support region, the fields a reseller may edit, store documents (RC, NIF, NIS, ID) and delivery addresses.